From 57a8ee8e325e861be920d0860bedd3e388f0b027 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Fri, 25 Oct 2019 06:15:11 +0200 Subject: [PATCH] bidi: add bracket algorithm --- src/lv_misc/lv_bidi.c | 110 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/src/lv_misc/lv_bidi.c b/src/lv_misc/lv_bidi.c index e9929bc67..4c365d660 100644 --- a/src/lv_misc/lv_bidi.c +++ b/src/lv_misc/lv_bidi.c @@ -15,10 +15,16 @@ /********************* * DEFINES *********************/ +#define LV_BIDI_BRACKLET_DEPTH 4 /********************** * TYPEDEFS **********************/ +typedef struct +{ + uint32_t bracklet_pos; + lv_bidi_dir_t dir; +}bracket_stack_t; /********************** * STATIC PROTOTYPES @@ -28,10 +34,15 @@ static uint32_t get_next_paragraph(const char * txt); static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint32_t * len); static void rtl_reverse(char * dest, const char * src, uint32_t len); static uint32_t char_change_to_pair(uint32_t letter); +static lv_bidi_dir_t bracket_process(const uint8_t * txt, uint32_t next_pos, uint32_t len, uint32_t letter, lv_bidi_dir_t base_dir); /********************** * STATIC VARIABLES **********************/ +static const uint8_t bracket_left[] = {"<({["}; +static const uint8_t bracket_right[] = {">)}]"}; +static bracket_stack_t br_stack[LV_BIDI_BRACKLET_DEPTH]; +static uint8_t br_stack_p; /********************** * MACROS @@ -43,6 +54,7 @@ static uint32_t char_change_to_pair(uint32_t letter); void lv_bidi_process(const char * str_in, char * str_out, lv_bidi_dir_t base_dir) { + br_stack_p = 0; if(base_dir == LV_BIDI_DIR_AUTO) base_dir = lv_bidi_detect_base_dir(str_in); @@ -151,6 +163,8 @@ static void process_paragraph(const char * str_in, char * str_out, uint32_t len, while(rd < len) { uint32_t letter = lv_txt_encoded_next(str_in, &rd); dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(str_in, rd, len, letter, base_dir); + if(dir != LV_BIDI_DIR_NEUTRAL && dir != LV_BIDI_DIR_WEAK) break; } @@ -204,11 +218,15 @@ static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint letter = lv_txt_encoded_next(txt, NULL); lv_bidi_dir_t dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, 0, len, letter, base_dir); + /*Find the first strong char. Skip the neutrals*/ while(dir == LV_BIDI_DIR_NEUTRAL || dir == LV_BIDI_DIR_WEAK) { letter = lv_txt_encoded_next(txt, &i); dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, i, len, letter, base_dir); + if(txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') { *len = i; return base_dir; @@ -225,6 +243,7 @@ static lv_bidi_dir_t get_next_run(const char * txt, lv_bidi_dir_t base_dir, uint while(txt[i] != '\0'&& txt[i] != '\n' && txt[i] != '\r') { letter = lv_txt_encoded_next(txt, &i); next_dir = lv_bidi_get_letter_dir(letter); + if(dir == LV_BIDI_DIR_NEUTRAL) dir = bracket_process(txt, i, len, letter, base_dir); /*New dir found?*/ if((next_dir == LV_BIDI_DIR_RTL || next_dir == LV_BIDI_DIR_LTR) && next_dir != run_dir) { @@ -303,19 +322,98 @@ static void rtl_reverse(char * dest, const char * src, uint32_t len) static uint32_t char_change_to_pair(uint32_t letter) { - static uint8_t left[] = {"<({["}; - static uint8_t right[] = {">)}]"}; uint8_t i; - for(i = 0; left[i] != '\0'; i++) { - if(letter == left[i]) return right[i]; + for(i = 0; bracket_left[i] != '\0'; i++) { + if(letter == bracket_left[i]) return bracket_right[i]; } - for(i = 0; right[i] != '\0'; i++) { - if(letter == right[i]) return left[i]; + for(i = 0; bracket_right[i] != '\0'; i++) { + if(letter == bracket_right[i]) return bracket_left[i]; } return letter; } +static lv_bidi_dir_t bracket_process(const uint8_t * txt, uint32_t next_pos, uint32_t len, uint32_t letter, lv_bidi_dir_t base_dir) +{ + if(br_stack_p >= LV_BIDI_BRACKLET_DEPTH) return LV_BIDI_DIR_NEUTRAL; + + lv_bidi_dir_t bracket_dir = LV_BIDI_DIR_NEUTRAL; + + uint8_t i; + /*Is the letter an opening bracket?*/ + for(i = 0; bracket_left[i] != '\0'; i++) { + if(bracket_left[i] == letter) { + /* If so find it's matching closing bracket. + * If a char with base dir. direction is found then the brackets will have `base_dir` direction*/ + uint32_t txt_i = next_pos; + while(txt_i < len) { + uint32_t letter_next = lv_txt_encoded_next(txt, &txt_i); + if(letter_next == bracket_right[i]) { + /*Closing bracket found*/ + break; + } else { + /*Save the dir*/ + lv_bidi_dir_t letter_dir = lv_bidi_get_letter_dir(letter); + if(letter_dir == base_dir) { + bracket_dir = base_dir; + } + } + } + + /*There were no matching closing bracket*/ + if(txt_i >= len) return LV_BIDI_DIR_NEUTRAL; + + /*There where a strong char with base dir in the bracket so the dir is found.*/ + if(bracket_dir != LV_BIDI_DIR_NEUTRAL) break; + + /*If there were no matching strong chars in the brackets then check the previous chars*/ + txt_i = next_pos; + if(txt_i) lv_txt_encoded_prev(txt, &txt_i); + if(txt_i) lv_txt_encoded_prev(txt, &txt_i); + while(txt_i > 0) { + uint32_t letter_next = lv_txt_encoded_prev(txt, &txt_i); + lv_bidi_dir_t letter_dir = lv_bidi_get_letter_dir(letter); + if(letter_dir == LV_BIDI_DIR_LTR || letter_dir == LV_BIDI_DIR_RTL) { + bracket_dir = letter_dir; + break; + } + } + + + /*There where a previous strong char which can be used*/ + if(bracket_dir != LV_BIDI_DIR_NEUTRAL) break; + + /*There were no strong chars before the bracket, so use the base dir.*/ + if(txt_i == 0) bracket_dir = base_dir; + + break; + } + } + + + /*The letter was an opening bracket*/ + if(bracket_left[i] != '\0') { + + if(bracket_dir == LV_BIDI_DIR_NEUTRAL) break; + + br_stack[br_stack_p].bracklet_pos = i; + br_stack[br_stack_p].dir = bracket_dir; + + br_stack_p++; + return bracket_dir; + } else { + /*Is the letter a closing bracket of the last opening?*/ + if(letter == bracket_right[br_stack[br_stack_p].bracklet_pos]) { + bracket_dir = br_stack[br_stack_p].dir; + br_stack_p--; + return bracket_dir; + } + } + + return LV_BIDI_DIR_NEUTRAL; +} + + #endif /*LV_USE_BIDI*/