acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_bidi.c (22834B)

      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  */
     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  */
    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  */
    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  */
    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  */
    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 
    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  */
    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 a Unicode character
    343  * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL`
    344  */
    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 a Unicode character
    356  * @return true/false
    357  */
    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 a Unicode character
    375  * @return true/false
    376  */
    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 a Unicode character
    393  * @return true/false
    394  */
    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 
    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 
    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 
    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 
    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 
    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 
    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*/