1 /**
2 * @file lv_bidi.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include <stddef.h>
10 #include "lv_bidi.h"
11 #include "lv_txt.h"
12 #include "../lv_misc/lv_mem.h"
13
14 #if LV_USE_BIDI
15
16 /*********************
17 * DEFINES
18 *********************/
19 #define LV_BIDI_BRACKLET_DEPTH 4
20
21 // Highest bit of the 16-bit pos_conv value specifies whether this pos is RTL or not
22 #define GET_POS(x) ((x) & 0x7FFF)
23 #define IS_RTL_POS(x) (((x) & 0x8000) != 0)
24 #define SET_RTL_POS(x, is_rtl) (GET_POS(x) | ((is_rtl)? 0x8000: 0))
25
26 /**********************
27 * TYPEDEFS
28 **********************/
29 typedef struct {
30 uint32_t bracklet_pos;
31 lv_bidi_dir_t dir;
32 } bracket_stack_t;
33
34 /**********************
35 * STATIC PROTOTYPES
36 **********************/
37
38 static uint32_t lv_bidi_get_next_paragraph(const char * txt);
39 static lv_bidi_dir_t lv_bidi_get_letter_dir(uint32_t letter);
40 static bool lv_bidi_letter_is_weak(uint32_t letter);
41 static bool lv_bidi_letter_is_rtl(uint32_t letter);
42 static bool lv_bidi_letter_is_neutral(uint32_t letter);
43
44 static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t max_len, uint32_t * len,
45 uint16_t * pos_conv_len);
46 static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
47 uint16_t pos_conv_len);
48 static uint32_t char_change_to_pair(uint32_t letter);
49 static lv_bidi_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
50 lv_bidi_dir_t base_dir);
51 static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index);
52 static uint32_t get_txt_len(const char * txt, uint32_t max_len);
53
54 /**********************
55 * STATIC VARIABLES
56 **********************/
57 static const uint8_t bracket_left[] = {"<({["};
58 static const uint8_t bracket_right[] = {">)}]"};
59 static bracket_stack_t br_stack[LV_BIDI_BRACKLET_DEPTH];
60 static uint8_t br_stack_p;
61
62 /**********************
63 * MACROS
64 **********************/
65
66 /**********************
67 * GLOBAL FUNCTIONS
68 **********************/
69
70 /**
71 * Convert a text to get the characters in the correct visual order according to
72 * Unicode Bidirectional Algorithm
73 * @param str_in the text to process
74 * @param str_out store the result here. Has the be `strlen(str_in)` length
75 * @param base_dir `LV_BIDI_DIR_LTR` or `LV_BIDI_DIR_RTL`
76 */
_lv_bidi_process(const char * str_in,char * str_out,lv_bidi_dir_t base_dir)77 void _lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir)
78 {
79 if(base_dir == LV_BIDI_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in);
80
81 uint32_t par_start = 0;
82 uint32_t par_len;
83
84 while(str_in[par_start] == '\n' || str_in[par_start] == '\r') {
85 str_out[par_start] = str_in[par_start];
86 par_start ++;
87 }
88
89 while(str_in[par_start] != '\0') {
90 par_len = lv_bidi_get_next_paragraph(&str_in[par_start]);
91 _lv_bidi_process_paragraph(&str_in[par_start], &str_out[par_start], par_len, base_dir, NULL, 0);
92 par_start += par_len;
93
94 while(str_in[par_start] == '\n' || str_in[par_start] == '\r') {
95 str_out[par_start] = str_in[par_start];
96 par_start ++;
97 }
98 }
99
100 str_out[par_start] = '\0';
101 }
102
103 /**
104 * Auto-detect the direction of a text based on the first strong character
105 * @param txt the text to process
106 * @return `LV_BIDI_DIR_LTR` or `LV_BIDI_DIR_RTL`
107 */
_lv_bidi_detect_base_dir(const char * txt)108 lv_bidi_dir_t _lv_bidi_detect_base_dir(const char * txt)
109 {
110 uint32_t i = 0;
111 uint32_t letter;
112 while(txt[i] != '\0') {
113 letter = _lv_txt_encoded_next(txt, &i);
114
115 lv_bidi_dir_t dir;
116 dir = lv_bidi_get_letter_dir(letter);
117 if(dir == LV_BIDI_DIR_RTL || dir == LV_BIDI_DIR_LTR) return dir;
118 }
119
120 /*If there were no strong char earlier return with the default base dir */
121 if(LV_BIDI_BASE_DIR_DEF == LV_BIDI_DIR_AUTO) return LV_BIDI_DIR_LTR;
122 else return LV_BIDI_BASE_DIR_DEF;
123 }
124
125 /**
126 * Get the logical position of a character in a line
127 * @param str_in the input string. Can be only one line.
128 * @param bidi_txt internally the text is bidi processed which buffer can be get here.
129 * If not required anymore has to freed with `lv_mem_free()`
130 * Can be `NULL` is unused
131 * @param len length of the line in character count
132 * @param base_dir base direction of the text: `LV_BIDI_DIR_LTR` or `LV_BIDI_DIR_RTL`
133 * @param visual_pos the visual character position which logical position should be get
134 * @param is_rtl tell the the char at `visual_pos` is RTL or LTR context
135 * @return the logical character position
136 */
_lv_bidi_get_logical_pos(const char * str_in,char ** bidi_txt,uint32_t len,lv_bidi_dir_t base_dir,uint32_t visual_pos,bool * is_rtl)137 uint16_t _lv_bidi_get_logical_pos(const char * str_in, char ** bidi_txt, uint32_t len, lv_bidi_dir_t base_dir,
138 uint32_t visual_pos, bool * is_rtl)
139 {
140 uint32_t pos_conv_len = get_txt_len(str_in, len);
141 char * buf = _lv_mem_buf_get(len + 1);
142 if(buf == NULL) return (uint16_t) -1;
143
144 uint16_t * pos_conv_buf = _lv_mem_buf_get(pos_conv_len * sizeof(uint16_t));
145 if(pos_conv_buf == NULL) {
146 _lv_mem_buf_release(buf);
147 return (uint16_t) -1;
148 }
149
150 if(bidi_txt) *bidi_txt = buf;
151
152 _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);
153
154 if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]);
155
156 if(bidi_txt == NULL) _lv_mem_buf_release(buf);
157 uint16_t res = GET_POS(pos_conv_buf[visual_pos]);
158 _lv_mem_buf_release(pos_conv_buf);
159 return res;
160 }
161
162 /**
163 * Get the visual position of a character in a line
164 * @param str_in the input string. Can be only one line.
165 * @param bidi_txt internally the text is bidi processed which buffer can be get here.
166 * If not required anymore has to freed with `lv_mem_free()`
167 * Can be `NULL` is unused
168 * @param len length of the line in character count
169 * @param base_dir base direction of the text: `LV_BIDI_DIR_LTR` or `LV_BIDI_DIR_RTL`
170 * @param logical_pos the logical character position which visual position should be get
171 * @param is_rtl tell the the char at `logical_pos` is RTL or LTR context
172 * @return the visual character position
173 */
_lv_bidi_get_visual_pos(const char * str_in,char ** bidi_txt,uint16_t len,lv_bidi_dir_t base_dir,uint32_t logical_pos,bool * is_rtl)174 uint16_t _lv_bidi_get_visual_pos(const char * str_in, char ** bidi_txt, uint16_t len, lv_bidi_dir_t base_dir,
175 uint32_t logical_pos, bool * is_rtl)
176 {
177 uint32_t pos_conv_len = get_txt_len(str_in, len);
178 char * buf = _lv_mem_buf_get(len + 1);
179 if(buf == NULL) return (uint16_t) -1;
180
181 uint16_t * pos_conv_buf = _lv_mem_buf_get(pos_conv_len * sizeof(uint16_t));
182 if(pos_conv_buf == NULL) {
183 _lv_mem_buf_release(buf);
184 return (uint16_t) -1;
185 }
186
187 if(bidi_txt) *bidi_txt = buf;
188
189 _lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);
190
191 for(uint16_t i = 0; i < pos_conv_len; i++) {
192 if(GET_POS(pos_conv_buf[i]) == logical_pos) {
193
194 if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[i]);
195 _lv_mem_buf_release(pos_conv_buf);
196
197 if(bidi_txt == NULL) _lv_mem_buf_release(buf);
198 return i;
199 }
200 }
201 _lv_mem_buf_release(pos_conv_buf);
202 if(bidi_txt == NULL) _lv_mem_buf_release(buf);
203 return (uint16_t) -1;
204 }
205
206 /**
207 * Bidi process a paragraph of text
208 * @param str_in the string to process
209 * @param str_out store the result here
210 * @param len length of the text
211 * @param base_dir base dir of the text
212 * @param pos_conv_out an `uint16_t` array to store the related logical position of the character.
213 * Can be `NULL` is unused
214 * @param pos_conv_len length of `pos_conv_out` in element count
215 */
_lv_bidi_process_paragraph(const char * str_in,char * str_out,uint32_t len,lv_bidi_dir_t base_dir,uint16_t * pos_conv_out,uint16_t pos_conv_len)216 void _lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_bidi_dir_t base_dir,
217 uint16_t * pos_conv_out, uint16_t pos_conv_len)
218 {
219 uint32_t run_len = 0;
220 lv_bidi_dir_t run_dir;
221 uint32_t rd = 0;
222 uint32_t wr;
223 uint16_t pos_conv_run_len = 0;
224 uint16_t pos_conv_rd = 0;
225 uint16_t pos_conv_wr;
226
227 if(base_dir == LV_BIDI_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in);
228 if(base_dir == LV_BIDI_DIR_RTL) {
229 wr = len;
230 pos_conv_wr = pos_conv_len;
231 }
232 else {
233 wr = 0;
234 pos_conv_wr = 0;
235 }
236
237 if(str_out) str_out[len] = '\0';
238
239 lv_bidi_dir_t dir = base_dir;
240
241 /*Empty the bracket stack*/
242 br_stack_p = 0;
243
244 /*Process neutral chars in the beginning*/
245 while(rd < len) {
246 uint32_t letter = _lv_txt_encoded_next(str_in, &rd);
247 pos_conv_rd++;
248 dir = lv_bidi_get_letter_dir(letter);
249 if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(str_in, rd, len, letter, base_dir);
250 if(dir != LV_BIDI_DIR_NEUTRAL && dir != LV_BIDI_DIR_WEAK) break;
251 }
252
253 if(rd && str_in[rd] != '\0') {
254 _lv_txt_encoded_prev(str_in, &rd);
255 pos_conv_rd--;
256 }
257
258 if(rd) {
259 if(base_dir == LV_BIDI_DIR_LTR) {
260 if(str_out) {
261 _lv_memcpy(&str_out[wr], str_in, rd);
262 wr += rd;
263 }
264 if(pos_conv_out) {
265 fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_rd, 0);
266 pos_conv_wr += pos_conv_rd;
267 }
268 }
269 else {
270 wr -= rd;
271 pos_conv_wr -= pos_conv_rd;
272 rtl_reverse(str_out ? &str_out[wr] : NULL, str_in, rd, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, 0,
273 pos_conv_rd);
274 }
275 }
276
277 /*Get and process the runs*/
278
279 while(rd < len && str_in[rd]) {
280 run_dir = get_next_run(&str_in[rd], base_dir, len - rd, &run_len, &pos_conv_run_len);
281
282 if(base_dir == LV_BIDI_DIR_LTR) {
283 if(run_dir == LV_BIDI_DIR_LTR) {
284 if(str_out) _lv_memcpy(&str_out[wr], &str_in[rd], run_len);
285 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
286 }
287 else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
288 pos_conv_rd, pos_conv_run_len);
289 wr += run_len;
290 pos_conv_wr += pos_conv_run_len;
291 }
292 else {
293 wr -= run_len;
294 pos_conv_wr -= pos_conv_run_len;
295 if(run_dir == LV_BIDI_DIR_LTR) {
296 if(str_out) _lv_memcpy(&str_out[wr], &str_in[rd], run_len);
297 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
298 }
299 else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
300 pos_conv_rd, pos_conv_run_len);
301 }
302
303 rd += run_len;
304 pos_conv_rd += pos_conv_run_len;
305 }
306 }
307
308 /**********************
309 * STATIC FUNCTIONS
310 **********************/
311
312 /**
313 * Get the next paragraph from a text
314 * @param txt the text to process
315 * @return the length of the current paragraph in byte count
316 */
lv_bidi_get_next_paragraph(const char * txt)317 static uint32_t lv_bidi_get_next_paragraph(const char * txt)
318 {
319 uint32_t i = 0;
320
321 _lv_txt_encoded_next(txt, &i);
322
323 while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
324 _lv_txt_encoded_next(txt, &i);
325 }
326
327 return i;
328 }
329
330 /**
331 * Get the direction of a character
332 * @param letter an Unicode character
333 * @return `LV_BIDI_DIR_RTL/LTR/WEAK/NEUTRAL`
334 */
lv_bidi_get_letter_dir(uint32_t letter)335 static lv_bidi_dir_t lv_bidi_get_letter_dir(uint32_t letter)
336 {
337 if(lv_bidi_letter_is_rtl(letter)) return LV_BIDI_DIR_RTL;
338 if(lv_bidi_letter_is_neutral(letter)) return LV_BIDI_DIR_NEUTRAL;
339 if(lv_bidi_letter_is_weak(letter)) return LV_BIDI_DIR_WEAK;
340
341 return LV_BIDI_DIR_LTR;
342 }
343 /**
344 * Tell whether a character is weak or not
345 * @param letter an Unicode character
346 * @return true/false
347 */
lv_bidi_letter_is_weak(uint32_t letter)348 static bool lv_bidi_letter_is_weak(uint32_t letter)
349 {
350 uint32_t i = 0;
351 static const char weaks[] = "0123456789";
352
353 do {
354 uint32_t x = _lv_txt_encoded_next(weaks, &i);
355 if(letter == x) {
356 return true;
357 }
358 } while(weaks[i] != '\0');
359
360 return false;
361 }
362 /**
363 * Tell whether a character is RTL or not
364 * @param letter an Unicode character
365 * @return true/false
366 */
lv_bidi_letter_is_rtl(uint32_t letter)367 static bool lv_bidi_letter_is_rtl(uint32_t letter)
368 {
369 if(letter >= 0x5d0 && letter <= 0x5ea) return true;
370 if(letter == 0x202E) return true; /*Unicode of LV_BIDI_RLO*/
371
372 /* Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/
373 if(letter >= 0x600 && letter <= 0x6FF) return true;
374 if(letter >= 0xFB50 && letter <= 0xFDFF) return true;
375 if(letter >= 0xFE70 && letter <= 0xFEFF) return true;
376
377 return false;
378 }
379
380 /**
381 * Tell whether a character is neutral or not
382 * @param letter an Unicode character
383 * @return true/false
384 */
lv_bidi_letter_is_neutral(uint32_t letter)385 static bool lv_bidi_letter_is_neutral(uint32_t letter)
386 {
387 uint16_t i;
388 static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
389 for(i = 0; neutrals[i] != '\0'; i++) {
390 if(letter == (uint32_t)neutrals[i]) return true;
391 }
392
393 return false;
394 }
395
get_txt_len(const char * txt,uint32_t max_len)396 static uint32_t get_txt_len(const char * txt, uint32_t max_len)
397 {
398 uint32_t len = 0;
399 uint32_t i = 0;
400
401 while(i < max_len && txt[i] != '\0') {
402 _lv_txt_encoded_next(txt, &i);
403 len++;
404 }
405
406 return len;
407 }
408
fill_pos_conv(uint16_t * out,uint16_t len,uint16_t index)409 static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index)
410 {
411 uint16_t i;
412 for(i = 0; i < len; i++) {
413 out[i] = SET_RTL_POS(index, false);
414 index++;
415 }
416 }
417
get_next_run(const char * txt,lv_bidi_dir_t base_dir,uint32_t max_len,uint32_t * len,uint16_t * pos_conv_len)418 static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t max_len, uint32_t * len,
419 uint16_t * pos_conv_len)
420 {
421 uint32_t i = 0;
422 uint32_t letter;
423
424 uint16_t pos_conv_i = 0;
425
426 letter = _lv_txt_encoded_next(txt, NULL);
427 lv_bidi_dir_t dir = lv_bidi_get_letter_dir(letter);
428 if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, 0, max_len, letter, base_dir);
429
430 /*Find the first strong char. Skip the neutrals*/
431 while(dir == LV_BIDI_DIR_NEUTRAL || dir == LV_BIDI_DIR_WEAK) {
432 letter = _lv_txt_encoded_next(txt, &i);
433 pos_conv_i++;
434 dir = lv_bidi_get_letter_dir(letter);
435 if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, i, max_len, letter, base_dir);
436
437 if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') {
438 *len = i;
439 *pos_conv_len = pos_conv_i;
440 return base_dir;
441 }
442 }
443
444 lv_bidi_dir_t run_dir = dir;
445
446 uint32_t i_prev = i;
447 uint32_t i_last_strong = i;
448 uint16_t pos_conv_i_prev = pos_conv_i;
449 uint16_t pos_conv_i_last_strong = pos_conv_i;
450
451 /*Find the next char which has different direction*/
452 lv_bidi_dir_t next_dir = base_dir;
453 while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
454 letter = _lv_txt_encoded_next(txt, &i);
455 pos_conv_i++;
456 next_dir = lv_bidi_get_letter_dir(letter);
457 if(next_dir == LV_BIDI_DIR_NEUTRAL) next_dir = bracket_process(txt, i, max_len, letter, base_dir);
458
459 /*New dir found?*/
460 if((next_dir == LV_BIDI_DIR_RTL || next_dir == LV_BIDI_DIR_LTR) && next_dir != run_dir) {
461 /*Include neutrals if `run_dir == base_dir` */
462 if(run_dir == base_dir) {
463 *len = i_prev;
464 *pos_conv_len = pos_conv_i_prev;
465 }
466 /*Exclude neutrals if `run_dir != base_dir` */
467 else {
468 *len = i_last_strong;
469 *pos_conv_len = pos_conv_i_last_strong;
470 }
471
472 return run_dir;
473 }
474
475 if(next_dir != LV_BIDI_DIR_NEUTRAL) {
476 i_last_strong = i;
477 pos_conv_i_last_strong = pos_conv_i;
478 }
479
480 i_prev = i;
481 pos_conv_i_prev = pos_conv_i;
482 }
483
484 /*Handle end of of string. Apply `base_dir` on trailing neutrals*/
485
486 /*Include neutrals if `run_dir == base_dir` */
487 if(run_dir == base_dir) {
488 *len = i_prev;
489 *pos_conv_len = pos_conv_i_prev;
490 }
491 /*Exclude neutrals if `run_dir != base_dir` */
492 else {
493 *len = i_last_strong;
494 *pos_conv_len = pos_conv_i_last_strong;
495 }
496
497 return run_dir;
498 }
499
rtl_reverse(char * dest,const char * src,uint32_t len,uint16_t * pos_conv_out,uint16_t pos_conv_rd_base,uint16_t pos_conv_len)500 static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
501 uint16_t pos_conv_len)
502 {
503 uint32_t i = len;
504 uint32_t wr = 0;
505 uint16_t pos_conv_i = pos_conv_len;
506 uint16_t pos_conv_wr = 0;
507
508 while(i) {
509 uint32_t letter = _lv_txt_encoded_prev(src, &i);
510 uint16_t pos_conv_letter = --pos_conv_i;
511
512 /*Keep weak letters (numbers) as LTR*/
513 if(lv_bidi_letter_is_weak(letter)) {
514 uint32_t last_weak = i;
515 uint32_t first_weak = i;
516 uint16_t pos_conv_last_weak = pos_conv_i;
517 uint16_t pos_conv_first_weak = pos_conv_i;
518 while(i) {
519 letter = _lv_txt_encoded_prev(src, &i);
520 pos_conv_letter = --pos_conv_i;
521
522 /*No need to call `char_change_to_pair` because there not such chars here*/
523
524 /*Finish on non-weak char */
525 /*but treat number and currency related chars as weak*/
526 if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') {
527 _lv_txt_encoded_next(src, &i); /*Rewind one letter*/
528 pos_conv_i++;
529 first_weak = i;
530 pos_conv_first_weak = pos_conv_i;
531 break;
532 }
533 }
534 if(i == 0) {
535 first_weak = 0;
536 pos_conv_first_weak = 0;
537 }
538
539 if(dest) _lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1);
540 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1,
541 pos_conv_rd_base + pos_conv_first_weak);
542 wr += last_weak - first_weak + 1;
543 pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1;
544 }
545
546 /*Simply store in reversed order*/
547 else {
548 uint32_t letter_size = _lv_txt_encoded_size((const char *)&src[i]);
549 /*Swap arithmetical symbols*/
550 if(letter_size == 1) {
551 uint32_t new_letter = letter = char_change_to_pair(letter);
552 if(dest) dest[wr] = (uint8_t)new_letter;
553 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true);
554 wr++;
555 pos_conv_wr++;
556 }
557 /*Just store the letter*/
558 else {
559 if(dest) _lv_memcpy(&dest[wr], &src[i], letter_size);
560 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true);
561 wr += letter_size;
562 pos_conv_wr++;
563 }
564 }
565 }
566 }
567
char_change_to_pair(uint32_t letter)568 static uint32_t char_change_to_pair(uint32_t letter)
569 {
570
571 uint8_t i;
572 for(i = 0; bracket_left[i] != '\0'; i++) {
573 if(letter == bracket_left[i]) return bracket_right[i];
574 }
575
576 for(i = 0; bracket_right[i] != '\0'; i++) {
577 if(letter == bracket_right[i]) return bracket_left[i];
578 }
579
580 return letter;
581 }
582
bracket_process(const char * txt,uint32_t next_pos,uint32_t len,uint32_t letter,lv_bidi_dir_t base_dir)583 static lv_bidi_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
584 lv_bidi_dir_t base_dir)
585 {
586 lv_bidi_dir_t bracket_dir = LV_BIDI_DIR_NEUTRAL;
587
588 uint8_t i;
589 /*Is the letter an opening bracket?*/
590 for(i = 0; bracket_left[i] != '\0'; i++) {
591 if(bracket_left[i] == letter) {
592 /* If so find it's matching closing bracket.
593 * If a char with base dir. direction is found then the brackets will have `base_dir` direction*/
594 uint32_t txt_i = next_pos;
595 while(txt_i < len) {
596 uint32_t letter_next = _lv_txt_encoded_next(txt, &txt_i);
597 if(letter_next == bracket_right[i]) {
598 /*Closing bracket found*/
599 break;
600 }
601 else {
602 /*Save the dir*/
603 lv_bidi_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
604 if(letter_dir == base_dir) {
605 bracket_dir = base_dir;
606 }
607 }
608 }
609
610 /*There were no matching closing bracket*/
611 if(txt_i > len) return LV_BIDI_DIR_NEUTRAL;
612
613 /*There where a strong char with base dir in the bracket so the dir is found.*/
614 if(bracket_dir != LV_BIDI_DIR_NEUTRAL && bracket_dir != LV_BIDI_DIR_WEAK) break;
615
616 /*If there were no matching strong chars in the brackets then check the previous chars*/
617 txt_i = next_pos;
618 if(txt_i) _lv_txt_encoded_prev(txt, &txt_i);
619 while(txt_i > 0) {
620 uint32_t letter_next = _lv_txt_encoded_prev(txt, &txt_i);
621 lv_bidi_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
622 if(letter_dir == LV_BIDI_DIR_LTR || letter_dir == LV_BIDI_DIR_RTL) {
623 bracket_dir = letter_dir;
624 break;
625 }
626 }
627
628
629 /*There where a previous strong char which can be used*/
630 if(bracket_dir != LV_BIDI_DIR_NEUTRAL) break;
631
632 /*There were no strong chars before the bracket, so use the base dir.*/
633 if(txt_i == 0) bracket_dir = base_dir;
634
635 break;
636 }
637 }
638
639
640 /*The letter was an opening bracket*/
641 if(bracket_left[i] != '\0') {
642
643 if(bracket_dir == LV_BIDI_DIR_NEUTRAL || br_stack_p == LV_BIDI_BRACKLET_DEPTH) return LV_BIDI_DIR_NEUTRAL;
644
645 br_stack[br_stack_p].bracklet_pos = i;
646 br_stack[br_stack_p].dir = bracket_dir;
647
648 br_stack_p++;
649 return bracket_dir;
650 }
651 else if(br_stack_p > 0) {
652 /*Is the letter a closing bracket of the last opening?*/
653 if(letter == bracket_right[br_stack[br_stack_p - 1].bracklet_pos]) {
654 bracket_dir = br_stack[br_stack_p - 1].dir;
655 br_stack_p--;
656 return bracket_dir;
657 }
658 }
659
660 return LV_BIDI_DIR_NEUTRAL;
661 }
662
663
664 #endif /*LV_USE_BIDI*/
665