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 "../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_base_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_base_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_base_dir_t get_next_run(const char * txt, lv_base_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_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
50                                      lv_base_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_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
76  */
_lv_bidi_process(const char * str_in,char * str_out,lv_base_dir_t base_dir)77 void _lv_bidi_process(const char * str_in, char * str_out, lv_base_dir_t base_dir)
78 {
79     if(base_dir == LV_BASE_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_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
107  */
_lv_bidi_detect_base_dir(const char * txt)108 lv_base_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_base_dir_t dir;
116         dir = lv_bidi_get_letter_dir(letter);
117         if(dir == LV_BASE_DIR_RTL || dir == LV_BASE_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_BASE_DIR_AUTO) return LV_BASE_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_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
133  * @param visual_pos the visual character position which logical position should be get
134  * @param is_rtl tell 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_base_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_base_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_BASE_DIR_LTR` or `LV_BASE_DIR_RTL`
170  * @param logical_pos the logical character position which visual position should be get
171  * @param is_rtl tell 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_base_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_base_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_base_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_base_dir_t base_dir,
217                                 uint16_t * pos_conv_out, uint16_t pos_conv_len)
218 {
219     uint32_t run_len = 0;
220     lv_base_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_BASE_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(str_in);
228     if(base_dir == LV_BASE_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_base_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_BASE_DIR_NEUTRAL)  dir = bracket_process(str_in, rd, len, letter, base_dir);
250         if(dir != LV_BASE_DIR_NEUTRAL && dir != LV_BASE_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_BASE_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_BASE_DIR_LTR) {
283             if(run_dir == LV_BASE_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_BASE_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 
lv_bidi_calculate_align(lv_text_align_t * align,lv_base_dir_t * base_dir,const char * txt)308 void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt)
309 {
310     if(*base_dir == LV_BASE_DIR_AUTO) *base_dir = _lv_bidi_detect_base_dir(txt);
311 
312     if(*align == LV_TEXT_ALIGN_AUTO) {
313         if(*base_dir == LV_BASE_DIR_RTL) *align = LV_TEXT_ALIGN_RIGHT;
314         else *align = LV_TEXT_ALIGN_LEFT;
315     }
316 }
317 
318 /**********************
319  *   STATIC FUNCTIONS
320  **********************/
321 
322 /**
323  * Get the next paragraph from a text
324  * @param txt the text to process
325  * @return the length of the current paragraph in byte count
326  */
lv_bidi_get_next_paragraph(const char * txt)327 static uint32_t lv_bidi_get_next_paragraph(const char * txt)
328 {
329     uint32_t i = 0;
330 
331     _lv_txt_encoded_next(txt, &i);
332 
333     while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
334         _lv_txt_encoded_next(txt, &i);
335     }
336 
337     return i;
338 }
339 
340 /**
341  * Get the direction of a character
342  * @param letter an Unicode character
343  * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL`
344  */
lv_bidi_get_letter_dir(uint32_t letter)345 static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter)
346 {
347     if(lv_bidi_letter_is_rtl(letter)) return LV_BASE_DIR_RTL;
348     if(lv_bidi_letter_is_neutral(letter)) return LV_BASE_DIR_NEUTRAL;
349     if(lv_bidi_letter_is_weak(letter)) return LV_BASE_DIR_WEAK;
350 
351     return LV_BASE_DIR_LTR;
352 }
353 /**
354  * Tell whether a character is weak or not
355  * @param letter an Unicode character
356  * @return true/false
357  */
lv_bidi_letter_is_weak(uint32_t letter)358 static bool lv_bidi_letter_is_weak(uint32_t letter)
359 {
360     uint32_t i = 0;
361     static const char weaks[] = "0123456789";
362 
363     do {
364         uint32_t x = _lv_txt_encoded_next(weaks, &i);
365         if(letter == x) {
366             return true;
367         }
368     } while(weaks[i] != '\0');
369 
370     return false;
371 }
372 /**
373  * Tell whether a character is RTL or not
374  * @param letter an Unicode character
375  * @return true/false
376  */
lv_bidi_letter_is_rtl(uint32_t letter)377 static bool lv_bidi_letter_is_rtl(uint32_t letter)
378 {
379     if(letter >= 0x5d0 && letter <= 0x5ea) return true;
380     if(letter == 0x202E) return true;               /*Unicode of LV_BIDI_RLO*/
381 
382     /*Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/
383     if(letter >= 0x600 && letter <= 0x6FF) return true;
384     if(letter >= 0xFB50 && letter <= 0xFDFF) return true;
385     if(letter >= 0xFE70 && letter <= 0xFEFF) return true;
386 
387     return false;
388 }
389 
390 /**
391  * Tell whether a character is neutral or not
392  * @param letter an Unicode character
393  * @return true/false
394  */
lv_bidi_letter_is_neutral(uint32_t letter)395 static bool lv_bidi_letter_is_neutral(uint32_t letter)
396 {
397     uint16_t i;
398     static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
399     for(i = 0; neutrals[i] != '\0'; i++) {
400         if(letter == (uint32_t)neutrals[i]) return true;
401     }
402 
403     return false;
404 }
405 
get_txt_len(const char * txt,uint32_t max_len)406 static uint32_t get_txt_len(const char * txt, uint32_t max_len)
407 {
408     uint32_t len = 0;
409     uint32_t i   = 0;
410 
411     while(i < max_len && txt[i] != '\0') {
412         _lv_txt_encoded_next(txt, &i);
413         len++;
414     }
415 
416     return len;
417 }
418 
fill_pos_conv(uint16_t * out,uint16_t len,uint16_t index)419 static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index)
420 {
421     uint16_t i;
422     for(i = 0; i < len; i++) {
423         out[i] = SET_RTL_POS(index, false);
424         index++;
425     }
426 }
427 
get_next_run(const char * txt,lv_base_dir_t base_dir,uint32_t max_len,uint32_t * len,uint16_t * pos_conv_len)428 static lv_base_dir_t get_next_run(const char * txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t * len,
429                                   uint16_t  * pos_conv_len)
430 {
431     uint32_t i = 0;
432     uint32_t letter;
433 
434     uint16_t pos_conv_i = 0;
435 
436     letter = _lv_txt_encoded_next(txt, NULL);
437     lv_base_dir_t dir = lv_bidi_get_letter_dir(letter);
438     if(dir == LV_BASE_DIR_NEUTRAL)  dir = bracket_process(txt, 0, max_len, letter, base_dir);
439 
440     /*Find the first strong char. Skip the neutrals*/
441     while(dir == LV_BASE_DIR_NEUTRAL || dir == LV_BASE_DIR_WEAK) {
442         letter = _lv_txt_encoded_next(txt, &i);
443 
444         pos_conv_i++;
445         dir = lv_bidi_get_letter_dir(letter);
446         if(dir == LV_BASE_DIR_NEUTRAL)  dir = bracket_process(txt, i, max_len, letter, base_dir);
447 
448         if(dir == LV_BASE_DIR_LTR || dir == LV_BASE_DIR_RTL)  break;
449 
450         if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') {
451             *len = i;
452             *pos_conv_len = pos_conv_i;
453             return base_dir;
454         }
455     }
456 
457     lv_base_dir_t run_dir = dir;
458 
459     uint32_t i_prev = i;
460     uint32_t i_last_strong = i;
461     uint16_t pos_conv_i_prev = pos_conv_i;
462     uint16_t pos_conv_i_last_strong = pos_conv_i;
463 
464     /*Find the next char which has different direction*/
465     lv_base_dir_t next_dir = base_dir;
466     while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
467         letter = _lv_txt_encoded_next(txt, &i);
468         pos_conv_i++;
469         next_dir  = lv_bidi_get_letter_dir(letter);
470         if(next_dir == LV_BASE_DIR_NEUTRAL)  next_dir = bracket_process(txt, i, max_len, letter, base_dir);
471 
472         if(next_dir == LV_BASE_DIR_WEAK) {
473             if(run_dir == LV_BASE_DIR_RTL) {
474                 if(base_dir == LV_BASE_DIR_RTL) {
475                     next_dir = LV_BASE_DIR_LTR;
476                 }
477             }
478         }
479 
480         /*New dir found?*/
481         if((next_dir == LV_BASE_DIR_RTL || next_dir == LV_BASE_DIR_LTR) && next_dir != run_dir) {
482             /*Include neutrals if `run_dir == base_dir`*/
483             if(run_dir == base_dir) {
484                 *len = i_prev;
485                 *pos_conv_len = pos_conv_i_prev;
486             }
487             /*Exclude neutrals if `run_dir != base_dir`*/
488             else {
489                 *len = i_last_strong;
490                 *pos_conv_len = pos_conv_i_last_strong;
491             }
492 
493             return run_dir;
494         }
495 
496         if(next_dir != LV_BASE_DIR_NEUTRAL) {
497             i_last_strong = i;
498             pos_conv_i_last_strong = pos_conv_i;
499         }
500 
501         i_prev = i;
502         pos_conv_i_prev = pos_conv_i;
503     }
504 
505     /*Handle end of of string. Apply `base_dir` on trailing neutrals*/
506 
507     /*Include neutrals if `run_dir == base_dir`*/
508     if(run_dir == base_dir) {
509         *len = i_prev;
510         *pos_conv_len = pos_conv_i_prev;
511     }
512     /*Exclude neutrals if `run_dir != base_dir`*/
513     else {
514         *len = i_last_strong;
515         *pos_conv_len = pos_conv_i_last_strong;
516     }
517 
518     return run_dir;
519 }
520 
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)521 static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
522                         uint16_t pos_conv_len)
523 {
524     uint32_t i = len;
525     uint32_t wr = 0;
526     uint16_t pos_conv_i = pos_conv_len;
527     uint16_t pos_conv_wr = 0;
528 
529     while(i) {
530         uint32_t letter = _lv_txt_encoded_prev(src, &i);
531         uint16_t pos_conv_letter = --pos_conv_i;
532 
533         /*Keep weak letters (numbers) as LTR*/
534         if(lv_bidi_letter_is_weak(letter)) {
535             uint32_t last_weak = i;
536             uint32_t first_weak = i;
537             uint16_t pos_conv_last_weak = pos_conv_i;
538             uint16_t pos_conv_first_weak = pos_conv_i;
539             while(i) {
540                 letter = _lv_txt_encoded_prev(src, &i);
541                 pos_conv_letter = --pos_conv_i;
542 
543                 /*No need to call `char_change_to_pair` because there not such chars here*/
544 
545                 /*Finish on non-weak char*/
546                 /*but treat number and currency related chars as weak*/
547                 if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') {
548                     _lv_txt_encoded_next(src, &i);   /*Rewind one letter*/
549                     pos_conv_i++;
550                     first_weak = i;
551                     pos_conv_first_weak = pos_conv_i;
552                     break;
553                 }
554             }
555             if(i == 0) {
556                 first_weak = 0;
557                 pos_conv_first_weak = 0;
558             }
559 
560             if(dest) lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1);
561             if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1,
562                                                pos_conv_rd_base + pos_conv_first_weak);
563             wr += last_weak - first_weak + 1;
564             pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1;
565         }
566 
567         /*Simply store in reversed order*/
568         else {
569             uint32_t letter_size = _lv_txt_encoded_size((const char *)&src[i]);
570             /*Swap arithmetical symbols*/
571             if(letter_size == 1) {
572                 uint32_t new_letter = letter = char_change_to_pair(letter);
573                 if(dest) dest[wr] = (uint8_t)new_letter;
574                 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true);
575                 wr++;
576                 pos_conv_wr++;
577             }
578             /*Just store the letter*/
579             else {
580                 if(dest) lv_memcpy(&dest[wr], &src[i], letter_size);
581                 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true);
582                 wr += letter_size;
583                 pos_conv_wr++;
584             }
585         }
586     }
587 }
588 
char_change_to_pair(uint32_t letter)589 static uint32_t char_change_to_pair(uint32_t letter)
590 {
591 
592     uint8_t i;
593     for(i = 0; bracket_left[i] != '\0'; i++) {
594         if(letter == bracket_left[i]) return bracket_right[i];
595     }
596 
597     for(i = 0; bracket_right[i] != '\0'; i++) {
598         if(letter == bracket_right[i]) return bracket_left[i];
599     }
600 
601     return letter;
602 }
603 
bracket_process(const char * txt,uint32_t next_pos,uint32_t len,uint32_t letter,lv_base_dir_t base_dir)604 static lv_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
605                                      lv_base_dir_t base_dir)
606 {
607     lv_base_dir_t bracket_dir = LV_BASE_DIR_NEUTRAL;
608 
609     uint8_t i;
610     /*Is the letter an opening bracket?*/
611     for(i = 0; bracket_left[i] != '\0'; i++) {
612         if(bracket_left[i] == letter) {
613             /*If so find its matching closing bracket.
614              *If a char with base dir. direction is found then the brackets will have `base_dir` direction*/
615             uint32_t txt_i = next_pos;
616             while(txt_i < len) {
617                 uint32_t letter_next = _lv_txt_encoded_next(txt, &txt_i);
618                 if(letter_next == bracket_right[i]) {
619                     /*Closing bracket found*/
620                     break;
621                 }
622                 else {
623                     /*Save the dir*/
624                     lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
625                     if(letter_dir == base_dir) {
626                         bracket_dir = base_dir;
627                     }
628                 }
629             }
630 
631             /*There were no matching closing bracket*/
632             if(txt_i > len)  return LV_BASE_DIR_NEUTRAL;
633 
634             /*There where a strong char with base dir in the bracket so the dir is found.*/
635             if(bracket_dir != LV_BASE_DIR_NEUTRAL && bracket_dir != LV_BASE_DIR_WEAK) break;
636 
637             /*If there were no matching strong chars in the brackets then check the previous chars*/
638             txt_i = next_pos;
639             if(txt_i) _lv_txt_encoded_prev(txt, &txt_i);
640             while(txt_i > 0) {
641                 uint32_t letter_next = _lv_txt_encoded_prev(txt, &txt_i);
642                 lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
643                 if(letter_dir == LV_BASE_DIR_LTR || letter_dir == LV_BASE_DIR_RTL) {
644                     bracket_dir = letter_dir;
645                     break;
646                 }
647             }
648 
649             /*There where a previous strong char which can be used*/
650             if(bracket_dir != LV_BASE_DIR_NEUTRAL) break;
651 
652             /*There were no strong chars before the bracket, so use the base dir.*/
653             if(txt_i == 0) bracket_dir = base_dir;
654 
655             break;
656         }
657     }
658 
659     /*The letter was an opening bracket*/
660     if(bracket_left[i] != '\0') {
661 
662         if(bracket_dir == LV_BASE_DIR_NEUTRAL || br_stack_p == LV_BIDI_BRACKLET_DEPTH) return LV_BASE_DIR_NEUTRAL;
663 
664         br_stack[br_stack_p].bracklet_pos = i;
665         br_stack[br_stack_p].dir = bracket_dir;
666 
667         br_stack_p++;
668         return bracket_dir;
669     }
670     else if(br_stack_p > 0) {
671         /*Is the letter a closing bracket of the last opening?*/
672         if(letter == bracket_right[br_stack[br_stack_p - 1].bracklet_pos]) {
673             bracket_dir = br_stack[br_stack_p - 1].dir;
674             br_stack_p--;
675             return bracket_dir;
676         }
677     }
678 
679     return LV_BASE_DIR_NEUTRAL;
680 }
681 
682 #endif /*LV_USE_BIDI*/
683