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