1 /**
2 * @file lv_txt_ap.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include <stddef.h>
10 #include "lv_bidi.h"
11 #include "lv_txt.h"
12 #include "lv_txt_ap.h"
13 #include "lv_mem.h"
14 #include "../draw/lv_draw.h"
15
16 /*********************
17 * DEFINES
18 *********************/
19
20 /**********************
21 * TYPEDEFS
22 **********************/
23 typedef struct {
24 uint8_t char_offset;
25 uint16_t char_end_form;
26 int8_t char_begining_form_offset;
27 int8_t char_middle_form_offset;
28 int8_t char_isolated_form_offset;
29 struct {
30 uint8_t conj_to_previous;
31 uint8_t conj_to_next;
32 } ap_chars_conjunction;
33 } ap_chars_map_t;
34
35 /**********************
36 * STATIC PROTOTYPES
37 **********************/
38 #if LV_USE_ARABIC_PERSIAN_CHARS == 1
39 static uint32_t lv_ap_get_char_index(uint16_t c);
40 static uint32_t lv_txt_lam_alef(uint32_t ch_curr, uint32_t ch_next);
41 static bool lv_txt_is_arabic_vowel(uint16_t c);
42
43 /**********************
44 * STATIC VARIABLES
45 **********************/
46
47 const ap_chars_map_t ap_chars_map[] = {
48 /*{Key Offset, End, Beginning, Middle, Isolated, {conjunction}}*/
49 {1, 0xFE84, -1, 0, -1, {1, 0}}, // أ
50 {2, 0xFE86, -1, 0, -1, {1, 0}}, // ؤ
51 {3, 0xFE88, -1, 0, -1, {1, 0}}, // ﺇ
52 {4, 0xFE8A, 1, 2, -1, {1, 0}}, // ئ
53 {5, 0xFE8E, -1, 0, -1, {1, 0}}, // آ
54 {6, 0xFE90, 1, 2, -1, {1, 1}}, // ب
55 {92, 0xFB57, 1, 2, -1, {1, 1}}, // پ
56 {8, 0xFE96, 1, 2, -1, {1, 1}}, // ت
57 {9, 0xFE9A, 1, 2, -1, {1, 1}}, // ث
58 {10, 0xFE9E, 1, 2, -1, {1, 1}}, // ج
59 {100, 0xFB7B, 1, 2, -1, {1, 1}}, // چ
60 {11, 0xFEA2, 1, 2, -1, {1, 1}}, // ح
61 {12, 0xFEA6, 1, 2, -1, {1, 1}}, // خ
62 {13, 0xFEAA, -1, 0, -1, {1, 0}}, // د
63 {14, 0xFEAC, -1, 0, -1, {1, 0}}, // ذ
64 {15, 0xFEAE, -1, 0, -1, {1, 0}}, // ر
65 {16, 0xFEB0, -1, 0, -1, {1, 0}}, // ز
66 {118, 0xFB8B, -1, 0, -1, {1, 0}}, // ژ
67 {17, 0xFEB2, 1, 2, -1, {1, 1}}, // س
68 {18, 0xFEB6, 1, 2, -1, {1, 1}}, // ش
69 {19, 0xFEBA, 1, 2, -1, {1, 1}}, // ص
70 {20, 0xFEBE, 1, 2, -1, {1, 1}}, // ض
71 {21, 0xFEC2, 1, 2, -1, {1, 1}}, // ط
72 {22, 0xFEC6, 1, 2, -1, {1, 1}}, // ظ
73 {23, 0xFECA, 1, 2, -1, {1, 1}}, // ع
74 {24, 0xFECE, 1, 2, -1, {1, 1}}, // غ
75 {30, 0x0640, 0, 0, 0, {1, 1}}, // - (mad, hyphen)
76 {31, 0xFED2, 1, 2, -1, {1, 1}}, // ف
77 {32, 0xFED6, 1, 2, -1, {1, 1}}, // ق
78 {135, 0xFB8F, 1, 2, -1, {1, 1}}, // ک
79 {33, 0xFEDA, 1, 2, -1, {1, 1}}, // ﻙ
80 {141, 0xFB93, 1, 2, -1, {1, 1}}, // گ
81 {34, 0xFEDE, 1, 2, -1, {1, 1}}, // ل
82 {35, 0xFEE2, 1, 2, -1, {1, 1}}, // م
83 {36, 0xFEE6, 1, 2, -1, {1, 1}}, // ن
84 {38, 0xFEEE, -1, 0, -1, {1, 0}}, // و
85 {37, 0xFEEA, 1, 2, -1, {1, 1}}, // ه
86 {39, 0xFEF0, 0, 0, -1, {1, 0}}, // ى
87 {40, 0xFEF2, 1, 2, -1, {1, 1}}, // ي
88 {170, 0xFBFD, 1, 2, -1, {1, 1}}, // ی
89 {7, 0xFE94, 1, 2, -1, {1, 0}}, // ة
90 {206, 0x06F0, 1, 2, -1, {0, 0}}, // ۰
91 {207, 0x06F1, 0, 0, 0, {0, 0}}, // ۱
92 {208, 0x06F2, 0, 0, 0, {0, 0}}, // ۲
93 {209, 0x06F3, 0, 0, 0, {0, 0}}, // ۳
94 {210, 0x06F4, 0, 0, 0, {0, 0}}, // ۴
95 {211, 0x06F5, 0, 0, 0, {0, 0}}, // ۵
96 {212, 0x06F6, 0, 0, 0, {0, 0}}, // ۶
97 {213, 0x06F7, 0, 0, 0, {0, 0}}, // ۷
98 {214, 0x06F8, 0, 0, 0, {0, 0}}, // ۸
99 {215, 0x06F9, 0, 0, 0, {0, 0}}, // ۹
100 LV_AP_END_CHARS_LIST
101 };
102 /**********************
103 * MACROS
104 **********************/
105
106 /**********************
107 * GLOBAL FUNCTIONS
108 **********************/
_lv_txt_ap_calc_bytes_cnt(const char * txt)109 uint32_t _lv_txt_ap_calc_bytes_cnt(const char * txt)
110 {
111 uint32_t txt_length = 0;
112 uint32_t chars_cnt = 0;
113 uint32_t current_ap_idx = 0;
114 uint32_t i, j;
115 uint32_t ch_enc;
116
117 txt_length = _lv_txt_get_encoded_length(txt);
118
119 i = 0;
120 j = 0;
121 while(i < txt_length) {
122 ch_enc = _lv_txt_encoded_next(txt, &j);
123 current_ap_idx = lv_ap_get_char_index(ch_enc);
124
125 if(current_ap_idx != LV_UNDEF_ARABIC_PERSIAN_CHARS)
126 ch_enc = ap_chars_map[current_ap_idx].char_end_form;
127
128 if(ch_enc < 0x80)
129 chars_cnt++;
130 else if(ch_enc < 0x0800)
131 chars_cnt += 2;
132 else if(ch_enc < 0x010000)
133 chars_cnt += 3;
134 else
135 chars_cnt += 4;
136
137 i++;
138 }
139
140 return chars_cnt + 1;
141 }
142
_lv_txt_ap_proc(const char * txt,char * txt_out)143 void _lv_txt_ap_proc(const char * txt, char * txt_out)
144 {
145 uint32_t txt_length = 0;
146 uint32_t index_current, idx_next, idx_previous, i, j;
147 uint32_t * ch_enc;
148 uint32_t * ch_fin;
149 char * txt_out_temp;
150
151 txt_length = _lv_txt_get_encoded_length(txt);
152
153 ch_enc = (uint32_t *)lv_mem_alloc(sizeof(uint32_t) * (txt_length + 1));
154 ch_fin = (uint32_t *)lv_mem_alloc(sizeof(uint32_t) * (txt_length + 1));
155
156 i = 0;
157 j = 0;
158 while(j < txt_length)
159 ch_enc[j++] = _lv_txt_encoded_next(txt, &i);
160
161 ch_enc[j] = 0;
162
163 i = 0;
164 j = 0;
165 idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
166 while(i < txt_length) {
167 index_current = lv_ap_get_char_index(ch_enc[i]);
168 idx_next = lv_ap_get_char_index(ch_enc[i + 1]);
169
170 if(lv_txt_is_arabic_vowel(ch_enc[i])) { // Current character is a vowel
171 ch_fin[j] = ch_enc[i];
172 i++;
173 j++;
174 continue; // Skip this character
175 }
176 else if(lv_txt_is_arabic_vowel(ch_enc[i + 1])) { // Next character is a vowel
177 idx_next = lv_ap_get_char_index(ch_enc[i + 2]); // Skip the vowel character to join with the character after it
178 }
179
180 if(index_current == LV_UNDEF_ARABIC_PERSIAN_CHARS) {
181 ch_fin[j] = ch_enc[i];
182 j++;
183 i++;
184 idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
185 continue;
186 }
187
188 uint8_t conjunction_to_previuse = (i == 0 ||
189 idx_previous == LV_UNDEF_ARABIC_PERSIAN_CHARS) ? 0 : ap_chars_map[idx_previous].ap_chars_conjunction.conj_to_next;
190 uint8_t conjunction_to_next = ((i == txt_length - 1) ||
191 idx_next == LV_UNDEF_ARABIC_PERSIAN_CHARS) ? 0 : ap_chars_map[idx_next].ap_chars_conjunction.conj_to_previous;
192
193 uint32_t lam_alef = lv_txt_lam_alef(index_current, idx_next);
194 if(lam_alef) {
195 if(conjunction_to_previuse) {
196 lam_alef ++;
197 }
198 ch_fin[j] = lam_alef;
199 idx_previous = LV_UNDEF_ARABIC_PERSIAN_CHARS;
200 i += 2;
201 j++;
202 continue;
203 }
204
205 if(conjunction_to_previuse && conjunction_to_next)
206 ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_middle_form_offset;
207 else if(!conjunction_to_previuse && conjunction_to_next)
208 ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_begining_form_offset;
209 else if(conjunction_to_previuse && !conjunction_to_next)
210 ch_fin[j] = ap_chars_map[index_current].char_end_form;
211 else
212 ch_fin[j] = ap_chars_map[index_current].char_end_form + ap_chars_map[index_current].char_isolated_form_offset;
213 idx_previous = index_current;
214 i++;
215 j++;
216 }
217 ch_fin[j] = 0;
218 for(i = 0; i < txt_length; i++)
219 ch_enc[i] = 0;
220 for(i = 0; i < j; i++)
221 ch_enc[i] = ch_fin[i];
222 lv_mem_free(ch_fin);
223
224 txt_out_temp = txt_out;
225 i = 0;
226
227 while(i < txt_length) {
228 if(ch_enc[i] < 0x80) {
229 *(txt_out_temp++) = ch_enc[i] & 0xFF;
230 }
231 else if(ch_enc[i] < 0x0800) {
232 *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x1F) | 0xC0;
233 *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80;
234 }
235 else if(ch_enc[i] < 0x010000) {
236 *(txt_out_temp++) = ((ch_enc[i] >> 12) & 0x0F) | 0xE0;
237 *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x3F) | 0x80;
238 *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80;
239 }
240 else if(ch_enc[i] < 0x110000) {
241 *(txt_out_temp++) = ((ch_enc[i] >> 18) & 0x07) | 0xF0;
242 *(txt_out_temp++) = ((ch_enc[i] >> 12) & 0x3F) | 0x80;
243 *(txt_out_temp++) = ((ch_enc[i] >> 6) & 0x3F) | 0x80;
244 *(txt_out_temp++) = ((ch_enc[i] >> 0) & 0x3F) | 0x80;
245 }
246
247 i++;
248 }
249 *(txt_out_temp) = '\0';
250 lv_mem_free(ch_enc);
251 }
252 /**********************
253 * STATIC FUNCTIONS
254 **********************/
255
lv_ap_get_char_index(uint16_t c)256 static uint32_t lv_ap_get_char_index(uint16_t c)
257 {
258 for(uint8_t i = 0; ap_chars_map[i].char_end_form; i++) {
259 if(c == (ap_chars_map[i].char_offset + LV_AP_ALPHABET_BASE_CODE))
260 return i;
261 else if(c == ap_chars_map[i].char_end_form //is it an End form
262 || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_begining_form_offset) //is it a Beginning form
263 || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_middle_form_offset) //is it a middle form
264 || c == (ap_chars_map[i].char_end_form + ap_chars_map[i].char_isolated_form_offset)) { //is it an isolated form
265 return i;
266 }
267 }
268 return LV_UNDEF_ARABIC_PERSIAN_CHARS;
269 }
270
lv_txt_lam_alef(uint32_t ch_curr,uint32_t ch_next)271 static uint32_t lv_txt_lam_alef(uint32_t ch_curr, uint32_t ch_next)
272 {
273 uint32_t ch_code = 0;
274 if(ap_chars_map[ch_curr].char_offset != 34) {
275 return 0;
276 }
277 if(ch_next == LV_UNDEF_ARABIC_PERSIAN_CHARS) {
278 return 0;
279 }
280 ch_code = ap_chars_map[ch_next].char_offset + LV_AP_ALPHABET_BASE_CODE;
281 if(ch_code == 0x0622) {
282 return 0xFEF5; // (lam-alef) mad
283 }
284 if(ch_code == 0x0623) {
285 return 0xFEF7; // (lam-alef) top hamza
286 }
287 if(ch_code == 0x0625) {
288 return 0xFEF9; // (lam-alef) bot hamza
289 }
290 if(ch_code == 0x0627) {
291 return 0xFEFB; // (lam-alef) alef
292 }
293 return 0;
294 }
295
lv_txt_is_arabic_vowel(uint16_t c)296 static bool lv_txt_is_arabic_vowel(uint16_t c)
297 {
298 return (c >= 0x064B) && (c <= 0x0652);
299 }
300
301 #endif
302