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 == 0x202E) return true; /*Unicode of LV_BIDI_RLO*/
380
381 /*Check for Persian and Arabic characters [https://en.wikipedia.org/wiki/Arabic_script_in_Unicode]*/
382 if(letter >= 0x600 && letter <= 0x6FF) return true;
383 if(letter >= 0xFB50 && letter <= 0xFDFF) return true;
384 if(letter >= 0xFE70 && letter <= 0xFEFF) return true;
385
386 /*Check for Hebrew characters [https://en.wikipedia.org/wiki/Unicode_and_HTML_for_the_Hebrew_alphabet]*/
387 if(letter >= 0x590 && letter <= 0x5FF) return true;
388 if(letter >= 0xFB1D && letter <= 0xFB4F) return true;
389
390 return false;
391 }
392
393 /**
394 * Tell whether a character is neutral or not
395 * @param letter a Unicode character
396 * @return true/false
397 */
lv_bidi_letter_is_neutral(uint32_t letter)398 static bool lv_bidi_letter_is_neutral(uint32_t letter)
399 {
400 uint16_t i;
401 static const char neutrals[] = " \t\n\r.,:;'\"`!?%/\\-=()[]{}<>@#&$|";
402 for(i = 0; neutrals[i] != '\0'; i++) {
403 if(letter == (uint32_t)neutrals[i]) return true;
404 }
405
406 return false;
407 }
408
get_txt_len(const char * txt,uint32_t max_len)409 static uint32_t get_txt_len(const char * txt, uint32_t max_len)
410 {
411 uint32_t len = 0;
412 uint32_t i = 0;
413
414 while(i < max_len && txt[i] != '\0') {
415 _lv_txt_encoded_next(txt, &i);
416 len++;
417 }
418
419 return len;
420 }
421
fill_pos_conv(uint16_t * out,uint16_t len,uint16_t index)422 static void fill_pos_conv(uint16_t * out, uint16_t len, uint16_t index)
423 {
424 uint16_t i;
425 for(i = 0; i < len; i++) {
426 out[i] = SET_RTL_POS(index, false);
427 index++;
428 }
429 }
430
get_next_run(const char * txt,lv_base_dir_t base_dir,uint32_t max_len,uint32_t * len,uint16_t * pos_conv_len)431 static lv_base_dir_t get_next_run(const char * txt, lv_base_dir_t base_dir, uint32_t max_len, uint32_t * len,
432 uint16_t * pos_conv_len)
433 {
434 uint32_t i = 0;
435 uint32_t letter;
436
437 uint16_t pos_conv_i = 0;
438
439 letter = _lv_txt_encoded_next(txt, NULL);
440 lv_base_dir_t dir = lv_bidi_get_letter_dir(letter);
441 if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(txt, 0, max_len, letter, base_dir);
442
443 /*Find the first strong char. Skip the neutrals*/
444 while(dir == LV_BASE_DIR_NEUTRAL || dir == LV_BASE_DIR_WEAK) {
445 letter = _lv_txt_encoded_next(txt, &i);
446
447 pos_conv_i++;
448 dir = lv_bidi_get_letter_dir(letter);
449 if(dir == LV_BASE_DIR_NEUTRAL) dir = bracket_process(txt, i, max_len, letter, base_dir);
450
451 if(dir == LV_BASE_DIR_LTR || dir == LV_BASE_DIR_RTL) break;
452
453 if(i >= max_len || txt[i] == '\0' || txt[i] == '\n' || txt[i] == '\r') {
454 *len = i;
455 *pos_conv_len = pos_conv_i;
456 return base_dir;
457 }
458 }
459
460 lv_base_dir_t run_dir = dir;
461
462 uint32_t i_prev = i;
463 uint32_t i_last_strong = i;
464 uint16_t pos_conv_i_prev = pos_conv_i;
465 uint16_t pos_conv_i_last_strong = pos_conv_i;
466
467 /*Find the next char which has different direction*/
468 lv_base_dir_t next_dir = base_dir;
469 while(i_prev < max_len && txt[i] != '\0' && txt[i] != '\n' && txt[i] != '\r') {
470 letter = _lv_txt_encoded_next(txt, &i);
471 pos_conv_i++;
472 next_dir = lv_bidi_get_letter_dir(letter);
473 if(next_dir == LV_BASE_DIR_NEUTRAL) next_dir = bracket_process(txt, i, max_len, letter, base_dir);
474
475 if(next_dir == LV_BASE_DIR_WEAK) {
476 if(run_dir == LV_BASE_DIR_RTL) {
477 if(base_dir == LV_BASE_DIR_RTL) {
478 next_dir = LV_BASE_DIR_LTR;
479 }
480 }
481 }
482
483 /*New dir found?*/
484 if((next_dir == LV_BASE_DIR_RTL || next_dir == LV_BASE_DIR_LTR) && next_dir != run_dir) {
485 /*Include neutrals if `run_dir == base_dir`*/
486 if(run_dir == base_dir) {
487 *len = i_prev;
488 *pos_conv_len = pos_conv_i_prev;
489 }
490 /*Exclude neutrals if `run_dir != base_dir`*/
491 else {
492 *len = i_last_strong;
493 *pos_conv_len = pos_conv_i_last_strong;
494 }
495
496 return run_dir;
497 }
498
499 if(next_dir != LV_BASE_DIR_NEUTRAL) {
500 i_last_strong = i;
501 pos_conv_i_last_strong = pos_conv_i;
502 }
503
504 i_prev = i;
505 pos_conv_i_prev = pos_conv_i;
506 }
507
508 /*Handle end of of string. Apply `base_dir` on trailing neutrals*/
509
510 /*Include neutrals if `run_dir == base_dir`*/
511 if(run_dir == base_dir) {
512 *len = i_prev;
513 *pos_conv_len = pos_conv_i_prev;
514 }
515 /*Exclude neutrals if `run_dir != base_dir`*/
516 else {
517 *len = i_last_strong;
518 *pos_conv_len = pos_conv_i_last_strong;
519 }
520
521 return run_dir;
522 }
523
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)524 static void rtl_reverse(char * dest, const char * src, uint32_t len, uint16_t * pos_conv_out, uint16_t pos_conv_rd_base,
525 uint16_t pos_conv_len)
526 {
527 uint32_t i = len;
528 uint32_t wr = 0;
529 uint16_t pos_conv_i = pos_conv_len;
530 uint16_t pos_conv_wr = 0;
531
532 while(i) {
533 uint32_t letter = _lv_txt_encoded_prev(src, &i);
534 uint16_t pos_conv_letter = --pos_conv_i;
535
536 /*Keep weak letters (numbers) as LTR*/
537 if(lv_bidi_letter_is_weak(letter)) {
538 uint32_t last_weak = i;
539 uint32_t first_weak = i;
540 uint16_t pos_conv_last_weak = pos_conv_i;
541 uint16_t pos_conv_first_weak = pos_conv_i;
542 while(i) {
543 letter = _lv_txt_encoded_prev(src, &i);
544 pos_conv_letter = --pos_conv_i;
545
546 /*No need to call `char_change_to_pair` because there not such chars here*/
547
548 /*Finish on non-weak char*/
549 /*but treat number and currency related chars as weak*/
550 if(lv_bidi_letter_is_weak(letter) == false && letter != '.' && letter != ',' && letter != '$' && letter != '%') {
551 _lv_txt_encoded_next(src, &i); /*Rewind one letter*/
552 pos_conv_i++;
553 first_weak = i;
554 pos_conv_first_weak = pos_conv_i;
555 break;
556 }
557 }
558 if(i == 0) {
559 first_weak = 0;
560 pos_conv_first_weak = 0;
561 }
562
563 if(dest) lv_memcpy(&dest[wr], &src[first_weak], last_weak - first_weak + 1);
564 if(pos_conv_out) fill_pos_conv(&pos_conv_out[pos_conv_wr], pos_conv_last_weak - pos_conv_first_weak + 1,
565 pos_conv_rd_base + pos_conv_first_weak);
566 wr += last_weak - first_weak + 1;
567 pos_conv_wr += pos_conv_last_weak - pos_conv_first_weak + 1;
568 }
569
570 /*Simply store in reversed order*/
571 else {
572 uint32_t letter_size = _lv_txt_encoded_size((const char *)&src[i]);
573 /*Swap arithmetical symbols*/
574 if(letter_size == 1) {
575 uint32_t new_letter = letter = char_change_to_pair(letter);
576 if(dest) dest[wr] = (uint8_t)new_letter;
577 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_letter, true);
578 wr++;
579 pos_conv_wr++;
580 }
581 /*Just store the letter*/
582 else {
583 if(dest) lv_memcpy(&dest[wr], &src[i], letter_size);
584 if(pos_conv_out) pos_conv_out[pos_conv_wr] = SET_RTL_POS(pos_conv_rd_base + pos_conv_i, true);
585 wr += letter_size;
586 pos_conv_wr++;
587 }
588 }
589 }
590 }
591
char_change_to_pair(uint32_t letter)592 static uint32_t char_change_to_pair(uint32_t letter)
593 {
594
595 uint8_t i;
596 for(i = 0; bracket_left[i] != '\0'; i++) {
597 if(letter == bracket_left[i]) return bracket_right[i];
598 }
599
600 for(i = 0; bracket_right[i] != '\0'; i++) {
601 if(letter == bracket_right[i]) return bracket_left[i];
602 }
603
604 return letter;
605 }
606
bracket_process(const char * txt,uint32_t next_pos,uint32_t len,uint32_t letter,lv_base_dir_t base_dir)607 static lv_base_dir_t bracket_process(const char * txt, uint32_t next_pos, uint32_t len, uint32_t letter,
608 lv_base_dir_t base_dir)
609 {
610 lv_base_dir_t bracket_dir = LV_BASE_DIR_NEUTRAL;
611
612 uint8_t i;
613 /*Is the letter an opening bracket?*/
614 for(i = 0; bracket_left[i] != '\0'; i++) {
615 if(bracket_left[i] == letter) {
616 /*If so find its matching closing bracket.
617 *If a char with base dir. direction is found then the brackets will have `base_dir` direction*/
618 uint32_t txt_i = next_pos;
619 while(txt_i < len) {
620 uint32_t letter_next = _lv_txt_encoded_next(txt, &txt_i);
621 if(letter_next == bracket_right[i]) {
622 /*Closing bracket found*/
623 break;
624 }
625 else {
626 /*Save the dir*/
627 lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
628 if(letter_dir == base_dir) {
629 bracket_dir = base_dir;
630 }
631 }
632 }
633
634 /*There were no matching closing bracket*/
635 if(txt_i > len) return LV_BASE_DIR_NEUTRAL;
636
637 /*There where a strong char with base dir in the bracket so the dir is found.*/
638 if(bracket_dir != LV_BASE_DIR_NEUTRAL && bracket_dir != LV_BASE_DIR_WEAK) break;
639
640 /*If there were no matching strong chars in the brackets then check the previous chars*/
641 txt_i = next_pos;
642 if(txt_i) _lv_txt_encoded_prev(txt, &txt_i);
643 while(txt_i > 0) {
644 uint32_t letter_next = _lv_txt_encoded_prev(txt, &txt_i);
645 lv_base_dir_t letter_dir = lv_bidi_get_letter_dir(letter_next);
646 if(letter_dir == LV_BASE_DIR_LTR || letter_dir == LV_BASE_DIR_RTL) {
647 bracket_dir = letter_dir;
648 break;
649 }
650 }
651
652 /*There where a previous strong char which can be used*/
653 if(bracket_dir != LV_BASE_DIR_NEUTRAL) break;
654
655 /*There were no strong chars before the bracket, so use the base dir.*/
656 if(txt_i == 0) bracket_dir = base_dir;
657
658 break;
659 }
660 }
661
662 /*The letter was an opening bracket*/
663 if(bracket_left[i] != '\0') {
664
665 if(bracket_dir == LV_BASE_DIR_NEUTRAL || br_stack_p == LV_BIDI_BRACKLET_DEPTH) return LV_BASE_DIR_NEUTRAL;
666
667 br_stack[br_stack_p].bracklet_pos = i;
668 br_stack[br_stack_p].dir = bracket_dir;
669
670 br_stack_p++;
671 return bracket_dir;
672 }
673 else if(br_stack_p > 0) {
674 /*Is the letter a closing bracket of the last opening?*/
675 if(letter == bracket_right[br_stack[br_stack_p - 1].bracklet_pos]) {
676 bracket_dir = br_stack[br_stack_p - 1].dir;
677 br_stack_p--;
678 return bracket_dir;
679 }
680 }
681
682 return LV_BASE_DIR_NEUTRAL;
683 }
684
685 #endif /*LV_USE_BIDI*/
686