1 /**
2 * @file lv_bidi.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_bidi_private.h"
10 #include "lv_text_private.h"
11 #include "lv_types.h"
12 #include "../stdlib/lv_mem.h"
13 #include "../stdlib/lv_string.h"
14
15 #if LV_USE_BIDI
16
17 /*********************
18 * DEFINES
19 *********************/
20 #define LV_BIDI_BRACKET_DEPTH 4
21
22 // Highest bit of the 16-bit pos_conv value specifies whether this pos is RTL or not
23 #define GET_POS(x) ((x) & 0x7FFF)
24 #define IS_RTL_POS(x) (((x) & 0x8000) != 0)
25 #define SET_RTL_POS(x, is_rtl) (GET_POS(x) | ((is_rtl)? 0x8000: 0))
26
27 /**********************
28 * TYPEDEFS
29 **********************/
30 typedef struct {
31 uint32_t bracket_pos;
32 lv_base_dir_t dir;
33 } bracket_stack_t;
34
35 typedef struct {
36 bracket_stack_t br_stack[LV_BIDI_BRACKET_DEPTH];
37 uint8_t br_stack_p;
38 } lv_bidi_ctx_t;
39
40 /**********************
41 * STATIC PROTOTYPES
42 **********************/
43
44 static uint32_t lv_bidi_get_next_paragraph(const char * txt);
45 static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter);
46 static bool lv_bidi_letter_is_weak(uint32_t letter);
47 static bool lv_bidi_letter_is_rtl(uint32_t letter);
48 static bool lv_bidi_letter_is_neutral(uint32_t letter);
49
50 static lv_base_dir_t get_next_run(lv_bidi_ctx_t * ctx, const char * txt, lv_base_dir_t base_dir, uint32_t max_len,
51 uint32_t * len,
52 uint16_t * pos_conv_len);
53 static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
54 uint16_t pos_conv_len);
55 static uint32_t char_change_to_pair(uint32_t letter);
56 static lv_base_dir_t bracket_process(lv_bidi_ctx_t * ctx, const char * txt, uint32_t next_pos, uint32_t len,
57 uint32_t letter,
58 lv_base_dir_t base_dir);
59 static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index);
60 static uint32_t get_txt_len(const char * txt, uint32_t max_len);
61
62 /**********************
63 * STATIC VARIABLES
64 **********************/
65 static const uint8_t bracket_left[] = {"<({["};
66 static const uint8_t bracket_right[] = {">)}]"};
67 static const char * custom_neutrals = NULL;
68
69 /**********************
70 * MACROS
71 **********************/
72
73 /**********************
74 * GLOBAL FUNCTIONS
75 **********************/
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_text_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
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)125 uint16_t lv_bidi_get_logical_pos(const char * str_in, char ** bidi_txt, uint32_t len, lv_base_dir_t base_dir,
126 uint32_t visual_pos, bool * is_rtl)
127 {
128 uint32_t pos_conv_len = get_txt_len(str_in, len);
129 char * buf = lv_malloc(len + 1);
130 if(buf == NULL) return (uint16_t) -1;
131
132 uint16_t * pos_conv_buf = lv_malloc(pos_conv_len * sizeof(uint16_t));
133 if(pos_conv_buf == NULL) {
134 lv_free(buf);
135 return (uint16_t) -1;
136 }
137
138 if(bidi_txt) *bidi_txt = buf;
139
140 lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);
141
142 if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[visual_pos]);
143
144 if(bidi_txt == NULL) lv_free(buf);
145 uint16_t res = GET_POS(pos_conv_buf[visual_pos]);
146 lv_free(pos_conv_buf);
147 return res;
148 }
149
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)150 uint16_t lv_bidi_get_visual_pos(const char * str_in, char ** bidi_txt, uint16_t len, lv_base_dir_t base_dir,
151 uint32_t logical_pos, bool * is_rtl)
152 {
153 uint32_t pos_conv_len = get_txt_len(str_in, len);
154 char * buf = lv_malloc(len + 1);
155 if(buf == NULL) return (uint16_t) -1;
156
157 uint16_t * pos_conv_buf = lv_malloc(pos_conv_len * sizeof(uint16_t));
158 if(pos_conv_buf == NULL) {
159 lv_free(buf);
160 return (uint16_t) -1;
161 }
162
163 if(bidi_txt) *bidi_txt = buf;
164
165 lv_bidi_process_paragraph(str_in, bidi_txt ? *bidi_txt : NULL, len, base_dir, pos_conv_buf, pos_conv_len);
166
167 for(uint16_t i = 0; i < pos_conv_len; i++) {
168 if(GET_POS(pos_conv_buf[i]) == logical_pos) {
169
170 if(is_rtl) *is_rtl = IS_RTL_POS(pos_conv_buf[i]);
171 lv_free(pos_conv_buf);
172
173 if(bidi_txt == NULL) lv_free(buf);
174 return i;
175 }
176 }
177 lv_free(pos_conv_buf);
178 if(bidi_txt == NULL) lv_free(buf);
179 return (uint16_t) -1;
180 }
181
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)182 void lv_bidi_process_paragraph(const char * str_in, char * str_out, uint32_t len, lv_base_dir_t base_dir,
183 uint16_t * pos_conv_out, uint16_t pos_conv_len)
184 {
185 uint32_t run_len = 0;
186 lv_base_dir_t run_dir;
187 uint32_t rd = 0;
188 uint32_t wr;
189 uint16_t pos_conv_run_len = 0;
190 uint16_t pos_conv_rd = 0;
191 uint16_t pos_conv_wr;
192
193 if(base_dir == LV_BASE_DIR_AUTO) base_dir = lv_bidi_detect_base_dir(str_in);
194 if(base_dir == LV_BASE_DIR_RTL) {
195 wr = len;
196 pos_conv_wr = pos_conv_len;
197 }
198 else {
199 wr = 0;
200 pos_conv_wr = 0;
201 }
202
203 if(str_out) str_out[len] = '\0';
204
205 lv_base_dir_t dir = base_dir;
206
207 /*Empty the bracket stack*/
208 lv_bidi_ctx_t ctx;
209 lv_memzero(&ctx, sizeof(ctx));
210
211 /*Process neutral chars in the beginning*/
212 while(rd < len) {
213 uint32_t letter = lv_text_encoded_next(str_in, &rd);
214 pos_conv_rd++;
215 dir = lv_bidi_get_letter_dir(letter);
216 if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(&ctx, str_in, rd, len, letter, base_dir);
217 else if(dir != LV_BASE_DIR_WEAK) break;
218 }
219
220 if(rd && str_in[rd] != '\0' && rd < len) {
221 lv_text_encoded_prev(str_in, &rd);
222 pos_conv_rd--;
223 }
224
225 if(rd) {
226 if(base_dir == LV_BASE_DIR_LTR) {
227 if(str_out) {
228 lv_memcpy(&str_out[wr], str_in, rd);
229 wr += rd;
230 }
231 if(pos_conv_out) {
232 fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_rd, 0);
233 pos_conv_wr += pos_conv_rd;
234 }
235 }
236 else {
237 wr -= rd;
238 pos_conv_wr -= pos_conv_rd;
239 rtl_reverse(str_out ? &str_out[wr] : NULL, str_in, rd, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL, 0,
240 pos_conv_rd);
241 }
242 }
243
244 /*Get and process the runs*/
245
246 while(rd < len && str_in[rd]) {
247 run_dir = get_next_run(&ctx, &str_in[rd], base_dir, len - rd, &run_len, &pos_conv_run_len);
248
249 if(base_dir == LV_BASE_DIR_LTR) {
250 if(run_dir == LV_BASE_DIR_LTR) {
251 if(str_out) lv_memcpy(&str_out[wr], &str_in[rd], run_len);
252 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
253 }
254 else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
255 pos_conv_rd, pos_conv_run_len);
256 wr += run_len;
257 pos_conv_wr += pos_conv_run_len;
258 }
259 else {
260 wr -= run_len;
261 pos_conv_wr -= pos_conv_run_len;
262 if(run_dir == LV_BASE_DIR_LTR) {
263 if(str_out) lv_memcpy(&str_out[wr], &str_in[rd], run_len);
264 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_run_len, pos_conv_rd);
265 }
266 else rtl_reverse(str_out ? &str_out[wr] : NULL, &str_in[rd], run_len, pos_conv_out ? &pos_conv_out[pos_conv_wr] : NULL,
267 pos_conv_rd, pos_conv_run_len);
268 }
269
270 rd += run_len;
271 pos_conv_rd += pos_conv_run_len;
272 }
273 }
274
lv_bidi_calculate_align(lv_text_align_t * align,lv_base_dir_t * base_dir,const char * txt)275 void lv_bidi_calculate_align(lv_text_align_t * align, lv_base_dir_t * base_dir, const char * txt)
276 {
277 if(*base_dir == LV_BASE_DIR_AUTO) *base_dir = lv_bidi_detect_base_dir(txt);
278
279 if(*align == LV_TEXT_ALIGN_AUTO) {
280 if(*base_dir == LV_BASE_DIR_RTL) *align = LV_TEXT_ALIGN_RIGHT;
281 else *align = LV_TEXT_ALIGN_LEFT;
282 }
283 }
284
lv_bidi_set_custom_neutrals_static(const char * neutrals)285 void lv_bidi_set_custom_neutrals_static(const char * neutrals)
286 {
287 custom_neutrals = neutrals;
288 }
289
290 /**********************
291 * STATIC FUNCTIONS
292 **********************/
293
294 /**
295 * Get the next paragraph from a text
296 * @param txt the text to process
297 * @return the length of the current paragraph in byte count
298 */
lv_bidi_get_next_paragraph(const char * txt)299 static uint32_t lv_bidi_get_next_paragraph(const char * txt)
300 {
301 uint32_t i = 0;
302
303 lv_text_encoded_next(txt, &i);
304
305 while(txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
306 lv_text_encoded_next(txt, &i);
307 }
308
309 return i;
310 }
311
312 /**
313 * Get the direction of a character
314 * @param letter a Unicode character
315 * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL`
316 */
lv_bidi_get_letter_dir(uint32_t letter)317 static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter)
318 {
319 if(lv_bidi_letter_is_rtl(letter)) return LV_BASE_DIR_RTL;
320 if(lv_bidi_letter_is_neutral(letter)) return LV_BASE_DIR_NEUTRAL;
321 if(lv_bidi_letter_is_weak(letter)) return LV_BASE_DIR_WEAK;
322
323 return LV_BASE_DIR_LTR;
324 }
325 /**
326 * Tell whether a character is weak or not
327 * @param letter a Unicode character
328 * @return true/false
329 */
lv_bidi_letter_is_weak(uint32_t letter)330 static bool lv_bidi_letter_is_weak(uint32_t letter)
331 {
332 uint32_t i = 0;
333 static const char weaks[] = "0123456789";
334
335 do {
336 uint32_t x = lv_text_encoded_next(weaks, &i);
337 if(letter == x) {
338 return true;
339 }
340 } while(weaks[i] != '\0');
341
342 return false;
343 }
344 /**
345 * Tell whether a character is RTL or not
346 * @param letter a Unicode character
347 * @return true/false
348 */
lv_bidi_letter_is_rtl(uint32_t letter)349 static bool lv_bidi_letter_is_rtl(uint32_t letter)
350 {
351 if(letter == 0x202E) return true; /*Unicode of LV_BIDI_RLO*/
352
353 /*Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/
354 if(letter >= 0x600 && letter <= 0x6FF) return true;
355 if(letter >= 0xFB50 && letter <= 0xFDFF) return true;
356 if(letter >= 0xFE70 && letter <= 0xFEFF) return true;
357
358 /*Check for Hebrew characters [https://en.wikipedia.org/wiki/Unicode_and_HTML_for_the_Hebrew_alphabet]*/
359 if(letter >= 0x590 && letter <= 0x5FF) return true;
360 if(letter >= 0xFB1D && letter <= 0xFB4F) return true;
361
362 return false;
363 }
364
365 /**
366 * Tell whether a character is neutral or not
367 * @param letter a Unicode character
368 * @return true/false
369 */
lv_bidi_letter_is_neutral(uint32_t letter)370 static bool lv_bidi_letter_is_neutral(uint32_t letter)
371 {
372 uint16_t i;
373 const char * neutrals = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
374 if(custom_neutrals) {
375 neutrals = custom_neutrals;
376 }
377
378 for(i = 0; neutrals[i] != '\0'; i++) {
379 if(letter == (uint32_t)neutrals[i]) return true;
380 }
381
382 return false;
383 }
384
get_txt_len(const char * txt,uint32_t max_len)385 static uint32_t get_txt_len(const char * txt, uint32_t max_len)
386 {
387 uint32_t len = 0;
388 uint32_t i = 0;
389
390 while(i < max_len && txt[i] != '\0') {
391 lv_text_encoded_next(txt, &i);
392 len++;
393 }
394
395 return len;
396 }
397
fill_pos_conv(uint16_t * out,uint16_t len,uint16_t index)398 static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index)
399 {
400 uint16_t i;
401 for(i = 0; i < len; i++) {
402 out[i] = SET_RTL_POS(index, false);
403 index++;
404 }
405 }
406
get_next_run(lv_bidi_ctx_t * ctx,const char * txt,lv_base_dir_t base_dir,uint32_t max_len,uint32_t * len,uint16_t * pos_conv_len)407 static lv_base_dir_t get_next_run(lv_bidi_ctx_t * ctx, const char * txt, lv_base_dir_t base_dir, uint32_t max_len,
408 uint32_t * len,
409 uint16_t * pos_conv_len)
410 {
411 uint32_t i = 0;
412 uint32_t letter;
413
414 uint16_t pos_conv_i = 0;
415
416 letter = lv_text_encoded_next(txt, NULL);
417 lv_base_dir_t dir = lv_bidi_get_letter_dir(letter);
418 if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(ctx, txt, 0, max_len, letter, base_dir);
419
420 /*Find the first strong char. Skip the neutrals*/
421 while(dir == LV_BASE_DIR_NEUTRAL || dir == LV_BASE_DIR_WEAK) {
422 letter = lv_text_encoded_next(txt, &i);
423
424 pos_conv_i++;
425 dir = lv_bidi_get_letter_dir(letter);
426 if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(ctx, txt, i, max_len, letter, base_dir);
427
428 if(dir == LV_BASE_DIR_LTR || dir == LV_BASE_DIR_RTL) break;
429
430 if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') {
431 *len = i;
432 *pos_conv_len = pos_conv_i;
433 return base_dir;
434 }
435 }
436
437 lv_base_dir_t run_dir = dir;
438
439 uint32_t i_prev = i;
440 uint32_t i_last_strong = i;
441 uint16_t pos_conv_i_prev = pos_conv_i;
442 uint16_t pos_conv_i_last_strong = pos_conv_i;
443
444 /*Find the next char which has different direction*/
445 lv_base_dir_t next_dir = base_dir;
446 while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
447 letter = lv_text_encoded_next(txt, &i);
448 pos_conv_i++;
449 next_dir = lv_bidi_get_letter_dir(letter);
450 if(next_dir == LV_BASE_DIR_NEUTRAL) next_dir = bracket_process(ctx, txt, i, max_len, letter, base_dir);
451
452 if(next_dir == LV_BASE_DIR_WEAK) {
453 if(run_dir == LV_BASE_DIR_RTL) {
454 if(base_dir == LV_BASE_DIR_RTL) {
455 next_dir = LV_BASE_DIR_LTR;
456 }
457 }
458 }
459
460 /*New dir found?*/
461 if((next_dir == LV_BASE_DIR_RTL || next_dir == LV_BASE_DIR_LTR) && next_dir != run_dir) {
462 /*Include neutrals if `run_dir == base_dir`*/
463 if(run_dir == base_dir) {
464 *len = i_prev;
465 *pos_conv_len = pos_conv_i_prev;
466 }
467 /*Exclude neutrals if `run_dir != base_dir`*/
468 else {
469 *len = i_last_strong;
470 *pos_conv_len = pos_conv_i_last_strong;
471 }
472
473 return run_dir;
474 }
475
476 if(next_dir != LV_BASE_DIR_NEUTRAL) {
477 i_last_strong = i;
478 pos_conv_i_last_strong = pos_conv_i;
479 }
480
481 i_prev = i;
482 pos_conv_i_prev = pos_conv_i;
483 }
484
485 /*Handle end of of string. Apply `base_dir` on trailing neutrals*/
486
487 /*Include neutrals if `run_dir == base_dir`*/
488 if(run_dir == base_dir) {
489 *len = i_prev;
490 *pos_conv_len = pos_conv_i_prev;
491 }
492 /*Exclude neutrals if `run_dir != base_dir`*/
493 else {
494 *len = i_last_strong;
495 *pos_conv_len = pos_conv_i_last_strong;
496 }
497
498 return run_dir;
499 }
500
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)501 static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
502 uint16_t pos_conv_len)
503 {
504 uint32_t i = len;
505 uint32_t wr = 0;
506 uint16_t pos_conv_i = pos_conv_len;
507 uint16_t pos_conv_wr = 0;
508
509 while(i) {
510 uint32_t letter = lv_text_encoded_prev(src, &i);
511 uint16_t pos_conv_letter = --pos_conv_i;
512
513 /*Keep weak letters (numbers) as LTR*/
514 if(lv_bidi_letter_is_weak(letter)) {
515 uint32_t last_weak = i;
516 uint32_t first_weak = i;
517 uint16_t pos_conv_last_weak = pos_conv_i;
518 uint16_t pos_conv_first_weak = pos_conv_i;
519 while(i) {
520 letter = lv_text_encoded_prev(src, &i);
521 pos_conv_letter = --pos_conv_i;
522
523 /*No need to call `char_change_to_pair` because there not such chars here*/
524
525 /*Finish on non-weak char*/
526 /*but treat number and currency related chars as weak*/
527 if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') {
528 lv_text_encoded_next(src, &i); /*Rewind one letter*/
529 pos_conv_i++;
530 first_weak = i;
531 pos_conv_first_weak = pos_conv_i;
532 break;
533 }
534 }
535 if(i == 0) {
536 first_weak = 0;
537 pos_conv_first_weak = 0;
538 }
539
540 if(dest) lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1);
541 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1,
542 pos_conv_rd_base + pos_conv_first_weak);
543 wr += last_weak - first_weak + 1;
544 pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1;
545 }
546
547 /*Simply store in reversed order*/
548 else {
549 uint32_t letter_size = lv_text_encoded_size((const char *)&src[i]);
550 /*Swap arithmetical symbols*/
551 if(letter_size == 1) {
552 uint32_t new_letter = letter = char_change_to_pair(letter);
553 if(dest) dest[wr] = (uint8_t)new_letter;
554 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true);
555 wr++;
556 pos_conv_wr++;
557 }
558 /*Just store the letter*/
559 else {
560 if(dest) lv_memcpy(&dest[wr], &src[i], letter_size);
561 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true);
562 wr += letter_size;
563 pos_conv_wr++;
564 }
565 }
566 }
567 }
568
char_change_to_pair(uint32_t letter)569 static uint32_t char_change_to_pair(uint32_t letter)
570 {
571
572 uint8_t i;
573 for(i = 0; bracket_left[i] != '\0'; i++) {
574 if(letter == bracket_left[i]) return bracket_right[i];
575 }
576
577 for(i = 0; bracket_right[i] != '\0'; i++) {
578 if(letter == bracket_right[i]) return bracket_left[i];
579 }
580
581 return letter;
582 }
583
bracket_process(lv_bidi_ctx_t * ctx,const char * txt,uint32_t next_pos,uint32_t len,uint32_t letter,lv_base_dir_t base_dir)584 static lv_base_dir_t bracket_process(lv_bidi_ctx_t * ctx, const char * txt, uint32_t next_pos, uint32_t len,
585 uint32_t letter,
586 lv_base_dir_t base_dir)
587 {
588 lv_base_dir_t bracket_dir = LV_BASE_DIR_NEUTRAL;
589
590 uint8_t i;
591 /*Is the letter an opening bracket?*/
592 for(i = 0; bracket_left[i] != '\0'; i++) {
593 if(bracket_left[i] == letter) {
594 /*If so find its matching closing bracket.
595 *If a char with base dir. direction is found then the brackets will have `base_dir` direction*/
596 uint32_t txt_i = next_pos;
597 while(txt_i < len) {
598 uint32_t letter_next = lv_text_encoded_next(txt, &txt_i);
599 if(letter_next == bracket_right[i]) {
600 /*Closing bracket found*/
601 break;
602 }
603 else {
604 /*Save the dir*/
605 lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
606 if(letter_dir == base_dir) {
607 bracket_dir = base_dir;
608 }
609 }
610 }
611
612 /*There were no matching closing bracket*/
613 if(txt_i > len) return LV_BASE_DIR_NEUTRAL;
614
615 /*There where a strong char with base dir in the bracket so the dir is found.*/
616 if(bracket_dir != LV_BASE_DIR_NEUTRAL && bracket_dir != LV_BASE_DIR_WEAK) break;
617
618 /*If there were no matching strong chars in the brackets then check the previous chars*/
619 txt_i = next_pos;
620 if(txt_i) lv_text_encoded_prev(txt, &txt_i);
621 while(txt_i > 0) {
622 uint32_t letter_next = lv_text_encoded_prev(txt, &txt_i);
623 lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
624 if(letter_dir == LV_BASE_DIR_LTR || letter_dir == LV_BASE_DIR_RTL) {
625 bracket_dir = letter_dir;
626 break;
627 }
628 }
629
630 /*There where a previous strong char which can be used*/
631 if(bracket_dir != LV_BASE_DIR_NEUTRAL) break;
632
633 /*There were no strong chars before the bracket, so use the base dir.*/
634 if(txt_i == 0) bracket_dir = base_dir;
635
636 break;
637 }
638 }
639
640 /*The letter was an opening bracket*/
641 if(bracket_left[i] != '\0') {
642
643 if(bracket_dir == LV_BASE_DIR_NEUTRAL || ctx->br_stack_p == LV_BIDI_BRACKET_DEPTH) return LV_BASE_DIR_NEUTRAL;
644
645 ctx->br_stack[ctx->br_stack_p].bracket_pos = i;
646 ctx->br_stack[ctx->br_stack_p].dir = bracket_dir;
647
648 ctx->br_stack_p++;
649 return bracket_dir;
650 }
651 else if(ctx->br_stack_p > 0) {
652 /*Is the letter a closing bracket of the last opening?*/
653 if(letter == bracket_right[ctx->br_stack[ctx->br_stack_p - 1].bracket_pos]) {
654 bracket_dir = ctx->br_stack[ctx->br_stack_p - 1].dir;
655 ctx->br_stack_p--;
656 return bracket_dir;
657 }
658 }
659
660 return LV_BASE_DIR_NEUTRAL;
661 }
662
663 #endif /*LV_USE_BIDI*/
664