ugfx/src/gdisp/mcufont/mf_justify.c

346 lines
8.2 KiB
C

/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.org/license.html
*/
#include "mf_justify.h"
#include "mf_kerning.h"
#ifndef MF_NO_COMPILE
#if MF_USE_TABS
/* Round the X coordinate up to the nearest tab stop. */
static int16_t mf_round_to_tab(const struct mf_font_s *font,
int16_t x0, int16_t x)
{
int16_t tabw, dx;
tabw = mf_character_width(font, 'm') * MF_TABSIZE;
/* Always atleast 1 space */
x += mf_character_width(font, ' ');
/* Round to next tab stop */
dx = x - x0 + font->baseline_x;
x += tabw - (dx % tabw);
return x;
}
/* Round the X coordinate down to the nearest tab stop. */
static int16_t mf_round_to_prev_tab(const struct mf_font_s *font,
int16_t x0, int16_t x)
{
int16_t tabw, dx;
tabw = mf_character_width(font, 'm') * MF_TABSIZE;
/* Always atleast 1 space */
x -= mf_character_width(font, ' ');
/* Round to previous tab stop */
dx = x0 - x + font->baseline_x;
x -= tabw - (dx % tabw);
return x;
}
#endif
int16_t mf_get_string_width(const struct mf_font_s *font, mf_str text,
uint16_t count, bool kern)
{
int16_t result = 0;
uint16_t c1 = 0, c2;
if (!count)
count = 0xFFFF;
while (count-- && *text)
{
c2 = mf_getchar(&text);
if (c2 == '\t')
{
#if MF_USE_TABS
result = mf_round_to_tab(font, 0, result);
c1 = ' ';
continue;
#else
c2 = ' ';
#endif
}
if (kern && c1 != 0)
result += mf_compute_kerning(font, c1, c2);
result += mf_character_width(font, c2);
c1 = c2;
}
return result;
}
/* Return the length of the string without trailing spaces. */
static uint16_t strip_spaces(mf_str text, uint16_t count, mf_char *last_char)
{
uint16_t i = 0, result = 0;
mf_char tmp = 0;
if (!count)
count = 0xFFFF;
while (count-- && *text)
{
i++;
tmp = mf_getchar(&text);
if (tmp != ' ' && tmp != (mf_char)0xA0 && tmp != '\n' &&
tmp != '\r' && tmp != '\t')
{
result = i;
}
}
if (last_char)
{
if (!*text)
*last_char = 0;
else
*last_char = tmp;
}
return result;
}
/* Render left-aligned string, left edge at x0. */
static void render_left(const struct mf_font_s *font,
int16_t x0, int16_t y0,
mf_str text, uint16_t count,
mf_character_callback_t callback,
void *state)
{
int16_t x;
mf_char c1 = 0, c2;
x = x0 - font->baseline_x;
while (count--)
{
c2 = mf_getchar(&text);
if (c2 == '\t')
{
#if MF_USE_TABS
x = mf_round_to_tab(font, x0, x);
c1 = ' ';
continue;
#else
c2 = ' ';
#endif
}
if (c1 != 0)
x += mf_compute_kerning(font, c1, c2);
x += callback(x, y0, c2, state);
c1 = c2;
}
}
#if !MF_USE_ALIGN
void mf_render_aligned(const struct mf_font_s *font,
int16_t x0, int16_t y0,
enum mf_align_t align,
mf_str text, uint16_t count,
mf_character_callback_t callback,
void *state)
{
int16_t string_width;
count = strip_spaces(text, count, 0);
render_left(font, x0, y0, text, count, callback, state);
}
#else
/* Render right-aligned string, right edge at x0. */
static void render_right(const struct mf_font_s *font,
int16_t x0, int16_t y0,
mf_str text, uint16_t count,
mf_character_callback_t callback,
void *state)
{
int16_t x;
uint16_t i;
mf_char c1, c2 = 0;
mf_str tmp;
/* Go to the end of the line. */
for (i = 0; i < count; i++)
mf_getchar(&text);
x = x0 - font->baseline_x;
for (i = 0; i < count; i++)
{
mf_rewind(&text);
tmp = text;
c1 = mf_getchar(&tmp);
/* Perform tab alignment */
if (c1 == '\t')
{
#if MF_USE_TABS
x = mf_round_to_prev_tab(font, x0, x);
c2 = ' ';
continue;
#else
c1 = ' ';
#endif
}
/* Apply the nominal character width */
x -= mf_character_width(font, c1);
/* Apply kerning */
if (c2 != 0)
x -= mf_compute_kerning(font, c1, c2);
callback(x, y0, c1, state);
c2 = c1;
}
}
void mf_render_aligned(const struct mf_font_s *font,
int16_t x0, int16_t y0,
enum mf_align_t align,
mf_str text, uint16_t count,
mf_character_callback_t callback,
void *state)
{
int16_t string_width;
count = strip_spaces(text, count, 0);
if (align == MF_ALIGN_LEFT)
{
render_left(font, x0, y0, text, count, callback, state);
}
if (align == MF_ALIGN_CENTER)
{
string_width = mf_get_string_width(font, text, count, false);
x0 -= string_width / 2;
render_left(font, x0, y0, text, count, callback, state);
}
else if (align == MF_ALIGN_RIGHT)
{
render_right(font, x0, y0, text, count, callback, state);
}
}
#endif
#if !MF_USE_JUSTIFY
void mf_render_justified(const struct mf_font_s *font,
int16_t x0, int16_t y0, int16_t width,
mf_str text, uint16_t count,
mf_character_callback_t callback,
void *state)
{
(void) width;
mf_render_aligned(font, x0, y0, MF_ALIGN_LEFT, text, count, callback, state);
}
#else
/* Returns true if the character is a justification point, i.e. expands
* when the text is being justified. */
static bool is_justify_space(uint16_t c)
{
return c == ' ' || c == 0xA0;
}
/* Count the number of space characters in string */
static uint16_t count_spaces(mf_str text, uint16_t count)
{
uint16_t spaces = 0;
while (count-- && *text)
{
if (is_justify_space(mf_getchar(&text)))
spaces++;
}
return spaces;
}
void mf_render_justified(const struct mf_font_s *font,
int16_t x0, int16_t y0, int16_t width,
mf_str text, uint16_t count,
mf_character_callback_t callback,
void *state)
{
int16_t string_width, adjustment;
uint16_t num_spaces;
mf_char last_char;
count = strip_spaces(text, count, &last_char);
if (last_char == '\n' || last_char == 0)
{
/* Line ends in linefeed, do not justify. */
render_left(font, x0, y0, text, count, callback, state);
return;
}
string_width = mf_get_string_width(font, text, count, false);
adjustment = width - string_width;
num_spaces = count_spaces(text, count);
{
int16_t x, tmp;
mf_char c1 = 0, c2;
x = x0 - font->baseline_x;
while (count--)
{
c2 = mf_getchar(&text);
if (c2 == '\t')
{
#if MF_USE_TABS
tmp = x;
x = mf_round_to_tab(font, x0, x);
adjustment -= x - tmp - mf_character_width(font, '\t');
c1 = c2;
continue;
#else
c2 = ' ';
#endif
}
if (is_justify_space(c2))
{
tmp = (adjustment + num_spaces / 2) / num_spaces;
adjustment -= tmp;
num_spaces--;
x += tmp;
}
if (c1 != 0)
{
tmp = mf_compute_kerning(font, c1, c2);
x += tmp;
adjustment -= tmp;
}
x += callback(x, y0, c2, state);
c1 = c2;
}
}
}
#endif
#endif //MF_NO_COMPILE