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