333 lines
7.8 KiB
C
333 lines
7.8 KiB
C
#include "mf_justify.h"
|
|
#include "mf_kerning.h"
|
|
|
|
#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 != 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)
|
|
{
|
|
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;
|
|
uint16_t 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
|
|
|