Embedded SDK
Embedded SDK
ux_layout_paging_compute.c
Go to the documentation of this file.
1 #include "os_helpers.h"
2 #include "os_pic.h"
3 #include "ux.h"
4 #include "ux_layouts.h"
5 #include "bagl.h"
6 #include "string.h"
7 #include "os.h"
8 
9 #ifdef HAVE_UX_FLOW
10 #ifdef HAVE_BAGL
11 
12 #if !defined(HAVE_SE_SCREEN)
13 
14 // We implement a light mechanism in order to be able to retrieve the width of
15 // nano S characters, in the two possible fonts:
16 // - BAGL_FONT_OPEN_SANS_EXTRABOLD_11px,
17 // - BAGL_FONT_OPEN_SANS_REGULAR_11px.
18 #define NANOS_FIRST_CHAR 0x20
19 #define NANOS_LAST_CHAR 0x7F
20 
21 // OPEN_SANS_REGULAR_11PX << 4 | OPEN_SANS_EXTRABOLD_11PX
22 // Check with files (in the bagl_font_character_t array):
23 // - common/bagl/src/bagl_font_open_sans_extrabold_11px.inc
24 // - common/bagl/src/bagl_font_open_sans_regular_11px.inc
25 const char nanos_characters_width[96] = {
26  3 << 4 | 3, /* code 0020 */
27  3 << 4 | 3, /* code 0021 */
28  4 << 4 | 6, /* code 0022 */
29  7 << 4 | 7, /* code 0023 */
30  6 << 4 | 6, /* code 0024 */
31  9 << 4 | 10, /* code 0025 */
32  8 << 4 | 9, /* code 0026 */
33  2 << 4 | 3, /* code 0027 */
34  3 << 4 | 4, /* code 0028 */
35  3 << 4 | 4, /* code 0029 */
36  6 << 4 | 6, /* code 002A */
37  6 << 4 | 6, /* code 002B */
38  3 << 4 | 3, /* code 002C */
39  4 << 4 | 4, /* code 002D */
40  3 << 4 | 3, /* code 002E */
41  4 << 4 | 5, /* code 002F */
42  6 << 4 | 8, /* code 0030 */
43  6 << 4 | 6, /* code 0031 */
44  6 << 4 | 7, /* code 0032 */
45  6 << 4 | 7, /* code 0033 */
46  8 << 4 | 8, /* code 0034 */
47  6 << 4 | 6, /* code 0035 */
48  6 << 4 | 8, /* code 0036 */
49  6 << 4 | 7, /* code 0037 */
50  6 << 4 | 8, /* code 0038 */
51  6 << 4 | 8, /* code 0039 */
52  3 << 4 | 3, /* code 003A */
53  3 << 4 | 3, /* code 003B */
54  6 << 4 | 5, /* code 003C */
55  6 << 4 | 6, /* code 003D */
56  6 << 4 | 5, /* code 003E */
57  5 << 4 | 6, /* code 003F */
58  10 << 4 | 10, /* code 0040 */
59  7 << 4 | 8, /* code 0041 */
60  7 << 4 | 7, /* code 0042 */
61  7 << 4 | 7, /* code 0043 */
62  8 << 4 | 8, /* code 0044 */
63  6 << 4 | 6, /* code 0045 */
64  6 << 4 | 6, /* code 0046 */
65  8 << 4 | 8, /* code 0047 */
66  8 << 4 | 8, /* code 0048 */
67  3 << 4 | 4, /* code 0049 */
68  4 << 4 | 5, /* code 004A */
69  7 << 4 | 8, /* code 004B */
70  6 << 4 | 6, /* code 004C */
71  10 << 4 | 11, /* code 004D */
72  8 << 4 | 9, /* code 004E */
73  9 << 4 | 9, /* code 004F */
74  7 << 4 | 7, /* code 0050 */
75  9 << 4 | 9, /* code 0051 */
76  7 << 4 | 8, /* code 0052 */
77  6 << 4 | 6, /* code 0053 */
78  7 << 4 | 6, /* code 0054 */
79  8 << 4 | 8, /* code 0055 */
80  7 << 4 | 6, /* code 0056 */
81  10 << 4 | 11, /* code 0057 */
82  6 << 4 | 8, /* code 0058 */
83  6 << 4 | 7, /* code 0059 */
84  6 << 4 | 7, /* code 005A */
85  4 << 4 | 5, /* code 005B */
86  4 << 4 | 5, /* code 005C */
87  4 << 4 | 5, /* code 005D */
88  6 << 4 | 7, /* code 005E */
89  5 << 4 | 6, /* code 005F */
90  6 << 4 | 7, /* code 0060 */
91  6 << 4 | 7, /* code 0061 */
92  7 << 4 | 7, /* code 0062 */
93  5 << 4 | 6, /* code 0063 */
94  7 << 4 | 7, /* code 0064 */
95  6 << 4 | 7, /* code 0065 */
96  5 << 4 | 6, /* code 0066 */
97  6 << 4 | 7, /* code 0067 */
98  7 << 4 | 7, /* code 0068 */
99  3 << 4 | 4, /* code 0069 */
100  4 << 4 | 5, /* code 006A */
101  6 << 4 | 7, /* code 006B */
102  3 << 4 | 4, /* code 006C */
103  10 << 4 | 10, /* code 006D */
104  7 << 4 | 7, /* code 006E */
105  7 << 4 | 7, /* code 006F */
106  7 << 4 | 7, /* code 0070 */
107  7 << 4 | 7, /* code 0071 */
108  4 << 4 | 5, /* code 0072 */
109  5 << 4 | 6, /* code 0073 */
110  4 << 4 | 5, /* code 0074 */
111  7 << 4 | 7, /* code 0075 */
112  6 << 4 | 7, /* code 0076 */
113  9 << 4 | 10, /* code 0077 */
114  6 << 4 | 7, /* code 0078 */
115  6 << 4 | 7, /* code 0079 */
116  5 << 4 | 6, /* code 007A */
117  4 << 4 | 5, /* code 007B */
118  6 << 4 | 6, /* code 007C */
119  4 << 4 | 5, /* code 007D */
120  6 << 4 | 6, /* code 007E */
121  7 << 4 | 6, /* code 007F */
122 };
123 
124 #if defined(HAVE_UNICODE_SUPPORT)
125 // That first array will helps find the index of the unicode char:
126 const unsigned int nanos_unicode_index[] = {
127  0x0000A1, 0x0000BF, 0x0000C9, 0x0000E0, 0x0000E1, 0x0000E2, 0x0000E7,
128  0x0000E8, 0x0000E9, 0x0000EA, 0x0000EB, 0x0000ED, 0x0000EE, 0x0000EF,
129  0x0000F1, 0x0000F3, 0x0000F4, 0x0000F9, 0x0000FA, 0x0000FB
130  // No need to enter FFFD, because it will be the one we'll use by default
131  // 0x00FFFD
132 };
133 
134 // OPEN_SANS_REGULAR_11PX_UNICODE << 4 | OPEN_SANS_EXTRABOLD_11PX_UNICODE
135 // Check with files (in the bagl_font_character_t array):
136 // - common/bagl/src/bagl_font_open_sans_extrabold_11px_unicode.inc
137 // - common/bagl/src/bagl_font_open_sans_regular_11px_unicode.inc
138 const unsigned char nanos_unicode_width[] = {
139  3 << 4 | 3, // unicode 0x0000A1
140  5 << 4 | 6, // unicode 0x0000BF
141  6 << 4 | 6, // unicode 0x0000C9
142  6 << 4 | 7, // unicode 0x0000E0
143  6 << 4 | 7, // unicode 0x0000E1
144  6 << 4 | 7, // unicode 0x0000E2
145  5 << 4 | 6, // unicode 0x0000E7
146  6 << 4 | 7, // unicode 0x0000E8
147  6 << 4 | 7, // unicode 0x0000E9
148  6 << 4 | 7, // unicode 0x0000EA
149  6 << 4 | 7, // unicode 0x0000EB
150  4 << 4 | 5, // unicode 0x0000ED
151  5 << 4 | 6, // unicode 0x0000EE
152  4 << 4 | 6, // unicode 0x0000EF
153  7 << 4 | 7, // unicode 0x0000F1
154  7 << 4 | 7, // unicode 0x0000F3
155  7 << 4 | 7, // unicode 0x0000F4
156  7 << 4 | 7, // unicode 0x0000F9
157  7 << 4 | 7, // unicode 0x0000FA
158  7 << 4 | 7 // unicode 0x0000FB
159  // No need to enter FFFD, because it will be the one we'll use by default
160  // 11 << 4 |11 //unicode 0x00FFFD
161 };
162 
163 #define DEFAULT_NANOS_UNICODE_WIDTH ((unsigned char) (11 << 4 | 11)) // For unicode FFFD
164 
165 #define NB_NANOS_UNICODE_CHARS (sizeof(nanos_unicode_width) / sizeof(nanos_unicode_width[0]))
166 #endif // defined(HAVE_UNICODE_SUPPORT)
167 
168 // This function is used to retrieve the length of a string (expressed in bytes) delimited with a
169 // boundary width (expressed in pixels).
170 uint8_t se_get_cropped_length(const char *text,
171  uint8_t text_length,
172  uint32_t width_limit_in_pixels,
173  uint8_t text_format)
174 {
175  char current_char;
176  uint8_t length;
177  uint32_t current_width_in_pixels = 0;
178 
179  for (length = 0; length < text_length; length++) {
180  current_char = text[length];
181 
182  if ((text_format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB) {
183  // Bold.
184  current_width_in_pixels
185  += nanos_characters_width[current_char - NANOS_FIRST_CHAR] & 0x0F;
186  }
187  else {
188  // Regular.
189  current_width_in_pixels
190  += (nanos_characters_width[current_char - NANOS_FIRST_CHAR] >> 0x04) & 0x0F;
191  }
192 
193  // We stop the processing when we reached the limit.
194  if (current_width_in_pixels > width_limit_in_pixels) {
195  break;
196  }
197  }
198 
199  return length;
200 }
201 
202 // This function is used to retrieve the width of a line of text.
203 STATIC_IF_NOT_INDEXED unsigned int se_compute_line_width_light(const char *text,
204  uint8_t text_length,
205  uint8_t text_format)
206 {
207  unsigned char ch;
208  unsigned int line_width = 0;
209 #if defined(HAVE_INDEXED_STRINGS)
210  unsigned int width = 0;
211  unsigned char bold_toggle = 0;
212 
213  // Bold state at the beginning of the line:
214  if ((text_format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB) {
215  bold_toggle = 1;
216  }
217 #endif // defined(HAVE_INDEXED_STRINGS)
218 
219  // We parse the characters of the input text on all the input length.
220  while (text_length--) {
221  ch = *(const unsigned char *) text;
222 #if defined(HAVE_INDEXED_STRINGS)
223  ++text;
224  width = 0;
225 #endif // defined(HAVE_INDEXED_STRINGS)
226 
227 #if defined(HAVE_UNICODE_SUPPORT)
228  unsigned int unicode;
229 
230  // Handle UTF-8 decoding (RFC3629): (https://www.ietf.org/rfc/rfc3629.txt
231  // Char. number range | UTF-8 octet sequence
232  // (hexadecimal) | (binary)
233  // --------------------+---------------------------------------------
234  // 0000 0000-0000 007F | 0xxxxxxx
235  // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
236  // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
237  // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
238 
239  // 4 bytes UTF-8, Unicode 0x1000 to 0x1FFFF
240  if (ch >= 0xF0 && text_length >= 3) {
241  unicode = (ch & 0x07) << 18;
242  unicode |= (*((const unsigned char *) text + 0) & 0x3F) << 12;
243  unicode |= (*((const unsigned char *) text + 1) & 0x3F) << 6;
244  unicode |= (*((const unsigned char *) text + 2) & 0x3F);
245  text += 3;
246  text_length -= 3;
247 
248  // 3 bytes, from 0x800 to 0xFFFF
249  }
250  else if (ch >= 0xE0 && text_length >= 2) {
251  unicode = (ch & 0x0F) << 12;
252  unicode |= (*((const unsigned char *) text + 0) & 0x3F) << 6;
253  unicode |= (*((const unsigned char *) text + 1) & 0x3F);
254  text += 2;
255  text_length -= 2;
256 
257  // 2 bytes UTF-8, Unicode 0x80 to 0x7FF
258  // (0xC0 & 0xC1 are unused and can be used to store something else)
259  }
260  else if (ch >= 0xC2 && text_length >= 1) {
261  unicode = (ch & 0x1F) << 6;
262  unicode |= (*((const unsigned char *) text + 0) & 0x3F);
263  ++text;
264  text_length -= 1;
265  }
266  else {
267  unicode = 0;
268  }
269 #endif // defined(HAVE_UNICODE_SUPPORT)
270 
271  if (ch < NANOS_FIRST_CHAR || ch > NANOS_LAST_CHAR) {
272 #if defined(HAVE_INDEXED_STRINGS)
273  // Only proceed the first line width, not the whole paragraph
274  switch (ch) {
275  case '\n':
276  case '\f':
277  return line_width;
278  case '\b': // Bold toggle: turn Bold On/Off
279  bold_toggle ^= 1;
280  continue;
281  case '\e': // Escape character => ignore it and the extra byte.
282  if (text_length >= 1) { // Take care of \e without additional byte!
283  ++text;
284  text_length -= 1;
285  }
286  continue;
287  }
288 #else // defined(HAVE_INDEXED_STRINGS)
289  if (ch == '\n' || ch == '\r') {
290  break;
291  }
292 #endif // defined(HAVE_INDEXED_STRINGS)
293 
294 #if defined(HAVE_UNICODE_SUPPORT)
295  if (unicode) {
296  unsigned int i;
297  // Find the index of the unicode character we are dealing with.
298  // For the moment, let just parse the full array, but at the end let
299  // use binary search as data are sorted by unicode value !
300  for (i = 0; i < NB_NANOS_UNICODE_CHARS; i++) {
301  if (nanos_unicode_index[i] == unicode) {
302  break;
303  }
304  }
305  // Did we find a corresponding unicode entry?
306  if (i < NB_NANOS_UNICODE_CHARS) {
307  width = nanos_unicode_width[i];
308  }
309  else {
310  // No, use FFFD character width:
311  width = DEFAULT_NANOS_UNICODE_WIDTH;
312  }
313  }
314 #endif // defined(HAVE_UNICODE_SUPPORT)
315  }
316  else {
317 #if defined(HAVE_INDEXED_STRINGS)
318  ch -= NANOS_FIRST_CHAR;
319  width = nanos_characters_width[ch]; // 4 MSB = regular, 4 LSB = extrabold
320 #else // defined(HAVE_INDEXED_STRINGS)
321  // We retrieve the character width, and the paging format indicates whether we are
322  // processing bold characters or not.
323  if ((text_format & PAGING_FORMAT_NB) == PAGING_FORMAT_NB) {
324  // Bold.
325  line_width += nanos_characters_width[ch - NANOS_FIRST_CHAR] & 0x0F;
326  }
327  else {
328  // Regular.
329  line_width += (nanos_characters_width[ch - NANOS_FIRST_CHAR] >> 0x04) & 0x0F;
330  }
331 #endif // defined(HAVE_INDEXED_STRINGS)
332  }
333 #if defined(HAVE_INDEXED_STRINGS)
334  if (width) {
335  if (!bold_toggle) {
336  width >>= 4; // 4 LSB = regular, now
337  }
338  width &= 0x0F; // Keep only the 4 LSB
339  line_width += width;
340  }
341 #else // defined(HAVE_INDEXED_STRINGS)
342  text++;
343 #endif // defined(HAVE_INDEXED_STRINGS)
344  }
345  return line_width;
346 }
347 
348 #endif // !HAVE_SE_SCREEN
349 
350 static bool is_word_delim(unsigned char c)
351 {
352  // return !((c >= 'a' && c <= 'z')
353  // || (c >= 'A' && c <= 'Z')
354  // || (c >= '0' && c <= '9'));
355  return c == ' ' || c == '\n' || c == '-' || c == '_';
356 }
357 
358 // return the number of pages to be displayed when current page to show is -1
359 unsigned int ux_layout_paging_compute(const char *text_to_split,
360  unsigned int page_to_display,
361  ux_layout_paging_state_t *paging_state,
362  bagl_font_id_e font)
363 {
364 #ifndef HAVE_FONTS
365  UNUSED(font);
366 #endif
367 
368  // reset length and offset of lines
369  memset(paging_state->offsets, 0, sizeof(paging_state->offsets));
370  memset(paging_state->lengths, 0, sizeof(paging_state->lengths));
371 
372  // a page has been asked, but no page exists
373  if (page_to_display >= paging_state->count && page_to_display != (unsigned int) -1) {
374  return 0;
375  }
376 
377  // compute offset/length of text of each line for the current page
378  unsigned int page = 0;
379  unsigned int line = 0;
380  const char *start = (text_to_split ? STRPIC(text_to_split) : G_ux.externalText);
381  const char *start2 = start;
382  const char *end = start + strlen(start);
383  while (start < end) {
384  unsigned int len = 0;
385  unsigned int linew = 0;
386  const char *last_word_delim = start;
387  // not reached end of content
388  while (start + len < end
389  // line is not full
390  && linew <= PIXEL_PER_LINE
391  // avoid display buffer overflow for each line
392  // && len < sizeof(G_ux.string_buffer)-1
393  ) {
394  // compute new line length
395 #ifdef HAVE_FONTS
396  linew = bagl_compute_line_width(font, 0, start, len + 1, BAGL_ENCODING_DEFAULT);
397 #else // HAVE_FONTS
398  linew = se_compute_line_width_light(start, len + 1, G_ux.layout_paging.format);
399 #endif // HAVE_FONTS
400  // if (start[len] )
401  if (linew > PIXEL_PER_LINE) {
402  // we got a full line
403  break;
404  }
405  unsigned char c = start[len];
406  if (is_word_delim(c)) {
407  last_word_delim = &start[len];
408  }
409  len++;
410  // new line, don't go further
411  if (c == '\n') {
412  break;
413  }
414  }
415 
416  // if not splitting line onto a word delimiter, then cut at the previous word_delim, adjust
417  // len accordingly (and a wor delim has been found already)
418  if (start + len < end && last_word_delim != start && len) {
419  // if line split within a word
420  if ((!is_word_delim(start[len - 1]) && !is_word_delim(start[len]))) {
421  len = last_word_delim - start;
422  }
423  }
424 
425  // fill up the paging structure
426  if (page_to_display != (unsigned int) -1 && page_to_display == page
427  && page_to_display < paging_state->count) {
428  paging_state->offsets[line] = start - start2;
429  paging_state->lengths[line] = len;
430 
431  // won't compute all pages, we reached the one to display
432 #if UX_LAYOUT_PAGING_LINE_COUNT > 1
433  if (line >= UX_LAYOUT_PAGING_LINE_COUNT - 1)
434 #endif // UX_LAYOUT_PAGING_LINE_COUNT
435  {
436  // a page has been computed
437  return 1;
438  }
439  }
440 
441  // prepare for next line
442  start += len;
443 
444  // skip to next line/page
445  line++;
446  if (
447 #if UX_LAYOUT_PAGING_LINE_COUNT > 1
448  line >= UX_LAYOUT_PAGING_LINE_COUNT &&
449 #endif // UX_LAYOUT_PAGING_LINE_COUNT
450  start < end) {
451  page++;
452  line = 0;
453  }
454  }
455 
456  // return total number of page detected
457  return page + 1;
458 }
459 
460 #endif // HAVE_BAGL
461 #endif // HAVE_UX_FLOW
#define BAGL_ENCODING_DEFAULT
Definition: nbgl_fonts.h:100
unsigned char uint8_t
Definition: usbd_conf.h:53
#define STATIC_IF_NOT_INDEXED
Definition: ux_bagl.h:74
#define G_ux
Definition: ux_bagl.h:345
#define STRPIC(x)