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