Milad akarie
Committed by David PHAM-VAN

Apply THE BIDIRECTIONAL ALGORITHM using dart_bidi lib

Rename some related references
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 ## 3.8.5 3 ## 3.8.5
4 4
5 - Improve TTF Writer compatibility 5 - Improve TTF Writer compatibility
  6 +- Apply THE BIDIRECTIONAL ALGORITHM using dart_bidi [Milad akarie]
6 7
7 ## 3.8.4 8 ## 3.8.4
8 9
1 -/*  
2 - * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -  
17 -/// Arabic shape substitutions: char code => (isolated, final, initial, medial).  
18 -/// Arabic Substition A  
19 -const Map<int, dynamic> _arabicSubstitionA = <int, dynamic>{  
20 - 0x0640: <int>[0x0640, 0x0640, 0x0640, 0x0640], // ARABIC TATWEEL  
21 -  
22 - 0x0621: <int>[1569], // ARABIC LETTER HAMZA  
23 - 0x0622: <int>[1570, 0xFE82], // ARABIC LETTER ALEF WITH MADDA ABOVE  
24 - 0x0623: <int>[1571, 0xFE84], // ARABIC LETTER ALEF WITH HAMZA ABOVE  
25 - 0x0624: <int>[1572, 0xFE86], // ARABIC LETTER WAW WITH HAMZA ABOVE  
26 - 0x0625: <int>[1573, 0xFE88], // ARABIC LETTER ALEF WITH HAMZA BELOW  
27 - 0x0626: <int>[  
28 - 1574,  
29 - 0xFE8A,  
30 - 0xFE8B,  
31 - 0xFE8C  
32 - ], // ARABIC LETTER YEH WITH HAMZA ABOVE  
33 - 0x0627: <int>[1575, 0xFE8E], // ARABIC LETTER ALEF  
34 - 0x0628: <int>[1576, 0xFE90, 0xFE91, 0xFE92], // ARABIC LETTER BEH  
35 - 0x0629: <int>[1577, 0xFE94], // ARABIC LETTER TEH MARBUTA  
36 - 0x062A: <int>[1578, 0xFE96, 0xFE97, 0xFE98], // ARABIC LETTER TEH  
37 - 0x062B: <int>[1579, 0xFE9A, 0xFE9B, 0xFE9C], // ARABIC LETTER THEH  
38 - 0x062C: <int>[1580, 0xFE9E, 0xFE9F, 0xFEA0], // ARABIC LETTER JEEM  
39 - 0x062D: <int>[1581, 0xFEA2, 0xFEA3, 0xFEA4], // ARABIC LETTER HAH  
40 - 0x062E: <int>[1582, 0xFEA6, 0xFEA7, 0xFEA8], // ARABIC LETTER KHAH  
41 - 0x062F: <int>[1583, 0xFEAA], // ARABIC LETTER DAL  
42 - 0x0630: <int>[1584, 0xFEAC], // ARABIC LETTER THAL  
43 - 0x0631: <int>[1585, 0xFEAE], // ARABIC LETTER REH  
44 - 0x0632: <int>[1586, 0xFEB0], // ARABIC LETTER ZAIN  
45 - 0x0633: <int>[1587, 0xFEB2, 0xFEB3, 0xFEB4], // ARABIC LETTER SEEN  
46 - 0x0634: <int>[1588, 0xFEB6, 0xFEB7, 0xFEB8], // ARABIC LETTER SHEEN  
47 - 0x0635: <int>[1589, 0xFEBA, 0xFEBB, 0xFEBC], // ARABIC LETTER SAD  
48 - 0x0636: <int>[1590, 0xFEBE, 0xFEBF, 0xFEC0], // ARABIC LETTER DAD  
49 - 0x0637: <int>[1591, 0xFEC2, 0xFEC3, 0xFEC4], // ARABIC LETTER TAH  
50 - 0x0638: <int>[1592, 0xFEC6, 0xFEC7, 0xFEC8], // ARABIC LETTER ZAH  
51 - 0x0639: <int>[1593, 0xFECA, 0xFECB, 0xFECC], // ARABIC LETTER AIN  
52 - 0x063A: <int>[1594, 0xFECE, 0xFECF, 0xFED0], // ARABIC LETTER GHAIN  
53 - 0x0641: <int>[1601, 0xFED2, 0xFED3, 0xFED4], // ARABIC LETTER FEH  
54 - 0x0642: <int>[1602, 0xFED6, 0xFED7, 0xFED8], // ARABIC LETTER QAF  
55 - 0x0643: <int>[1603, 0xFEDA, 0xFEDB, 0xFEDC], // ARABIC LETTER KAF  
56 - 0x0644: <int>[1604, 0xFEDE, 0xFEDF, 0xFEE0], // ARABIC LETTER LAM  
57 - 0x0645: <int>[1605, 0xFEE2, 0xFEE3, 0xFEE4], // ARABIC LETTER MEEM  
58 - 0x0646: <int>[1606, 0xFEE6, 0xFEE7, 0xFEE8], // ARABIC LETTER NOON  
59 - 0x0647: <int>[1607, 0xFEEA, 0xFEEB, 0xFEEC], // ARABIC LETTER HEH  
60 - 0x0648: <int>[1608, 0xFEEE], // ARABIC LETTER WAW  
61 - 0x0649: <int>[1609, 0xFEF0, 64488, 64489], // ARABIC LETTER ALEF MAKSURA  
62 - 0x064A: <int>[1610, 0xFEF2, 0xFEF3, 0xFEF4], // ARABIC LETTER YEH  
63 - 0x0671: <int>[0xFB50, 0xFB51], // ARABIC LETTER ALEF WASLA  
64 - 0x0677: <int>[0xFBDD], // ARABIC LETTER U WITH HAMZA ABOVE  
65 - 0x0679: <int>[0xFB66, 0xFB67, 0xFB68, 0xFB69], // ARABIC LETTER TTEH  
66 - 0x067A: <int>[0xFB5E, 0xFB5F, 0xFB60, 0xFB61], // ARABIC LETTER TTEHEH  
67 - 0x067B: <int>[0xFB52, 0xFB53, 0xFB54, 0xFB55], // ARABIC LETTER BEEH  
68 - 0x067E: <int>[0xFB56, 0xFB57, 0xFB58, 0xFB59], // ARABIC LETTER PEH  
69 - 0x067F: <int>[0xFB62, 0xFB63, 0xFB64, 0xFB65], // ARABIC LETTER TEHEH  
70 - 0x0680: <int>[0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D], // ARABIC LETTER BEHEH  
71 - 0x0683: <int>[0xFB76, 0xFB77, 0xFB78, 0xFB79], // ARABIC LETTER NYEH  
72 - 0x0684: <int>[0xFB72, 0xFB73, 0xFB74, 0xFB75], // ARABIC LETTER DYEH  
73 - 0x0686: <int>[0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D], // ARABIC LETTER TCHEH  
74 - 0x0687: <int>[0xFB7E, 0xFB7F, 0xFB80, 0xFB81], // ARABIC LETTER TCHEHEH  
75 - 0x0688: <int>[0xFB88, 0xFB89], // ARABIC LETTER DDAL  
76 - 0x068C: <int>[0xFB84, 0xFB85], // ARABIC LETTER DAHAL  
77 - 0x068D: <int>[0xFB82, 0xFB83], // ARABIC LETTER DDAHAL  
78 - 0x068E: <int>[0xFB86, 0xFB87], // ARABIC LETTER DUL  
79 - 0x0691: <int>[0xFB8C, 0xFB8D], // ARABIC LETTER RREH  
80 - 0x0698: <int>[0xFB8A, 0xFB8B], // ARABIC LETTER JEH  
81 - 0x06A4: <int>[0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D], // ARABIC LETTER VEH  
82 - 0x06A6: <int>[0xFB6E, 0xFB6F, 0xFB70, 0xFB71], // ARABIC LETTER PEHEH  
83 - 0x06A9: <int>[0xFB8E, 0xFB8F, 0xFB90, 0xFB91], // ARABIC LETTER KEHEH  
84 - 0x06AD: <int>[0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6], // ARABIC LETTER NG  
85 - 0x06AF: <int>[0xFB92, 0xFB93, 0xFB94, 0xFB95], // ARABIC LETTER GAF  
86 - 0x06B1: <int>[0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D], // ARABIC LETTER NGOEH  
87 - 0x06B3: <int>[0xFB96, 0xFB97, 0xFB98, 0xFB99], // ARABIC LETTER GUEH  
88 - 0x06BA: <int>[0xFB9E, 0xFB9F], // ARABIC LETTER NOON GHUNNA  
89 - 0x06BB: <int>[0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3], // ARABIC LETTER RNOON  
90 - 0x06BE: <int>[  
91 - 0xFBAA,  
92 - 0xFBAB,  
93 - 0xFBAC,  
94 - 0xFBAD  
95 - ], // ARABIC LETTER HEH DOACHASHMEE  
96 - 0x06C0: <int>[0xFBA4, 0xFBA5], // ARABIC LETTER HEH WITH YEH ABOVE  
97 - 0x06C1: <int>[0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9], // ARABIC LETTER HEH GOAL  
98 - 0x06C5: <int>[0xFBE0, 0xFBE1], // ARABIC LETTER KIRGHIZ OE  
99 - 0x06C6: <int>[0xFBD9, 0xFBDA], // ARABIC LETTER OE  
100 - 0x06C7: <int>[0xFBD7, 0xFBD8], // ARABIC LETTER U  
101 - 0x06C8: <int>[0xFBDB, 0xFBDC], // ARABIC LETTER YU  
102 - 0x06C9: <int>[0xFBE2, 0xFBE3], // ARABIC LETTER KIRGHIZ YU  
103 - 0x06CB: <int>[0xFBDE, 0xFBDF], // ARABIC LETTER VE  
104 - 0x06CC: <int>[0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF], // ARABIC LETTER FARSI YEH  
105 - 0x06D0: <int>[0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7], //ARABIC LETTER E  
106 - 0x06D2: <int>[0xFBAE, 0xFBAF], // ARABIC LETTER YEH BARREE  
107 - 0x06D3: <int>[0xFBB0, 0xFBB1], // ARABIC LETTER YEH BARREE WITH HAMZA ABOVE  
108 -};  
109 -  
110 -/*  
111 - var ligaturesSubstitutionA = {  
112 - 0xFBEA: []// ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM  
113 - };  
114 - */  
115 -  
116 -const Map<int, dynamic> _diacriticLigatures = <int, dynamic>{  
117 - 0x0651: <int, int>{  
118 - 0x064C: 0xFC5E, // Shadda + Dammatan  
119 - 0x064D: 0xFC5F, // Shadda + Kasratan  
120 - 0x064E: 0xFC60, // Shadda + Fatha  
121 - 0x064F: 0xFC61, // Shadda + Damma  
122 - 0x0650: 0xFC62, // Shadda + Kasra  
123 - 0x0670: 0xFC63, // Shadda + Dagger alif  
124 - },  
125 -};  
126 -  
127 -const Map<int, dynamic> _ligatures = <int, dynamic>{  
128 - 0xFEDF: <int, int>{  
129 - 0xFE82:  
130 - 0xFEF5, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM  
131 - 0xFE84:  
132 - 0xFEF7, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM  
133 - 0xFE88:  
134 - 0xFEF9, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM  
135 - 0xFE8E: 0xFEFB // ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM  
136 - },  
137 - 0xFEE0: <int, int>{  
138 - 0xFE82: 0xFEF6, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM  
139 - 0xFE84: 0xFEF8, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM  
140 - 0xFE88: 0xFEFA, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM  
141 - 0xFE8E: 0xFEFC // ARABIC LIGATURE LAM WITH ALEF FINAL FORM  
142 - },  
143 - 0xFE8D: <int, dynamic>{  
144 - 0xFEDF: <int, dynamic>{  
145 - 0xFEE0: <int, int>{0xFEEA: 0xFDF2}  
146 - }  
147 - }, // ALLAH  
148 -};  
149 -  
150 -const List<int> _alfletter = <int>[1570, 1571, 1573, 1575];  
151 -  
152 -const Map<int, int> _arabicDiacritics = <int, int>{  
153 - 0x064B: 0x064B, // Fathatan  
154 - 0x064C: 0x064C, // Dammatan  
155 - 0x064D: 0x064D, // Kasratan  
156 - 0x064E: 0x064E, // Fatha  
157 - 0x064F: 0x064F, // Damma  
158 - 0x0650: 0x0650, // Kasra  
159 - 0x0651: 0x0651, // Shadda  
160 - 0x0652: 0x0652, // Sukun  
161 -  
162 - 0x0670: 0x0670, // Dagger alif  
163 -  
164 - 0xFC5E: 0xFC5E, // Shadda + Dammatan  
165 - 0xFC5F: 0xFC5F, // Shadda + Kasratan  
166 - 0xFC60: 0xFC60, // Shadda + Fatha  
167 - 0xFC61: 0xFC61, // Shadda + Damma  
168 - 0xFC62: 0xFC62, // Shadda + Kasra  
169 - 0xFC63: 0xFC63, // Shadda + Dagger alif  
170 - // 1548: 1548,  
171 -};  
172 -  
173 -const int _noChangeInForm = -1;  
174 -const int _isolatedForm = 0;  
175 -const int _finalForm = 1;  
176 -const int _initialForm = 2;  
177 -const int _medialForm = 3;  
178 -  
179 -bool _isInArabicSubstitutionA(int letter) {  
180 - return _arabicSubstitionA.containsKey(letter);  
181 -}  
182 -  
183 -bool _isArabicLetter(int letter) {  
184 - return (letter >= 0x0600 && letter <= 0x06FF) ||  
185 - (letter >= 0x0750 && letter <= 0x077F) ||  
186 - (letter >= 0x0870 && letter <= 0x089F) ||  
187 - (letter >= 0x08A0 && letter <= 0x08FF) ||  
188 - (letter >= 0xFB50 && letter <= 0xFDFF) ||  
189 - (letter >= 0xFE70 && letter <= 0xFEFF) ||  
190 - (letter >= 0x10E60 && letter <= 0x10E7F) ||  
191 - (letter >= 0x1EC70 && letter <= 0x1ECBF) ||  
192 - (letter >= 0x1ED00 && letter <= 0x1ED4F) ||  
193 - (letter >= 0x1EE00 && letter <= 0x1EEFF);  
194 -}  
195 -  
196 -bool _isArabicEndLetter(int letter) {  
197 - return _isArabicLetter(letter) &&  
198 - _isInArabicSubstitutionA(letter) &&  
199 - _arabicSubstitionA[letter].length <= 2;  
200 -}  
201 -  
202 -bool _isArabicAlfLetter(int letter) {  
203 - return _isArabicLetter(letter) && _alfletter.contains(letter);  
204 -}  
205 -  
206 -bool _arabicLetterHasFinalForm(int letter) {  
207 - return _isArabicLetter(letter) &&  
208 - _isInArabicSubstitutionA(letter) &&  
209 - (_arabicSubstitionA[letter].length >= 2);  
210 -}  
211 -  
212 -bool _arabicLetterHasMedialForm(int letter) {  
213 - return _isArabicLetter(letter) &&  
214 - _isInArabicSubstitutionA(letter) &&  
215 - _arabicSubstitionA[letter].length == 4;  
216 -}  
217 -  
218 -bool _isArabicDiacritic(int letter) {  
219 - return _arabicDiacritics.containsKey(letter);  
220 -}  
221 -  
222 -bool isArabicDiacriticValue(int letter) {  
223 - return _arabicDiacritics.containsValue(letter);  
224 -}  
225 -  
226 -List<int> _resolveLigatures(List<int> lettersq) {  
227 - final result = <int>[];  
228 - dynamic tmpLigatures = _ligatures;  
229 - dynamic tmpDiacritic = _diacriticLigatures;  
230 - final letters = lettersq.reversed.toList();  
231 -  
232 - final effectedLetters = <int>[];  
233 - final effectedDiacritics = <int>[];  
234 -  
235 - final finalDiacritics = <int>[];  
236 -  
237 - for (var i = 0; i < letters.length; i++) {  
238 - if (isArabicDiacriticValue(letters[i])) {  
239 - effectedDiacritics.insert(0, letters[i]);  
240 - if (tmpDiacritic.containsKey(letters[i])) {  
241 - tmpDiacritic = tmpDiacritic[letters[i]];  
242 -  
243 - if (tmpDiacritic is int) {  
244 - finalDiacritics.insert(0, tmpDiacritic);  
245 - tmpDiacritic = _diacriticLigatures;  
246 - effectedDiacritics.clear();  
247 - }  
248 - } else {  
249 - tmpDiacritic = _diacriticLigatures;  
250 -  
251 - // add all Diacritics if there is no letter Ligatures.  
252 - if (effectedLetters.isEmpty) {  
253 - result.insertAll(0, finalDiacritics);  
254 - result.insertAll(0, effectedDiacritics);  
255 - finalDiacritics.clear();  
256 - effectedDiacritics.clear();  
257 - }  
258 - }  
259 - } else if (tmpLigatures.containsKey(letters[i])) {  
260 - effectedLetters.insert(0, letters[i]);  
261 - tmpLigatures = tmpLigatures[letters[i]];  
262 -  
263 - if (tmpLigatures is int) {  
264 - result.insert(0, tmpLigatures);  
265 - tmpLigatures = _ligatures;  
266 - effectedLetters.clear();  
267 - }  
268 - } else {  
269 - tmpLigatures = _ligatures;  
270 -  
271 - // add effected letters if they aren't ligature.  
272 - if (effectedLetters.isNotEmpty) {  
273 - result.insertAll(0, effectedLetters);  
274 - effectedLetters.clear();  
275 - }  
276 -  
277 - // add Diacritics after or before letter ligature.  
278 - if (effectedLetters.isEmpty && effectedDiacritics.isNotEmpty) {  
279 - result.insertAll(0, effectedDiacritics);  
280 - effectedDiacritics.clear();  
281 - }  
282 -  
283 - result.insert(0, letters[i]);  
284 - }  
285 -  
286 - // add Diacritic ligatures.  
287 - if (effectedLetters.isEmpty && finalDiacritics.isNotEmpty) {  
288 - result.insertAll(0, finalDiacritics);  
289 - finalDiacritics.clear();  
290 - }  
291 - }  
292 -  
293 - return result;  
294 -}  
295 -  
296 -int _getCorrectForm(int currentChar, int beforeChar, int nextChar) {  
297 - if (_isInArabicSubstitutionA(currentChar) == false) {  
298 - return _noChangeInForm;  
299 - }  
300 - if (!_arabicLetterHasFinalForm(currentChar) ||  
301 - (!_isArabicLetter(beforeChar) && !_isArabicLetter(nextChar)) ||  
302 - (!_isArabicLetter(nextChar) && _isArabicEndLetter(beforeChar)) ||  
303 - (_isArabicEndLetter(currentChar) && !_isArabicLetter(beforeChar)) ||  
304 - (_isArabicEndLetter(currentChar) && _isArabicAlfLetter(beforeChar)) ||  
305 - (_isArabicEndLetter(currentChar) && _isArabicEndLetter(beforeChar))) {  
306 - return _isolatedForm;  
307 - }  
308 -  
309 - if (_arabicLetterHasMedialForm(currentChar) &&  
310 - _isArabicLetter(beforeChar) &&  
311 - !_isArabicEndLetter(beforeChar) &&  
312 - _isArabicLetter(nextChar) &&  
313 - _arabicLetterHasFinalForm(nextChar)) {  
314 - return _medialForm;  
315 - }  
316 -  
317 - if (_isArabicEndLetter(currentChar) || (!_isArabicLetter(nextChar))) {  
318 - return _finalForm;  
319 - }  
320 - return _initialForm;  
321 -}  
322 -  
323 -Iterable<String> _parse(String text) sync* {  
324 - final words = text.split(' ');  
325 -  
326 - final notArabicWords = <List<int>>[];  
327 -  
328 - var first = true;  
329 - for (final word in words) {  
330 - final newWord = <int>[];  
331 - var isNewWordArabic = false;  
332 -  
333 - var prevLetter = 0;  
334 -  
335 - for (var j = 0; j < word.length; j += 1) {  
336 - final currentLetter = word.codeUnitAt(j);  
337 -  
338 - if (_isArabicDiacritic(currentLetter)) {  
339 - newWord.insert(0, _arabicDiacritics[currentLetter]!);  
340 - continue;  
341 - }  
342 - final nextLetter = word  
343 - .split('')  
344 - .skip(j + 1)  
345 - .map((String e) => e.codeUnitAt(0))  
346 - .firstWhere(  
347 - (int element) => !_isArabicDiacritic(element),  
348 - orElse: () => 0,  
349 - );  
350 -  
351 - if (_isArabicLetter(currentLetter)) {  
352 - isNewWordArabic = true;  
353 -  
354 - final position = _getCorrectForm(currentLetter, prevLetter, nextLetter);  
355 - prevLetter = currentLetter;  
356 - if (position != -1) {  
357 - newWord.insert(0, _arabicSubstitionA[currentLetter][position]);  
358 - } else {  
359 - newWord.add(currentLetter);  
360 - }  
361 - } else {  
362 - prevLetter = 0;  
363 - if (isNewWordArabic && currentLetter > 32) {  
364 - newWord.insert(0, currentLetter);  
365 - } else {  
366 - newWord.add(currentLetter);  
367 - }  
368 - }  
369 - }  
370 -  
371 - if (!first && isNewWordArabic) {  
372 - yield ' ';  
373 - }  
374 - first = false;  
375 -  
376 - if (isNewWordArabic) {  
377 - isNewWordArabic = false;  
378 - for (final notArabicNewWord in notArabicWords) {  
379 - yield '${String.fromCharCodes(notArabicNewWord)} ';  
380 - }  
381 - notArabicWords.clear();  
382 - yield String.fromCharCodes(_resolveLigatures(newWord));  
383 - } else {  
384 - notArabicWords.insert(0, newWord);  
385 - }  
386 - }  
387 - // if notArabicWords.length != 0, that means all sentence doesn't contain Arabic.  
388 - for (var i = 0; i < notArabicWords.length; i++) {  
389 - if (!first) {  
390 - yield ' ';  
391 - }  
392 - yield String.fromCharCodes(notArabicWords[i]);  
393 - }  
394 -}  
395 -  
396 -/// Apply Arabic shape substitutions  
397 -String convert(String input) {  
398 - final lines = input.split('\n');  
399 - final parsed = <String>[];  
400 - for (var i = 0; i < lines.length; i++) {  
401 - if (lines[i].isEmpty) {  
402 - continue;  
403 - }  
404 - parsed.addAll([..._parse(lines[i]), if (i != lines.length - 1) '\n']);  
405 - }  
406 - return parsed.join();  
407 -}  
  1 +import 'package:bidi/bidi.dart' as bidi;
  2 +/*
  3 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  4 + *
  5 + * Licensed under the Apache License, Version 2.0 (the "License");
  6 + * you may not use this file except in compliance with the License.
  7 + * You may obtain a copy of the License at
  8 + *
  9 + * http://www.apache.org/licenses/LICENSE-2.0
  10 + *
  11 + * Unless required by applicable law or agreed to in writing, software
  12 + * distributed under the License is distributed on an "AS IS" BASIS,
  13 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + * See the License for the specific language governing permissions and
  15 + * limitations under the License.
  16 + */
  17 +
  18 +const Map<int, int> _arabicDiacritics = <int, int>{
  19 + 0x064B: 0x064B, // Fathatan
  20 + 0x064C: 0x064C, // Dammatan
  21 + 0x064D: 0x064D, // Kasratan
  22 + 0x064E: 0x064E, // Fatha
  23 + 0x064F: 0x064F, // Damma
  24 + 0x0650: 0x0650, // Kasra
  25 + 0x0651: 0x0651, // Shadda
  26 + 0x0652: 0x0652, // Sukun
  27 + 0x0670: 0x0670, // Dagger alif
  28 + 0xFC5E: 0xFC5E, // Shadda + Dammatan
  29 + 0xFC5F: 0xFC5F, // Shadda + Kasratan
  30 + 0xFC60: 0xFC60, // Shadda + Fatha
  31 + 0xFC61: 0xFC61, // Shadda + Damma
  32 + 0xFC62: 0xFC62, // Shadda + Kasra
  33 + 0xFC63: 0xFC63, // Shadda + Dagger alif
  34 + // 1548: 1548,
  35 +};
  36 +
  37 +bool isArabicDiacriticValue(int letter) {
  38 + return _arabicDiacritics.containsValue(letter);
  39 +}
  40 +
  41 +/// Applies THE BIDIRECTIONAL ALGORITHM using (https://pub.dev/packages/bidi)
  42 +String logicalToVisual(String input) {
  43 + final buffer = StringBuffer();
  44 + final paragraphs = bidi.splitStringToParagraphs(input);
  45 + for (final paragraph in paragraphs) {
  46 + final endsWithNewLine = paragraph.paragraphSeparator == 10;
  47 + final endIndex = paragraph.bidiText.length - (endsWithNewLine ? 1 : 0);
  48 + final visual = String.fromCharCodes(paragraph.bidiText, 0, endIndex);
  49 + buffer.write(visual.split(' ').reversed.join(' '));
  50 + if (endsWithNewLine) {
  51 + buffer.writeln();
  52 + }
  53 + }
  54 + return buffer.toString();
  55 +}
@@ -19,7 +19,7 @@ import 'dart:typed_data'; @@ -19,7 +19,7 @@ import 'dart:typed_data';
19 19
20 import '../data_types.dart'; 20 import '../data_types.dart';
21 import '../document.dart'; 21 import '../document.dart';
22 -import '../font/arabic.dart' as arabic; 22 +import '../font/bidi_utils.dart' as bidi;
23 import '../font/font_metrics.dart'; 23 import '../font/font_metrics.dart';
24 import '../font/ttf_parser.dart'; 24 import '../font/ttf_parser.dart';
25 import '../font/ttf_writer.dart'; 25 import '../font/ttf_writer.dart';
@@ -74,7 +74,7 @@ class PdfTtfFont extends PdfFont { @@ -74,7 +74,7 @@ class PdfTtfFont extends PdfFont {
74 return PdfFontMetrics.zero; 74 return PdfFontMetrics.zero;
75 } 75 }
76 76
77 - if (arabic.isArabicDiacriticValue(charCode)) { 77 + if (bidi.isArabicDiacriticValue(charCode)) {
78 final metric = font.glyphInfoMap[g] ?? PdfFontMetrics.zero; 78 final metric = font.glyphInfoMap[g] ?? PdfFontMetrics.zero;
79 return metric.copyWith(advanceWidth: 0); 79 return metric.copyWith(advanceWidth: 0);
80 } 80 }
@@ -19,7 +19,7 @@ import 'dart:math' as math; @@ -19,7 +19,7 @@ import 'dart:math' as math;
19 import 'package:meta/meta.dart'; 19 import 'package:meta/meta.dart';
20 20
21 import '../../pdf.dart'; 21 import '../../pdf.dart';
22 -import '../pdf/font/arabic.dart' as arabic; 22 +import '../pdf/font/bidi_utils.dart' as bidi;
23 import 'annotations.dart'; 23 import 'annotations.dart';
24 import 'basic.dart'; 24 import 'basic.dart';
25 import 'document.dart'; 25 import 'document.dart';
@@ -918,7 +918,7 @@ class RichText extends Widget with SpanningWidget { @@ -918,7 +918,7 @@ class RichText extends Widget with SpanningWidget {
918 font.stringMetrics(' ') * (style.fontSize! * textScaleFactor); 918 font.stringMetrics(' ') * (style.fontSize! * textScaleFactor);
919 919
920 final spanLines = (_textDirection == TextDirection.rtl 920 final spanLines = (_textDirection == TextDirection.rtl
921 - ? arabic.convert(span.text!) 921 + ? bidi.logicalToVisual(span.text!)
922 : span.text)! 922 : span.text)!
923 .split('\n'); 923 .split('\n');
924 924
@@ -11,6 +11,7 @@ environment: @@ -11,6 +11,7 @@ environment:
11 dependencies: 11 dependencies:
12 archive: ^3.1.0 12 archive: ^3.1.0
13 barcode: ">=2.2.3 <3.0.0" 13 barcode: ">=2.2.3 <3.0.0"
  14 + bidi: ^2.0.2
14 crypto: ^3.0.0 15 crypto: ^3.0.0
15 image: ">=3.0.1 <4.0.0" 16 image: ">=3.0.1 <4.0.0"
16 meta: ">=1.3.0 <2.0.0" 17 meta: ">=1.3.0 <2.0.0"
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 import 'dart:io'; 17 import 'dart:io';
18 18
19 -import 'package:pdf/src/pdf/font/arabic.dart' as arabic; 19 +import 'package:pdf/src/pdf/font/bidi_utils.dart' as bidi;
20 import 'package:pdf/widgets.dart'; 20 import 'package:pdf/widgets.dart';
21 import 'package:test/test.dart'; 21 import 'package:test/test.dart';
22 22
@@ -46,16 +46,16 @@ void main() { @@ -46,16 +46,16 @@ void main() {
46 }); 46 });
47 47
48 test('Arabic Diacritics', () { 48 test('Arabic Diacritics', () {
49 - final a = ArabicText('السلام', <int>[1605, 65276, 65204, 65247, 1575]); 49 + final a = ArabicText('السلام', <int>[65249, 65276, 65204, 65247, 65165]);
50 final b = ArabicText('السَلَاْمٌ', 50 final b = ArabicText('السَلَاْمٌ',
51 - <int>[1612, 1605, 1618, 1614, 65276, 1614, 65204, 65247, 1575]); 51 + <int>[1612, 65249, 1618, 1614, 65276, 1614, 65204, 65247, 65165]);
52 52
53 expect( 53 expect(
54 - arabic.convert(a.original).codeUnits, 54 + bidi.logicalToVisual(a.original).codeUnits,
55 equals(a.reshaped), 55 equals(a.reshaped),
56 ); 56 );
57 expect( 57 expect(
58 - arabic.convert(b.original).codeUnits, 58 + bidi.logicalToVisual(b.original).codeUnits,
59 equals(b.reshaped), 59 equals(b.reshaped),
60 ); 60 );
61 }); 61 });
@@ -64,7 +64,7 @@ void main() { @@ -64,7 +64,7 @@ void main() {
64 final cases = <ArabicText>[ 64 final cases = <ArabicText>[
65 ArabicText('الـــسَلاْمُ عَلَيْكُمْ', <int>[ 65 ArabicText('الـــسَلاْمُ عَلَيْكُمْ', <int>[
66 1615, 66 1615,
67 - 1605, 67 + 65249,
68 1618, 68 1618,
69 65276, 69 65276,
70 1614, 70 1614,
@@ -73,7 +73,7 @@ void main() { @@ -73,7 +73,7 @@ void main() {
73 1600, 73 1600,
74 1600, 74 1600,
75 65247, 75 65247,
76 - 1575, 76 + 65165,
77 32, 77 32,
78 1618, 78 1618,
79 65250, 79 65250,
@@ -94,7 +94,7 @@ void main() { @@ -94,7 +94,7 @@ void main() {
94 1600, 94 1600,
95 1600, 95 1600,
96 65247, 96 65247,
97 - 1575, 97 + 65165,
98 32, 98 32,
99 65172, 99 65172,
100 64608, 100 64608,
@@ -105,7 +105,7 @@ void main() { @@ -105,7 +105,7 @@ void main() {
105 1600, 105 1600,
106 65228, 106 65228,
107 65247, 107 65247,
108 - 1575, 108 + 65165,
109 32, 109 32,
110 65266, 110 65266,
111 65259, 111 65259,
@@ -114,15 +114,15 @@ void main() { @@ -114,15 +114,15 @@ void main() {
114 65198, 114 65198,
115 65180, 115 65180,
116 65243, 116 65243,
117 - 1571, 117 + 65155,
118 32, 118 32,
119 1616, 119 1616,
120 - 1578, 120 + 65173,
121 65166, 121 65166,
122 65232, 122 65232,
123 65248, 123 65248,
124 65247, 124 65247,
125 - 1575 125 + 65165
126 ]), 126 ]),
127 ArabicText('تحدُّثاً ونُطقاً ضِمْنَ مَجمُوعَة', <int>[ 127 ArabicText('تحدُّثاً ونُطقاً ضِمْنَ مَجمُوعَة', <int>[
128 1611, 128 1611,
@@ -139,7 +139,7 @@ void main() { @@ -139,7 +139,7 @@ void main() {
139 65220, 139 65220,
140 1615, 140 1615,
141 65255, 141 65255,
142 - 1608, 142 + 65261,
143 32, 143 32,
144 1614, 144 1614,
145 65254, 145 65254,
@@ -159,12 +159,12 @@ void main() { @@ -159,12 +159,12 @@ void main() {
159 65251 159 65251
160 ]), 160 ]),
161 ArabicText('اللغات السامية', <int>[ 161 ArabicText('اللغات السامية', <int>[
162 - 1578, 162 + 65173,
163 65166, 163 65166,
164 65232, 164 65232,
165 65248, 165 65248,
166 65247, 166 65247,
167 - 1575, 167 + 65165,
168 32, 168 32,
169 65172, 169 65172,
170 65268, 170 65268,
@@ -172,7 +172,7 @@ void main() { @@ -172,7 +172,7 @@ void main() {
172 65166, 172 65166,
173 65204, 173 65204,
174 65247, 174 65247,
175 - 1575 175 + 65165,
176 ]), 176 ]),
177 ArabicText('العربية لغةٌ رسميةٌ في', <int>[ 177 ArabicText('العربية لغةٌ رسميةٌ في', <int>[
178 65172, 178 65172,
@@ -181,7 +181,7 @@ void main() { @@ -181,7 +181,7 @@ void main() {
181 65198, 181 65198,
182 65228, 182 65228,
183 65247, 183 65247,
184 - 1575, 184 + 65165,
185 32, 185 32,
186 1612, 186 1612,
187 65172, 187 65172,
@@ -193,7 +193,7 @@ void main() { @@ -193,7 +193,7 @@ void main() {
193 65268, 193 65268,
194 65252, 194 65252,
195 65203, 195 65203,
196 - 1585, 196 + 65197,
197 32, 197 32,
198 65266, 198 65266,
199 65235 199 65235
@@ -204,16 +204,16 @@ void main() { @@ -204,16 +204,16 @@ void main() {
204 65243, 204 65243,
205 32, 205 32,
206 1616, 206 1616,
207 - 1604,  
208 - 1608,  
209 - 1583, 207 + 65245,
  208 + 65261,
  209 + 65193,
210 32, 210 32,
211 1616, 211 1616,
212 65254, 212 65254,
213 65219, 213 65219,
214 65262, 214 65262,
215 65247, 215 65247,
216 - 1575, 216 + 65165,
217 32, 217 32,
218 64610, 218 64610,
219 65266, 219 65266,
@@ -221,19 +221,19 @@ void main() { @@ -221,19 +221,19 @@ void main() {
221 65198, 221 65198,
222 65228, 222 65228,
223 65247, 223 65247,
224 - 1575 224 + 65165
225 ]), 225 ]),
226 ArabicText('إضافة إلىّٰ كونها لغة؟', <int>[ 226 ArabicText('إضافة إلىّٰ كونها لغة؟', <int>[
227 65172, 227 65172,
228 65235, 228 65235,
229 65166, 229 65166,
230 65215, 230 65215,
231 - 1573, 231 + 65159,
232 32, 232 32,
233 64611, 233 64611,
234 65264, 234 65264,
235 65247, 235 65247,
236 - 1573, 236 + 65159,
237 32, 237 32,
238 65166, 238 65166,
239 65260, 239 65260,
@@ -241,22 +241,22 @@ void main() { @@ -241,22 +241,22 @@ void main() {
241 65262, 241 65262,
242 65243, 242 65243,
243 32, 243 32,
  244 + 1567,
244 65172, 245 65172,
245 65232, 246 65232,
246 65247, 247 65247,
247 - 1567  
248 ]), 248 ]),
249 ArabicText('رسمية في تشاد وإريتريا', <int>[ 249 ArabicText('رسمية في تشاد وإريتريا', <int>[
250 65172, 250 65172,
251 65268, 251 65268,
252 65252, 252 65252,
253 65203, 253 65203,
254 - 1585, 254 + 65197,
255 32, 255 32,
256 65266, 256 65266,
257 65235, 257 65235,
258 32, 258 32,
259 - 1583, 259 + 65193,
260 65166, 260 65166,
261 65208, 261 65208,
262 65175, 262 65175,
@@ -266,36 +266,36 @@ void main() { @@ -266,36 +266,36 @@ void main() {
266 65198, 266 65198,
267 65176, 267 65176,
268 65267, 268 65267,
269 - 1585,  
270 - 1573,  
271 - 1608 269 + 65197,
  270 + 65159,
  271 + 65261
272 ]), 272 ]),
273 ArabicText('وإسرائيل. وهي إحدى اللغات', <int>[ 273 ArabicText('وإسرائيل. وهي إحدى اللغات', <int>[
274 46, 274 46,
275 65246, 275 65246,
276 65268, 276 65268,
277 65163, 277 65163,
278 - 1575, 278 + 65165,
279 65198, 279 65198,
280 65203, 280 65203,
281 - 1573,  
282 - 1608, 281 + 65159,
  282 + 65261,
283 32, 283 32,
284 65266, 284 65266,
285 65259, 285 65259,
286 - 1608, 286 + 65261,
287 32, 287 32,
288 - 1609, 288 + 65263,
289 65194, 289 65194,
290 65187, 290 65187,
291 - 1573, 291 + 65159,
292 32, 292 32,
293 - 1578, 293 + 65173,
294 65166, 294 65166,
295 65232, 295 65232,
296 65248, 296 65248,
297 65247, 297 65247,
298 - 1575 298 + 65165
299 ]), 299 ]),
300 ArabicText('الرسمية الست في منظمة', <int>[ 300 ArabicText('الرسمية الست في منظمة', <int>[
301 65172, 301 65172,
@@ -304,12 +304,12 @@ void main() { @@ -304,12 +304,12 @@ void main() {
304 65203, 304 65203,
305 65198, 305 65198,
306 65247, 306 65247,
307 - 1575, 307 + 65165,
308 32, 308 32,
309 65174, 309 65174,
310 65204, 310 65204,
311 65247, 311 65247,
312 - 1575, 312 + 65165,
313 32, 313 32,
314 65266, 314 65266,
315 65235, 315 65235,
@@ -324,16 +324,16 @@ void main() { @@ -324,16 +324,16 @@ void main() {
324 65250, 324 65250,
325 65251, 325 65251,
326 65271, 326 65271,
327 - 1575, 327 + 65165,
328 32, 328 32,
329 - 1577, 329 + 1548,
  330 + 65171,
330 65194, 331 65194,
331 65188, 332 65188,
332 65176, 333 65176,
333 65252, 334 65252,
334 65247, 335 65247,
335 - 1575,  
336 - 1548, 336 + 65165,
337 32, 337 32,
338 65246, 338 65246,
339 65236, 339 65236,
@@ -341,10 +341,10 @@ void main() { @@ -341,10 +341,10 @@ void main() {
341 65188, 341 65188,
342 1615, 342 1615,
343 65267, 343 65267,
344 - 1608 344 + 65261
345 ]), 345 ]),
346 ArabicText('باليوم العالمي للغة العربية', <int>[ 346 ArabicText('باليوم العالمي للغة العربية', <int>[
347 - 1605, 347 + 65249,
348 65262, 348 65262,
349 65268, 349 65268,
350 65247, 350 65247,
@@ -357,7 +357,7 @@ void main() { @@ -357,7 +357,7 @@ void main() {
357 65166, 357 65166,
358 65228, 358 65228,
359 65247, 359 65247,
360 - 1575, 360 + 65165,
361 32, 361 32,
362 65172, 362 65172,
363 65232, 363 65232,
@@ -370,7 +370,7 @@ void main() { @@ -370,7 +370,7 @@ void main() {
370 65198, 370 65198,
371 65228, 371 65228,
372 65247, 372 65247,
373 - 1575 373 + 65165
374 ]), 374 ]),
375 ArabicText('في 18 ديسمبر كذكرى اعتماد', <int>[ 375 ArabicText('في 18 ديسمبر كذكرى اعتماد', <int>[
376 65266, 376 65266,
@@ -384,20 +384,20 @@ void main() { @@ -384,20 +384,20 @@ void main() {
384 65252, 384 65252,
385 65204, 385 65204,
386 65267, 386 65267,
387 - 1583, 387 + 65193,
388 32, 388 32,
389 - 1609, 389 + 65263,
390 65198, 390 65198,
391 65243, 391 65243,
392 65196, 392 65196,
393 65243, 393 65243,
394 32, 394 32,
395 - 1583, 395 + 65193,
396 65166, 396 65166,
397 65252, 397 65252,
398 65176, 398 65176,
399 65227, 399 65227,
400 - 1575 400 + 65165
401 ]), 401 ]),
402 ArabicText('العربية بين لغات العمل في', <int>[ 402 ArabicText('العربية بين لغات العمل في', <int>[
403 65172, 403 65172,
@@ -406,13 +406,13 @@ void main() { @@ -406,13 +406,13 @@ void main() {
406 65198, 406 65198,
407 65228, 407 65228,
408 65247, 408 65247,
409 - 1575, 409 + 65165,
410 32, 410 32,
411 65254, 411 65254,
412 65268, 412 65268,
413 65169, 413 65169,
414 32, 414 32,
415 - 1578, 415 + 65173,
416 65166, 416 65166,
417 65232, 417 65232,
418 65247, 418 65247,
@@ -421,7 +421,7 @@ void main() { @@ -421,7 +421,7 @@ void main() {
421 65252, 421 65252,
422 65228, 422 65228,
423 65247, 423 65247,
424 - 1575, 424 + 65165,
425 32, 425 32,
426 65266, 426 65266,
427 65235 427 65235
@@ -430,16 +430,16 @@ void main() { @@ -430,16 +430,16 @@ void main() {
430 65250, 430 65250,
431 65251, 431 65251,
432 65271, 432 65271,
433 - 1575, 433 + 65165,
434 32, 434 32,
435 46, 435 46,
436 - 1577, 436 + 65171,
437 65194, 437 65194,
438 65188, 438 65188,
439 65176, 439 65176,
440 65252, 440 65252,
441 65247, 441 65247,
442 - 1575, 442 + 65165,
443 ]), 443 ]),
444 ]; 444 ];
445 445
@@ -462,7 +462,7 @@ void main() { @@ -462,7 +462,7 @@ void main() {
462 462
463 for (final item in cases) { 463 for (final item in cases) {
464 expect( 464 expect(
465 - arabic.convert(item.original).codeUnits, 465 + bidi.logicalToVisual(item.original).codeUnits,
466 equals(item.reshaped), 466 equals(item.reshaped),
467 ); 467 );
468 } 468 }
@@ -481,7 +481,7 @@ void main() { @@ -481,7 +481,7 @@ void main() {
481 children: const <TextSpan>[ 481 children: const <TextSpan>[
482 TextSpan( 482 TextSpan(
483 text: 483 text:
484 - 'القهوة مشروب يعد من بذور الب المحمصة، وينمو في أكثر من 70 لداً. خصوصاً في المناطق الاستوائية في أمريكا الشمالية والجنوبية وجنوب شرق آسيا وشبه القارة الهندية وأفريقيا. ويقال أن البن الأخضر هو ثاني أكثر السلع تداولاً في العالم بعد النفط الخام.', 484 + 'القهوة مشروب يعد من بذور الب المحمصة، وينمو في أكثر من 70 بلداً. خصوصاً في المناطق الاستوائية في أمريكا الشمالية والجنوبية وجنوب شرق آسيا وشبه القارة الهندية وأفريقيا. ويقال أن البن الأخضر هو ثاني أكثر السلع تداولاً في العالم بعد النفط الخام.',
485 style: TextStyle( 485 style: TextStyle(
486 fontSize: 20, 486 fontSize: 20,
487 ), 487 ),
@@ -506,7 +506,7 @@ void main() { @@ -506,7 +506,7 @@ void main() {
506 children: const <TextSpan>[ 506 children: const <TextSpan>[
507 TextSpan( 507 TextSpan(
508 text: 508 text:
509 - 'القهوة مشروب يعد من بذور الب المحمصة، وينمو في أكثر من 70 لداً. خصوصاً في المناطق الاستوائية في أمريكا الشمالية والجنوبية وجنوب شرق آسيا وشبه القارة الهندية وأفريقيا. ويقال أن البن الأخضر هو ثاني أكثر السلع تداولاً في العالم بعد النفط الخام.', 509 + 'القهوة مشروب يعد من بذور الب المحمصة، وينمو في أكثر من 70 بلداً. خصوصاً في المناطق الاستوائية في أمريكا الشمالية والجنوبية وجنوب شرق آسيا وشبه القارة الهندية وأفريقيا. ويقال أن البن الأخضر هو ثاني أكثر السلع تداولاً في العالم بعد النفط الخام.',
510 style: TextStyle( 510 style: TextStyle(
511 fontSize: 20, 511 fontSize: 20,
512 ), 512 ),
@@ -517,8 +517,34 @@ void main() { @@ -517,8 +517,34 @@ void main() {
517 )); 517 ));
518 }); 518 });
519 519
520 - test(  
521 - 'Text Widgets, Mixed Arabic and Latin words should be rendered in order ', 520 + test('Text Widgets, Arabic Text with Tashkeel', () {
  521 + pdf.addPage(Page(
  522 + textDirection: TextDirection.rtl,
  523 + build: (Context context) => RichText(
  524 + text: TextSpan(
  525 + text: 'الفَرَاشَةُ\n',
  526 + style: TextStyle(
  527 + font: arabicFont,
  528 + fontSize: 30,
  529 + ),
  530 + children: const <TextSpan>[
  531 + if (true)
  532 + TextSpan(
  533 + text:
  534 + 'فَرَاشَةٌ مُلَوَّنَةٌ تَطِيْرُ في البُسْتَانِ، حُلْوَةٌ مُهَنْدَمَةٌ تُدْهِشُ الإِنْسَانَ، أَهْدَافُهَا مُحَدَّدَةٌ، حَرَكَاتُها مُرَتَّبَةٌ، تَحُوْمُ بِانْتِظَامٍ،'
  535 + ' تَحُطُّ فِي نُعُومَةٍ تَنْشُرُ السَّلاَمَ. فَرَاشَةٌ مُلَوَّنَةٌ تَطِيرُ بِلا اُنْقِطَاعٍ، بِالنَّهارِ المُشْرِقِ تَمْلَأُ البِقَاعَ، تُحِبُّ الوَرْدَ '
  536 + 'المَزْرُوعَ، تَلْثُمُهُ فِي وَقْتِ الجُوعِ، تَمْتَصُّ رَحِيْقَ الأَزْهَارِ، تُحْيي جَنْيَ الأَشْجَارِ، مِنْ وَرْدَةٍ لِوَرْدَةٍ، تَطِيْرُ بِانْتِظَامٍ.',
  537 + style: TextStyle(
  538 + fontSize: 18,
  539 + ),
  540 + ),
  541 + ],
  542 + ),
  543 + ),
  544 + ));
  545 + });
  546 +
  547 + test('Text Widgets, Mixed Arabic and Latin words should be rendered in order',
522 () { 548 () {
523 pdf.addPage(Page( 549 pdf.addPage(Page(
524 textDirection: TextDirection.rtl, 550 textDirection: TextDirection.rtl,
@@ -530,11 +556,11 @@ void main() { @@ -530,11 +556,11 @@ void main() {
530 fontSize: 30, 556 fontSize: 30,
531 ), 557 ),
532 children: const <TextSpan>[ 558 children: const <TextSpan>[
  559 + if (true)
533 TextSpan( 560 TextSpan(
534 text: r''' 561 text: r'''
535 الكلمات اللاتينية المضافة إلى نص عربي يجب أن توضع في الترتيب الصحيح Right Order مهما كان موضعها في النص. 562 الكلمات اللاتينية المضافة إلى نص عربي يجب أن توضع في الترتيب الصحيح Right Order مهما كان موضعها في النص.
536 -At the Beginning of the sentence في بداية الجملة  
537 -أو في منتصفها In the middle of the sentence حيث يكون بعدها كلام عربي 563 + في منتصفها In the middle of the sentence حيث يكون بعدها كلام عربي
538 أو في نهاية النص At the end of the sentence 564 أو في نهاية النص At the end of the sentence
539 أيضا ترتيب الأرقام والرموز يجب 1 أن 2 يكون 3 صحيحاً$. 565 أيضا ترتيب الأرقام والرموز يجب 1 أن 2 يكون 3 صحيحاً$.
540 ولا ننسى أيضا فواصل السطور Line breakers حيث وجودها في موضعها الصحيح مهم جدا في النصوص ثنائية الاتجاه Bidirectional 566 ولا ننسى أيضا فواصل السطور Line breakers حيث وجودها في موضعها الصحيح مهم جدا في النصوص ثنائية الاتجاه Bidirectional
@@ -549,6 +575,34 @@ At the Beginning of the sentence في بداية الجملة @@ -549,6 +575,34 @@ At the Beginning of the sentence في بداية الجملة
549 )); 575 ));
550 }); 576 });
551 577
  578 + test('Text Widgets Arabic with weak/natural Chars', () {
  579 + pdf.addPage(Page(
  580 + build: (Context context) => SizedBox.expand(
  581 + child: RichText(
  582 + textDirection: TextDirection.rtl,
  583 + text: TextSpan(
  584 + style: TextStyle(
  585 + font: arabicFont,
  586 + fontSize: 18,
  587 + ),
  588 + children: const <TextSpan>[
  589 + TextSpan(
  590 + text: '''
  591 +اضف العدد (5) إلى العدد (40)
  592 +ثم اطرح منه العدد (10)
  593 +الناتج = 35
  594 +جملة (if) الشرطية:
  595 +كلمة عربية (بين قوسين)
  596 +حاصل العملية (5 * 2) + (2 - 4) يساوي 12
  597 +العدد 9 > 5 والعدد 9 < 10
  598 + ''',
  599 + ),
  600 + ],
  601 + ),
  602 + )),
  603 + ));
  604 + });
  605 +
552 tearDownAll(() async { 606 tearDownAll(() async {
553 final file = File('arabic.pdf'); 607 final file = File('arabic.pdf');
554 await file.writeAsBytes(await pdf.save()); 608 await file.writeAsBytes(await pdf.save());
No preview for this file type