aboutsummaryrefslogtreecommitdiffstats
path: root/src/gdisp/mcufont/mf_justify.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gdisp/mcufont/mf_justify.c')
-rw-r--r--src/gdisp/mcufont/mf_justify.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/gdisp/mcufont/mf_justify.c b/src/gdisp/mcufont/mf_justify.c
new file mode 100644
index 00000000..1938bb6a
--- /dev/null
+++ b/src/gdisp/mcufont/mf_justify.c
@@ -0,0 +1,321 @@
+#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 (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
+