diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index 5675178dc7..2959c07e85 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -336,9 +336,19 @@ void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_ds recolor_cmd_state = RECOLOR_CMD_STATE_WAIT_FOR_PARAMETER; next_char_offset = 0; #if LV_USE_BIDI - char * bidi_txt = lv_malloc(line_end - line_start + 1); + size_t bidi_size = line_end - line_start; + char * bidi_txt = lv_malloc(bidi_size + 1); LV_ASSERT_MALLOC(bidi_txt); - lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, line_end - line_start, base_dir, NULL, 0); + + /** + * has_bided = 1: already executed lv_bidi_process_paragraph. + * has_bided = 0: has not been executed lv_bidi_process_paragraph.*/ + if(dsc->has_bided) { + lv_memcpy(bidi_txt, &dsc->text[line_start], bidi_size); + } + else { + lv_bidi_process_paragraph(dsc->text + line_start, bidi_txt, bidi_size, base_dir, NULL, 0); + } #else const char * bidi_txt = dsc->text + line_start; #endif @@ -349,9 +359,14 @@ void lv_draw_label_iterate_characters(lv_draw_task_t * t, const lv_draw_label_ds /* Check if the text selection is enabled */ if(sel_start != LV_DRAW_LABEL_NO_TXT_SEL && sel_end != LV_DRAW_LABEL_NO_TXT_SEL) { #if LV_USE_BIDI - logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start); - uint32_t c_idx = lv_text_encoded_get_char_id(bidi_txt, next_char_offset); - logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, c_idx, NULL); + if(dsc->has_bided) { + logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset); + } + else { + logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start); + uint32_t c_idx = lv_text_encoded_get_char_id(bidi_txt, next_char_offset); + logical_char_pos += lv_bidi_get_logical_pos(bidi_txt, NULL, line_end - line_start, base_dir, c_idx, NULL); + } #else logical_char_pos = lv_text_encoded_get_char_id(dsc->text, line_start + next_char_offset); #endif diff --git a/src/draw/lv_draw_label.h b/src/draw/lv_draw_label.h index 4ee04200d2..e3b0ed7636 100644 --- a/src/draw/lv_draw_label.h +++ b/src/draw/lv_draw_label.h @@ -60,6 +60,11 @@ typedef struct { * Indicate that the text is constant and its pointer can be safely saved e.g. in a cache. */ uint8_t text_static : 1; + + /** + * 1: already executed lv_bidi_process_paragraph. + * 0: has not been executed lv_bidi_process_paragraph.*/ + uint8_t has_bided : 1; lv_draw_label_hint_t * hint; } lv_draw_label_dsc_t; diff --git a/src/widgets/span/lv_span.c b/src/widgets/span/lv_span.c index 3e89205924..c0eb199f59 100644 --- a/src/widgets/span/lv_span.c +++ b/src/widgets/span/lv_span.c @@ -15,6 +15,8 @@ #include "../../misc/lv_assert.h" #include "../../misc/lv_text_private.h" +#include "../../misc/lv_bidi_private.h" +#include "../../misc/lv_text_ap.h" #include "../../core/lv_global.h" /********************* @@ -170,7 +172,13 @@ void lv_span_set_text(lv_span_t * span, const char * text) return; } - size_t text_alloc_len = lv_strlen(text) + 1; + size_t text_alloc_len = 0; + +#if LV_USE_ARABIC_PERSIAN_CHARS + text_alloc_len = lv_text_ap_calc_bytes_count(text); +#else + text_alloc_len = lv_strlen(text) + 1; +#endif if(span->txt == NULL || span->static_flag == 1) { span->txt = lv_malloc(text_alloc_len); @@ -184,7 +192,12 @@ void lv_span_set_text(lv_span_t * span, const char * text) if(span->txt == NULL) return; span->static_flag = 0; + +#if LV_USE_ARABIC_PERSIAN_CHARS + lv_text_ap_proc(text, span->txt); +#else lv_memcpy(span->txt, text, text_alloc_len); +#endif } void lv_spangroup_set_span_text(lv_obj_t * obj, lv_span_t * span, const char * text) @@ -204,7 +217,16 @@ void lv_span_set_text_static(lv_span_t * span, const char * text) span->txt = NULL; } span->static_flag = 1; + +#if LV_USE_ARABIC_PERSIAN_CHARS + size_t text_alloc_len = lv_text_ap_calc_bytes_count(text); + span->txt = lv_malloc(text_alloc_len); + LV_ASSERT_MALLOC(span->txt) + lv_text_ap_proc(text, span->txt); + span->static_flag = 0; +#else span->txt = (char *)text; +#endif } void lv_spangroup_set_span_text_static(lv_obj_t * obj, lv_span_t * span, const char * text) @@ -952,6 +974,28 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer) lv_span_t * cur_span = lv_ll_get_head(&spans->child_ll); const char * cur_txt = cur_span->txt; span_text_check(&cur_txt); + + lv_text_align_t align = lv_obj_get_style_text_align(obj, LV_PART_MAIN); +#if LV_USE_BIDI + lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN); + if(base_dir == LV_BASE_DIR_AUTO) { + base_dir = lv_bidi_detect_base_dir(cur_txt) == LV_BASE_DIR_RTL ? LV_BASE_DIR_RTL : LV_BASE_DIR_AUTO; + } + + while(cur_span) { + cur_span = lv_ll_get_next(&spans->child_ll, cur_span); + if(cur_span == NULL) break; + cur_txt = cur_span->txt; + span_text_check(&cur_txt); + + if(base_dir == LV_BASE_DIR_AUTO) { + base_dir = lv_bidi_detect_base_dir(cur_txt) == LV_BASE_DIR_RTL ? LV_BASE_DIR_RTL : LV_BASE_DIR_AUTO; + } + } + cur_span = lv_ll_get_head(&spans->child_ll); + cur_txt = cur_span->txt; +#endif + uint32_t cur_txt_ofs = 0; lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ lv_memzero(&snippet, sizeof(snippet)); @@ -1064,36 +1108,74 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer) } /* align deal with */ - lv_text_align_t align = lv_obj_get_style_text_align(obj, LV_PART_MAIN); +#if LV_USE_BIDI + if(base_dir == LV_BASE_DIR_AUTO) { + base_dir = LV_BASE_DIR_LTR; + } + + if(align == LV_TEXT_ALIGN_AUTO) { + if(base_dir == LV_BASE_DIR_RTL) align = LV_TEXT_ALIGN_RIGHT; + else align = LV_TEXT_ALIGN_LEFT; + } +#endif + int32_t align_ofs = 0; + int32_t txts_w = is_first_line ? indent : 0; + uint32_t i_item; + for(i_item = 0; i_item < item_cnt; i_item++) { + lv_snippet_t * pinfo = lv_get_snippet(i_item); + if(ellipsis_valid && i_item == item_cnt - 1) { + uint32_t n_ofs = 0; + lv_text_get_snippet(pinfo->txt, pinfo->font, pinfo->letter_space, max_width - txts_w, + LV_TEXT_FLAG_BREAK_ALL, &pinfo->txt_w, &n_ofs); + pinfo->bytes = n_ofs; + } + txts_w = txts_w + pinfo->txt_w; + } + txts_w -= lv_get_snippet(item_cnt - 1)->letter_space; + align_ofs = max_width > txts_w ? max_width - txts_w : 0; + if(align == LV_TEXT_ALIGN_CENTER) { + align_ofs = align_ofs >> 1; + } if(align == LV_TEXT_ALIGN_CENTER || align == LV_TEXT_ALIGN_RIGHT) { - int32_t align_ofs = 0; - int32_t txts_w = is_first_line ? indent : 0; - uint32_t i; - for(i = 0; i < item_cnt; i++) { - lv_snippet_t * pinfo = lv_get_snippet(i); - if(ellipsis_valid && i == item_cnt - 1) { - uint32_t n_ofs = 0; - lv_text_get_snippet(pinfo->txt, pinfo->font, pinfo->letter_space, max_width - txts_w, - LV_TEXT_FLAG_BREAK_ALL, &pinfo->txt_w, &n_ofs); - pinfo->bytes = n_ofs; - } - txts_w = txts_w + pinfo->txt_w; - } - txts_w -= lv_get_snippet(item_cnt - 1)->letter_space; - align_ofs = max_width > txts_w ? max_width - txts_w : 0; - if(align == LV_TEXT_ALIGN_CENTER) { - align_ofs = align_ofs >> 1; - } txt_pos.x += align_ofs; } +#if LV_USE_BIDI + int32_t first_txt_pos_x = txt_pos.x; + bool is_draw_rtl = false; + lv_snippet_t * pinfo0 = lv_get_snippet(0); + lv_base_dir_t bidi_dir = lv_bidi_detect_base_dir(pinfo0->txt); + if(bidi_dir == LV_BASE_DIR_RTL && base_dir == LV_BASE_DIR_RTL) { + is_draw_rtl = true; + if(align == LV_TEXT_ALIGN_LEFT || align == LV_TEXT_ALIGN_CENTER) { + txt_pos.x = coords.x2 - align_ofs; + } + else if(align == LV_TEXT_ALIGN_RIGHT) { + txt_pos.x = coords.x2; + } + } +#endif /* draw line letters */ uint32_t i; for(i = 0; i < item_cnt; i++) { lv_snippet_t * pinfo = lv_get_snippet(i); - /* bidi deal with:todo */ +#if LV_USE_BIDI + char * bidi_txt; + if(base_dir == LV_BASE_DIR_RTL) { + bidi_txt = lv_malloc(pinfo->bytes + 1); + lv_memcpy(bidi_txt, pinfo->txt, (size_t)pinfo->bytes); + label_draw_dsc.bidi_dir = base_dir; + label_draw_dsc.has_bided = true; + label_draw_dsc.text_local = true; + lv_bidi_process_paragraph(pinfo->txt, bidi_txt, pinfo->bytes, label_draw_dsc.bidi_dir, NULL, 0); + } + else { + bidi_txt = (char *)pinfo->txt; + } +#else const char * bidi_txt = pinfo->txt; +#endif lv_point_t pos; pos.x = txt_pos.x; @@ -1121,6 +1203,13 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer) a.x2 = a.x1 + pinfo->txt_w; a.y2 = a.y1 + pinfo->line_h; +#if LV_USE_BIDI + if(is_draw_rtl) { + a.x1 = pos.x - pinfo->txt_w; + a.x2 = pos.x; + } +#endif + bool need_draw_ellipsis = false; uint32_t dot_width = 0; /* deal overflow */ @@ -1134,18 +1223,64 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer) label_draw_dsc.flag, &pinfo->txt_w, &next_ofs); a.x2 = a.x1 + pinfo->txt_w; label_draw_dsc.text_length = next_ofs + 1; +#if LV_USE_BIDI + if(base_dir == LV_BASE_DIR_RTL) { + if(txt_bytes > label_draw_dsc.text_length) { + char * tmp_txt = lv_malloc(label_draw_dsc.text_length + 1); + + if(lv_bidi_detect_base_dir(bidi_txt) == LV_BASE_DIR_RTL) { + lv_memcpy(tmp_txt, bidi_txt + (txt_bytes - label_draw_dsc.text_length), (size_t)label_draw_dsc.text_length); + } + else { + lv_memcpy(tmp_txt, bidi_txt, (size_t)label_draw_dsc.text_length); + } + + label_draw_dsc.text = tmp_txt; + lv_free(bidi_txt); + } + if(i == 0) { + a.x1 = a.x1 + dot_width; + a.x2 = a.x2 + dot_width; + } + } +#endif } lv_draw_label(layer, &label_draw_dsc, &a); +#if LV_USE_BIDI + if(label_draw_dsc.has_bided) { + lv_free((void *)label_draw_dsc.text); + } +#endif if(need_draw_ellipsis) { label_draw_dsc.text = "..."; + +#if LV_USE_BIDI + if(label_draw_dsc.bidi_dir == LV_BASE_DIR_RTL) { + a.x1 = first_txt_pos_x; + a.x2 = a.x1 + dot_width; + } + else { + a.x1 = a.x2; + a.x2 = a.x1 + dot_width; + } + label_draw_dsc.text_local = false; +#else a.x1 = a.x2; a.x2 = a.x1 + dot_width; +#endif + lv_draw_label(layer, &label_draw_dsc, &a); } txt_pos.x = a.x2; + +#if LV_USE_BIDI + if(is_draw_rtl) { + txt_pos.x = a.x1; + } +#endif } Next_line_init: diff --git a/tests/ref_imgs/widgets/span_11.png b/tests/ref_imgs/widgets/span_11.png new file mode 100644 index 0000000000..fdceb2b003 Binary files /dev/null and b/tests/ref_imgs/widgets/span_11.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/span_11.png b/tests/ref_imgs_vg_lite/widgets/span_11.png new file mode 100644 index 0000000000..ebf6bab69e Binary files /dev/null and b/tests/ref_imgs_vg_lite/widgets/span_11.png differ diff --git a/tests/src/test_cases/widgets/test_span.c b/tests/src/test_cases/widgets/test_span.c index 652576aa5e..fefb0fbe0f 100644 --- a/tests/src/test_cases/widgets/test_span.c +++ b/tests/src/test_cases/widgets/test_span.c @@ -498,4 +498,23 @@ void test_spangroup_set_right_align_on_overflow(void) TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_10.png"); } +void test_spangroup_rtl_mode(void) +{ + const char * message = "مرحبا بكم في LVGL."; + + active_screen = lv_screen_active(); + spangroup = lv_spangroup_create(active_screen); + + lv_obj_set_style_outline_width(spangroup, 1, 0); + lv_obj_set_style_text_font(spangroup, &lv_font_dejavu_16_persian_hebrew, 0); + lv_obj_set_style_base_dir(spangroup, LV_BASE_DIR_RTL, 0); + lv_obj_set_size(spangroup, 300, lv_font_dejavu_16_persian_hebrew.line_height); + lv_spangroup_set_align(spangroup, LV_TEXT_ALIGN_LEFT); + + lv_span_t * span = lv_spangroup_new_span(spangroup); + lv_span_set_text_static(span, message); + + TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_11.png"); +} + #endif