1 #include "mf_justify.h"
2 #include "mf_kerning.h"
3 
4 #if MF_USE_TABS
5 /* Round the X coordinate up to the nearest tab stop. */
mf_round_to_tab(const struct mf_font_s * font,int16_t x0,int16_t x)6 static int16_t mf_round_to_tab(const struct mf_font_s *font,
7                                int16_t x0, int16_t x)
8 {
9     int16_t tabw, dx;
10 
11     tabw = mf_character_width(font, 'm') * MF_TABSIZE;
12 
13     /* Always atleast 1 space */
14     x += mf_character_width(font, ' ');
15 
16     /* Round to next tab stop */
17     dx = x - x0 + font->baseline_x;
18     x += tabw - (dx % tabw);
19 
20     return x;
21 }
22 
23 /* Round the X coordinate down to the nearest tab stop. */
mf_round_to_prev_tab(const struct mf_font_s * font,int16_t x0,int16_t x)24 static int16_t mf_round_to_prev_tab(const struct mf_font_s *font,
25                                     int16_t x0, int16_t x)
26 {
27     int16_t tabw, dx;
28 
29     tabw = mf_character_width(font, 'm') * MF_TABSIZE;
30 
31     /* Always atleast 1 space */
32     x -= mf_character_width(font, ' ');
33 
34     /* Round to previous tab stop */
35     dx = x0 - x + font->baseline_x;
36     x -= tabw - (dx % tabw);
37 
38     return x;
39 }
40 #endif
41 
mf_get_string_width(const struct mf_font_s * font,mf_str text,uint16_t count,bool kern)42 int16_t mf_get_string_width(const struct mf_font_s *font, mf_str text,
43                             uint16_t count, bool kern)
44 {
45     int16_t result = 0;
46     uint16_t c1 = 0, c2;
47 
48     if (!count)
49         count = 0xFFFF;
50 
51     while (count-- && *text)
52     {
53         c2 = mf_getchar(&text);
54 
55         if (c2 == '\t')
56         {
57 #if MF_USE_TABS
58             result = mf_round_to_tab(font, 0, result);
59             c1 = ' ';
60             continue;
61 #else
62             c2 = ' ';
63 #endif
64         }
65 
66         if (kern && c1 != 0)
67             result += mf_compute_kerning(font, c1, c2);
68 
69         result += mf_character_width(font, c2);
70         c1 = c2;
71     }
72 
73     return result;
74 }
75 
76 /* Return the length of the string without trailing spaces. */
strip_spaces(mf_str text,uint16_t count,mf_char * last_char)77 static uint16_t strip_spaces(mf_str text, uint16_t count, mf_char *last_char)
78 {
79     uint16_t i = 0, result = 0;
80     mf_char tmp = 0;
81 
82     if (!count)
83         count = 0xFFFF;
84 
85     while (count-- && *text)
86     {
87         i++;
88         tmp = mf_getchar(&text);
89         if (tmp != ' ' && tmp != 0xA0 && tmp != '\n' &&
90             tmp != '\r' && tmp != '\t')
91         {
92             result = i;
93         }
94     }
95 
96     if (last_char)
97     {
98         if (!*text)
99             *last_char = 0;
100         else
101             *last_char = tmp;
102     }
103 
104     return result;
105 }
106 
107 /* Render left-aligned string, left edge at x0. */
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)108 static void render_left(const struct mf_font_s *font,
109                         int16_t x0, int16_t y0,
110                         mf_str text, uint16_t count,
111                         mf_character_callback_t callback,
112                         void *state)
113 {
114     int16_t x;
115     mf_char c1 = 0, c2;
116 
117     x = x0 - font->baseline_x;
118     while (count--)
119     {
120         c2 = mf_getchar(&text);
121 
122         if (c2 == '\t')
123         {
124 #if MF_USE_TABS
125             x = mf_round_to_tab(font, x0, x);
126             c1 = ' ';
127             continue;
128 #else
129             c2 = ' ';
130 #endif
131         }
132 
133         if (c1 != 0)
134             x += mf_compute_kerning(font, c1, c2);
135 
136         x += callback(x, y0, c2, state);
137         c1 = c2;
138     }
139 }
140 
141 #if !MF_USE_ALIGN
142 
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)143 void mf_render_aligned(const struct mf_font_s *font,
144                        int16_t x0, int16_t y0,
145                        enum mf_align_t align,
146                        mf_str text, uint16_t count,
147                        mf_character_callback_t callback,
148                        void *state)
149 {
150     int16_t string_width;
151     count = strip_spaces(text, count, 0);
152     render_left(font, x0, y0, text, count, callback, state);
153 }
154 
155 #else
156 
157 /* Render right-aligned string, right edge at x0. */
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)158 static void render_right(const struct mf_font_s *font,
159                          int16_t x0, int16_t y0,
160                          mf_str text, uint16_t count,
161                          mf_character_callback_t callback,
162                          void *state)
163 {
164     int16_t x;
165     uint16_t i;
166     mf_char c1, c2 = 0;
167     mf_str tmp;
168 
169     /* Go to the end of the line. */
170     for (i = 0; i < count; i++)
171         mf_getchar(&text);
172 
173     x = x0 - font->baseline_x;
174     for (i = 0; i < count; i++)
175     {
176         mf_rewind(&text);
177         tmp = text;
178         c1 = mf_getchar(&tmp);
179 
180         /* Perform tab alignment */
181         if (c1 == '\t')
182         {
183 #if MF_USE_TABS
184             x = mf_round_to_prev_tab(font, x0, x);
185             c2 = ' ';
186             continue;
187 #else
188             c1 = ' ';
189 #endif
190         }
191 
192         /* Apply the nominal character width */
193         x -= mf_character_width(font, c1);
194 
195         /* Apply kerning */
196         if (c2 != 0)
197             x -= mf_compute_kerning(font, c1, c2);
198 
199         callback(x, y0, c1, state);
200         c2 = c1;
201     }
202 }
203 
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)204 void mf_render_aligned(const struct mf_font_s *font,
205                        int16_t x0, int16_t y0,
206                        enum mf_align_t align,
207                        mf_str text, uint16_t count,
208                        mf_character_callback_t callback,
209                        void *state)
210 {
211     int16_t string_width;
212     count = strip_spaces(text, count, 0);
213 
214     if (align == MF_ALIGN_LEFT)
215     {
216         render_left(font, x0, y0, text, count, callback, state);
217     }
218     if (align == MF_ALIGN_CENTER)
219     {
220         string_width = mf_get_string_width(font, text, count, false);
221         x0 -= string_width / 2;
222         render_left(font, x0, y0, text, count, callback, state);
223     }
224     else if (align == MF_ALIGN_RIGHT)
225     {
226         render_right(font, x0, y0, text, count, callback, state);
227     }
228 }
229 
230 #endif
231 
232 
233 #if !MF_USE_JUSTIFY
234 
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)235 void mf_render_justified(const struct mf_font_s *font,
236                          int16_t x0, int16_t y0, int16_t width,
237                          mf_str text, uint16_t count,
238                          mf_character_callback_t callback,
239                          void *state)
240 {
241     mf_render_aligned(font, x0, y0, MF_ALIGN_LEFT, text, count, callback, state);
242 }
243 
244 #else
245 
246 /* Returns true if the character is a justification point, i.e. expands
247  * when the text is being justified. */
is_justify_space(uint16_t c)248 static bool is_justify_space(uint16_t c)
249 {
250     return c == ' ' || c == 0xA0;
251 }
252 
253 /* Count the number of space characters in string */
count_spaces(mf_str text,uint16_t count)254 static uint16_t count_spaces(mf_str text, uint16_t count)
255 {
256     uint16_t spaces = 0;
257     while (count-- && *text)
258     {
259         if (is_justify_space(mf_getchar(&text)))
260             spaces++;
261     }
262     return spaces;
263 }
264 
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)265 void mf_render_justified(const struct mf_font_s *font,
266                          int16_t x0, int16_t y0, int16_t width,
267                          mf_str text, uint16_t count,
268                          mf_character_callback_t callback,
269                          void *state)
270 {
271     int16_t string_width, adjustment;
272     uint16_t num_spaces;
273     mf_char last_char;
274 
275     count = strip_spaces(text, count, &last_char);
276 
277     if (last_char == '\n' || last_char == 0)
278     {
279         /* Line ends in linefeed, do not justify. */
280         render_left(font, x0, y0, text, count, callback, state);
281         return;
282     }
283 
284     string_width = mf_get_string_width(font, text, count, false);
285     adjustment = width - string_width;
286     num_spaces = count_spaces(text, count);
287 
288     {
289         int16_t x, tmp;
290         uint16_t c1 = 0, c2;
291 
292         x = x0 - font->baseline_x;
293         while (count--)
294         {
295             c2 = mf_getchar(&text);
296 
297             if (c2 == '\t')
298             {
299 #if MF_USE_TABS
300                 tmp = x;
301                 x = mf_round_to_tab(font, x0, x);
302                 adjustment -= x - tmp - mf_character_width(font, '\t');
303                 c1 = c2;
304                 continue;
305 #else
306                 c2 = ' ';
307 #endif
308             }
309 
310             if (is_justify_space(c2))
311             {
312                 tmp = (adjustment + num_spaces / 2) / num_spaces;
313                 adjustment -= tmp;
314                 num_spaces--;
315                 x += tmp;
316             }
317 
318             if (c1 != 0)
319             {
320                 tmp = mf_compute_kerning(font, c1, c2);
321                 x += tmp;
322                 adjustment -= tmp;
323             }
324 
325             x += callback(x, y0, c2, state);
326             c1 = c2;
327         }
328     }
329 }
330 
331 #endif
332 
333