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 a 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 a 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 a 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 a 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