Showing
9 changed files
with
914 additions
and
2 deletions
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | DART_SRC=$(shell find . -name '*.dart') | 15 | DART_SRC=$(shell find . -name '*.dart') |
16 | CLNG_SRC=$(shell find printing/ios -name '*.java' -o -name '*.m' -o -name '*.h') $(shell find printing/android -name '*.java' -o -name '*.m' -o -name '*.h') | 16 | CLNG_SRC=$(shell find printing/ios -name '*.java' -o -name '*.m' -o -name '*.h') $(shell find printing/android -name '*.java' -o -name '*.m' -o -name '*.h') |
17 | SWFT_SRC=$(shell find . -name '*.swift') | 17 | SWFT_SRC=$(shell find . -name '*.swift') |
18 | - FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf | 18 | + FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf demo/assets/roboto1.ttf demo/assets/roboto2.ttf demo/assets/roboto3.ttf demo/assets/open-sans.ttf demo/assets/open-sans-bold.ttf pdf/hacen-tunisia.ttf |
19 | COV_PORT=9292 | 19 | COV_PORT=9292 |
20 | 20 | ||
21 | all: $(FONTS) demo/assets/logo.png demo/assets/profile.jpg format printing/example/.metadata get | 21 | all: $(FONTS) demo/assets/logo.png demo/assets/profile.jpg format printing/example/.metadata get |
@@ -57,6 +57,9 @@ demo/assets/logo.png: | @@ -57,6 +57,9 @@ demo/assets/logo.png: | ||
57 | demo/assets/profile.jpg: | 57 | demo/assets/profile.jpg: |
58 | curl -L "https://www.fakepersongenerator.com/Face/female/female20151024334209870.jpg" > $@ | 58 | curl -L "https://www.fakepersongenerator.com/Face/female/female20151024334209870.jpg" > $@ |
59 | 59 | ||
60 | +pdf/hacen-tunisia.ttf: | ||
61 | + curl -L "https://arbfonts.com/font_files/hacen/Hacen Tunisia.ttf" > $@ | ||
62 | + | ||
60 | format: format-dart format-clang format-swift | 63 | format: format-dart format-clang format-swift |
61 | 64 | ||
62 | format-dart: $(DART_SRC) | 65 | format-dart: $(DART_SRC) |
@@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
5 | - Allow MultiPage to relayout individual pages with support for flex | 5 | - Allow MultiPage to relayout individual pages with support for flex |
6 | - Implement BoxShadow for rect and circle BoxDecorations | 6 | - Implement BoxShadow for rect and circle BoxDecorations |
7 | - Implement TextStyle.letterSpacing | 7 | - Implement TextStyle.letterSpacing |
8 | +- Implement Arabic writing support [Anas Altair] | ||
8 | 9 | ||
9 | ## 1.8.1 | 10 | ## 1.8.1 |
10 | 11 |
@@ -33,6 +33,7 @@ import 'io/interface.dart' | @@ -33,6 +33,7 @@ import 'io/interface.dart' | ||
33 | if (dart.library.js) 'io/js.dart'; | 33 | if (dart.library.js) 'io/js.dart'; |
34 | 34 | ||
35 | part 'src/annotation.dart'; | 35 | part 'src/annotation.dart'; |
36 | +part 'src/arabic.dart'; | ||
36 | part 'src/array.dart'; | 37 | part 'src/array.dart'; |
37 | part 'src/ascii85.dart'; | 38 | part 'src/ascii85.dart'; |
38 | part 'src/border.dart'; | 39 | part 'src/border.dart'; |
pdf/lib/src/arabic.dart
0 → 100644
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 | +// ignore_for_file: omit_local_variable_types | ||
18 | +// https://github.com/agawish/Better-Arabic-Reshaper/tree/master/src/org/amr/arabic | ||
19 | +// http://mpcabd.xyz/python-arabic-text-reshaper/ | ||
20 | +// https://github.com/Georeactor/alif-toolkit/tree/master/src | ||
21 | + | ||
22 | +part of pdf; | ||
23 | + | ||
24 | +class PdfArabic { | ||
25 | + /// Arabic shape substitutions: char code => (isolated, final, initial, medial). | ||
26 | + /// Arabic Substition A | ||
27 | + static const Map<int, dynamic> _arabicSubstitionA = <int, dynamic>{ | ||
28 | +// 0x0652: <int>[0xFE7E, 0xFE7F], | ||
29 | + | ||
30 | + 0x0621: <int>[0xFE80], // ARABIC LETTER HAMZA | ||
31 | + 0x0622: <int>[0xFE81, 0xFE82], // ARABIC LETTER ALEF WITH MADDA ABOVE | ||
32 | + 0x0623: <int>[0xFE83, 0xFE84], // ARABIC LETTER ALEF WITH HAMZA ABOVE | ||
33 | + 0x0624: <int>[0xFE85, 0xFE86], // ARABIC LETTER WAW WITH HAMZA ABOVE | ||
34 | + 0x0625: <int>[0xFE87, 0xFE88], // ARABIC LETTER ALEF WITH HAMZA BELOW | ||
35 | + 0x0626: <int>[ | ||
36 | + 0xFE89, | ||
37 | + 0xFE8A, | ||
38 | + 0xFE8B, | ||
39 | + 0xFE8C | ||
40 | + ], // ARABIC LETTER YEH WITH HAMZA ABOVE | ||
41 | + 0x0627: <int>[0xFE8D, 0xFE8E], // ARABIC LETTER ALEF | ||
42 | + 0x0628: <int>[0xFE8F, 0xFE90, 0xFE91, 0xFE92], // ARABIC LETTER BEH | ||
43 | + 0x0629: <int>[0xFE93, 0xFE94], // ARABIC LETTER TEH MARBUTA | ||
44 | + 0x062A: <int>[0xFE95, 0xFE96, 0xFE97, 0xFE98], // ARABIC LETTER TEH | ||
45 | + 0x062B: <int>[0xFE99, 0xFE9A, 0xFE9B, 0xFE9C], // ARABIC LETTER THEH | ||
46 | + 0x062C: <int>[0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0], // ARABIC LETTER JEEM | ||
47 | + 0x062D: <int>[0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4], // ARABIC LETTER HAH | ||
48 | + 0x062E: <int>[0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8], // ARABIC LETTER KHAH | ||
49 | + 0x062F: <int>[0xFEA9, 0xFEAA], // ARABIC LETTER DAL | ||
50 | + 0x0630: <int>[0xFEAB, 0xFEAC], // ARABIC LETTER THAL | ||
51 | + 0x0631: <int>[0xFEAD, 0xFEAE], // ARABIC LETTER REH | ||
52 | + 0x0632: <int>[0xFEAF, 0xFEB0], // ARABIC LETTER ZAIN | ||
53 | + 0x0633: <int>[0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4], // ARABIC LETTER SEEN | ||
54 | + 0x0634: <int>[0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8], // ARABIC LETTER SHEEN | ||
55 | + 0x0635: <int>[0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC], // ARABIC LETTER SAD | ||
56 | + 0x0636: <int>[0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0], // ARABIC LETTER DAD | ||
57 | + 0x0637: <int>[0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4], // ARABIC LETTER TAH | ||
58 | + 0x0638: <int>[0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8], // ARABIC LETTER ZAH | ||
59 | + 0x0639: <int>[0xFEC9, 0xFECA, 0xFECB, 0xFECC], // ARABIC LETTER AIN | ||
60 | + 0x063A: <int>[0xFECD, 0xFECE, 0xFECF, 0xFED0], // ARABIC LETTER GHAIN | ||
61 | + 0x0641: <int>[0xFED1, 0xFED2, 0xFED3, 0xFED4], // ARABIC LETTER FEH | ||
62 | + 0x0642: <int>[0xFED5, 0xFED6, 0xFED7, 0xFED8], // ARABIC LETTER QAF | ||
63 | + 0x0643: <int>[0xFED9, 0xFEDA, 0xFEDB, 0xFEDC], // ARABIC LETTER KAF | ||
64 | + 0x0644: <int>[0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0], // ARABIC LETTER LAM | ||
65 | + 0x0645: <int>[0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4], // ARABIC LETTER MEEM | ||
66 | + 0x0646: <int>[0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8], // ARABIC LETTER NOON | ||
67 | + 0x0647: <int>[0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC], // ARABIC LETTER HEH | ||
68 | + 0x0648: <int>[0xFEED, 0xFEEE], // ARABIC LETTER WAW | ||
69 | + 0x0649: <int>[0xFEEF, 0xFEF0, 64488, 64489], // ARABIC LETTER ALEF MAKSURA | ||
70 | + 0x064A: <int>[0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4], // ARABIC LETTER YEH | ||
71 | + 0x0671: <int>[0xFB50, 0xFB51], // ARABIC LETTER ALEF WASLA | ||
72 | + 0x0677: <int>[0xFBDD], // ARABIC LETTER U WITH HAMZA ABOVE | ||
73 | + 0x0679: <int>[0xFB66, 0xFB67, 0xFB68, 0xFB69], // ARABIC LETTER TTEH | ||
74 | + 0x067A: <int>[0xFB5E, 0xFB5F, 0xFB60, 0xFB61], // ARABIC LETTER TTEHEH | ||
75 | + 0x067B: <int>[0xFB52, 0xFB53, 0xFB54, 0xFB55], // ARABIC LETTER BEEH | ||
76 | + 0x067E: <int>[0xFB56, 0xFB57, 0xFB58, 0xFB59], // ARABIC LETTER PEH | ||
77 | + 0x067F: <int>[0xFB62, 0xFB63, 0xFB64, 0xFB65], // ARABIC LETTER TEHEH | ||
78 | + 0x0680: <int>[0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D], // ARABIC LETTER BEHEH | ||
79 | + 0x0683: <int>[0xFB76, 0xFB77, 0xFB78, 0xFB79], // ARABIC LETTER NYEH | ||
80 | + 0x0684: <int>[0xFB72, 0xFB73, 0xFB74, 0xFB75], // ARABIC LETTER DYEH | ||
81 | + 0x0686: <int>[0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D], // ARABIC LETTER TCHEH | ||
82 | + 0x0687: <int>[0xFB7E, 0xFB7F, 0xFB80, 0xFB81], // ARABIC LETTER TCHEHEH | ||
83 | + 0x0688: <int>[0xFB88, 0xFB89], // ARABIC LETTER DDAL | ||
84 | + 0x068C: <int>[0xFB84, 0xFB85], // ARABIC LETTER DAHAL | ||
85 | + 0x068D: <int>[0xFB82, 0xFB83], // ARABIC LETTER DDAHAL | ||
86 | + 0x068E: <int>[0xFB86, 0xFB87], // ARABIC LETTER DUL | ||
87 | + 0x0691: <int>[0xFB8C, 0xFB8D], // ARABIC LETTER RREH | ||
88 | + 0x0698: <int>[0xFB8A, 0xFB8B], // ARABIC LETTER JEH | ||
89 | + 0x06A4: <int>[0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D], // ARABIC LETTER VEH | ||
90 | + 0x06A6: <int>[0xFB6E, 0xFB6F, 0xFB70, 0xFB71], // ARABIC LETTER PEHEH | ||
91 | + 0x06A9: <int>[0xFB8E, 0xFB8F, 0xFB90, 0xFB91], // ARABIC LETTER KEHEH | ||
92 | + 0x06AD: <int>[0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6], // ARABIC LETTER NG | ||
93 | + 0x06AF: <int>[0xFB92, 0xFB93, 0xFB94, 0xFB95], // ARABIC LETTER GAF | ||
94 | + 0x06B1: <int>[0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D], // ARABIC LETTER NGOEH | ||
95 | + 0x06B3: <int>[0xFB96, 0xFB97, 0xFB98, 0xFB99], // ARABIC LETTER GUEH | ||
96 | + 0x06BA: <int>[0xFB9E, 0xFB9F], // ARABIC LETTER NOON GHUNNA | ||
97 | + 0x06BB: <int>[0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3], // ARABIC LETTER RNOON | ||
98 | + 0x06BE: <int>[ | ||
99 | + 0xFBAA, | ||
100 | + 0xFBAB, | ||
101 | + 0xFBAC, | ||
102 | + 0xFBAD | ||
103 | + ], // ARABIC LETTER HEH DOACHASHMEE | ||
104 | + 0x06C0: <int>[0xFBA4, 0xFBA5], // ARABIC LETTER HEH WITH YEH ABOVE | ||
105 | + 0x06C1: <int>[0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9], // ARABIC LETTER HEH GOAL | ||
106 | + 0x06C5: <int>[0xFBE0, 0xFBE1], // ARABIC LETTER KIRGHIZ OE | ||
107 | + 0x06C6: <int>[0xFBD9, 0xFBDA], // ARABIC LETTER OE | ||
108 | + 0x06C7: <int>[0xFBD7, 0xFBD8], // ARABIC LETTER U | ||
109 | + 0x06C8: <int>[0xFBDB, 0xFBDC], // ARABIC LETTER YU | ||
110 | + 0x06C9: <int>[0xFBE2, 0xFBE3], // ARABIC LETTER KIRGHIZ YU | ||
111 | + 0x06CB: <int>[0xFBDE, 0xFBDF], // ARABIC LETTER VE | ||
112 | + 0x06CC: <int>[0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF], // ARABIC LETTER FARSI YEH | ||
113 | + 0x06D0: <int>[0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7], //ARABIC LETTER E | ||
114 | + 0x06D2: <int>[0xFBAE, 0xFBAF], // ARABIC LETTER YEH BARREE | ||
115 | + 0x06D3: <int>[0xFBB0, 0xFBB1], // ARABIC LETTER YEH BARREE WITH HAMZA ABOVE | ||
116 | + }; | ||
117 | + | ||
118 | + /* | ||
119 | + var ligaturesSubstitutionA = { | ||
120 | + 0xFBEA: []// ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM | ||
121 | + }; | ||
122 | + */ | ||
123 | + | ||
124 | + static const Map<int, dynamic> _diacriticLigatures = <int, dynamic>{ | ||
125 | + 0x0651: <int, int>{ | ||
126 | + 0x064C: 0xFC5E, // Shadda + Dammatan | ||
127 | + 0x064D: 0xFC5F, // Shadda + Kasratan | ||
128 | + 0x064E: 0xFC60, // Shadda + Fatha | ||
129 | + 0x064F: 0xFC61, // Shadda + Damma | ||
130 | + 0x0650: 0xFC62, // Shadda + Kasra | ||
131 | + }, | ||
132 | + }; | ||
133 | + | ||
134 | + static const Map<int, dynamic> _ligatures = <int, dynamic>{ | ||
135 | + 0xFEDF: <int, int>{ | ||
136 | + 0xFE82: | ||
137 | + 0xFEF5, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM | ||
138 | + 0xFE84: | ||
139 | + 0xFEF7, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM | ||
140 | + 0xFE88: | ||
141 | + 0xFEF9, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM | ||
142 | + 0xFE8E: 0xFEFB // ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM | ||
143 | + }, | ||
144 | + 0xFEE0: <int, int>{ | ||
145 | + 0xFE82: | ||
146 | + 0xFEF6, // ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM | ||
147 | + 0xFE84: | ||
148 | + 0xFEF8, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM | ||
149 | + 0xFE88: | ||
150 | + 0xFEFA, // ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM | ||
151 | + 0xFE8E: 0xFEFC // ARABIC LIGATURE LAM WITH ALEF FINAL FORM | ||
152 | + }, | ||
153 | +// 0xFE8D: <int, dynamic>{ | ||
154 | +// 0xFEDF: <int, dynamic>{ | ||
155 | +// 0xFEE0: <int, int>{0xFEEA: 0xFDF2} | ||
156 | +// } | ||
157 | +// }, // ALLAH | ||
158 | + }; | ||
159 | + | ||
160 | + static const List<int> _alfletter = <int>[1570, 1571, 1573, 1575]; | ||
161 | + | ||
162 | + static const Map<int, int> _arabicDiacritics = <int, int>{ | ||
163 | + 1611: 1611, // Fathatan | ||
164 | + 1612: 1612, // Dammatan | ||
165 | + 1613: 1613, // Kasratan | ||
166 | + 1614: 1614, // Fatha | ||
167 | + 1615: 1615, // Damma | ||
168 | + 1616: 1616, // Kasra | ||
169 | + 1617: 1617, | ||
170 | + 1618: 1618, | ||
171 | + | ||
172 | + 64606: 64606, // Shadda + Dammatan | ||
173 | + 64607: 64607, // Shadda + Kasratan | ||
174 | + 64608: 64608, // Shadda + Fatha | ||
175 | + 64609: 64609, // Shadda + Damma | ||
176 | + 64610: 64610, // Shadda + Kasra | ||
177 | + // 1548: 1548, | ||
178 | + }; | ||
179 | + | ||
180 | + static const int _noChangeInForm = -1; | ||
181 | + static const int _isolatedForm = 0; | ||
182 | + static const int _finalForm = 1; | ||
183 | + static const int _initialForm = 2; | ||
184 | + static const int _medialForm = 3; | ||
185 | + | ||
186 | + static bool _isInArabicSubstitutionA(int letter) { | ||
187 | + return _arabicSubstitionA.containsKey(letter); | ||
188 | + } | ||
189 | + | ||
190 | + static bool _isArabicLetter(int letter) { | ||
191 | + return (letter >= 0x0600 && letter <= 0x06FF) || | ||
192 | + (letter >= 0x0750 && letter <= 0x077F) || | ||
193 | + (letter >= 0x08FF && letter <= 0xFB50) || | ||
194 | + (letter >= 0xFDFF && letter <= 0xFEFF); | ||
195 | + } | ||
196 | + | ||
197 | + static bool _isArabicEndLetter(int letter) { | ||
198 | + return _isArabicLetter(letter) && | ||
199 | + _isInArabicSubstitutionA(letter) && | ||
200 | + _arabicSubstitionA[letter].length <= 2; | ||
201 | + } | ||
202 | + | ||
203 | + static bool _isArabicAlfLetter(int letter) { | ||
204 | + return _isArabicLetter(letter) && _alfletter.contains(letter); | ||
205 | + } | ||
206 | + | ||
207 | + static bool _arabicLetterHasFinalForm(int letter) { | ||
208 | + return _isArabicLetter(letter) && | ||
209 | + _isInArabicSubstitutionA(letter) && | ||
210 | + (_arabicSubstitionA[letter].length >= 2); | ||
211 | + } | ||
212 | + | ||
213 | + static bool _arabicLetterHasMedialForm(int letter) { | ||
214 | + return _isArabicLetter(letter) && | ||
215 | + _isInArabicSubstitutionA(letter) && | ||
216 | + _arabicSubstitionA[letter].length == 4; | ||
217 | + } | ||
218 | + | ||
219 | + static bool _isArabicDiacritic(int letter) { | ||
220 | + return _arabicDiacritics.containsKey(letter); | ||
221 | + } | ||
222 | + | ||
223 | + static bool _isArabicDiacriticValue(int letter) { | ||
224 | + return _arabicDiacritics.containsValue(letter); | ||
225 | + } | ||
226 | + | ||
227 | + static List<int> _resolveLigatures(List<int> lettersq) { | ||
228 | + final List<int> result = <int>[]; | ||
229 | + dynamic tmpLigatures = _ligatures; | ||
230 | + dynamic tmpDiacritic = _diacriticLigatures; | ||
231 | + final List<int> letters = lettersq.reversed.toList(); | ||
232 | + | ||
233 | + final List<int> effectedLetters = <int>[]; | ||
234 | + final List<int> effectedDiacritics = <int>[]; | ||
235 | + | ||
236 | + final List<int> finalDiacritics = <int>[]; | ||
237 | + | ||
238 | + for (int i = 0; i < letters.length; i++) { | ||
239 | + if (_isArabicDiacriticValue(letters[i])) { | ||
240 | + effectedDiacritics.insert(0, letters[i]); | ||
241 | + if (tmpDiacritic.containsKey(letters[i])) { | ||
242 | + tmpDiacritic = tmpDiacritic[letters[i]]; | ||
243 | + | ||
244 | + if (tmpDiacritic is int) { | ||
245 | + finalDiacritics.insert(0, tmpDiacritic); | ||
246 | + tmpDiacritic = _diacriticLigatures; | ||
247 | + effectedDiacritics.clear(); | ||
248 | + } | ||
249 | + } else { | ||
250 | + tmpDiacritic = _diacriticLigatures; | ||
251 | + | ||
252 | + // add all Diacritics if there is no letter Ligatures. | ||
253 | + if (effectedLetters.isEmpty) { | ||
254 | + result.insertAll(0, finalDiacritics); | ||
255 | + result.insertAll(0, effectedDiacritics); | ||
256 | + finalDiacritics.clear(); | ||
257 | + effectedDiacritics.clear(); | ||
258 | + } | ||
259 | + } | ||
260 | + } else if (tmpLigatures.containsKey(letters[i])) { | ||
261 | + effectedLetters.insert(0, letters[i]); | ||
262 | + tmpLigatures = tmpLigatures[letters[i]]; | ||
263 | + | ||
264 | + if (tmpLigatures is int) { | ||
265 | + result.insert(0, tmpLigatures); | ||
266 | + tmpLigatures = _ligatures; | ||
267 | + effectedLetters.clear(); | ||
268 | + } | ||
269 | + } else { | ||
270 | + tmpLigatures = _ligatures; | ||
271 | + | ||
272 | + // add effected letters if they aren't ligature. | ||
273 | + if (effectedLetters.isNotEmpty) { | ||
274 | + result.insertAll(0, effectedLetters); | ||
275 | + effectedLetters.clear(); | ||
276 | + } | ||
277 | + | ||
278 | + // add Diacritics after or before letter ligature. | ||
279 | + if (effectedLetters.isEmpty && effectedDiacritics.isNotEmpty) { | ||
280 | + result.insertAll(0, effectedDiacritics); | ||
281 | + effectedDiacritics.clear(); | ||
282 | + } | ||
283 | + | ||
284 | + result.insert(0, letters[i]); | ||
285 | + } | ||
286 | + | ||
287 | + // add Diacritic ligatures. | ||
288 | + if (effectedLetters.isEmpty && finalDiacritics.isNotEmpty) { | ||
289 | + result.insertAll(0, finalDiacritics); | ||
290 | + finalDiacritics.clear(); | ||
291 | + } | ||
292 | + } | ||
293 | + | ||
294 | + return result; | ||
295 | + } | ||
296 | + | ||
297 | + static int _getCorrectForm(int currentChar, int beforeChar, int nextChar) { | ||
298 | + if (_isInArabicSubstitutionA(currentChar) == false) { | ||
299 | + return _noChangeInForm; | ||
300 | + } | ||
301 | + if (!_arabicLetterHasFinalForm(currentChar) || | ||
302 | + (!_isArabicLetter(beforeChar) && !_isArabicLetter(nextChar)) || | ||
303 | + (!_isArabicLetter(nextChar) && _isArabicEndLetter(beforeChar)) || | ||
304 | + (_isArabicEndLetter(currentChar) && !_isArabicLetter(beforeChar)) || | ||
305 | + (_isArabicEndLetter(currentChar) && _isArabicAlfLetter(beforeChar)) || | ||
306 | + (_isArabicEndLetter(currentChar) && _isArabicEndLetter(beforeChar))) { | ||
307 | + return _isolatedForm; | ||
308 | + } | ||
309 | + | ||
310 | + if (_arabicLetterHasMedialForm(currentChar) && | ||
311 | + _isArabicLetter(beforeChar) && | ||
312 | + !_isArabicEndLetter(beforeChar) && | ||
313 | + _isArabicLetter(nextChar) && | ||
314 | + _arabicLetterHasFinalForm(nextChar)) { | ||
315 | + return _medialForm; | ||
316 | + } | ||
317 | + | ||
318 | + if (_isArabicEndLetter(currentChar) || (!_isArabicLetter(nextChar))) { | ||
319 | + return _finalForm; | ||
320 | + } | ||
321 | + return _initialForm; | ||
322 | + } | ||
323 | + | ||
324 | + static Iterable<String> _parse(String text) sync* { | ||
325 | + final List<String> words = text.split(' '); | ||
326 | + | ||
327 | + bool first = true; | ||
328 | + for (String word in words) { | ||
329 | + final List<int> newWord = <int>[]; | ||
330 | + bool isArabic = false; | ||
331 | + | ||
332 | + int prevLetter = 0; | ||
333 | + | ||
334 | + for (int j = 0; j < word.length; j += 1) { | ||
335 | + final int currentLetter = word.codeUnitAt(j); | ||
336 | + | ||
337 | + if (_isArabicDiacritic(currentLetter)) { | ||
338 | + newWord.insert(0, _arabicDiacritics[currentLetter]); | ||
339 | + continue; | ||
340 | + } | ||
341 | + final int nextLetter = word | ||
342 | + .split('') | ||
343 | + .skip(j + 1) | ||
344 | + .map((String e) => e.codeUnitAt(0)) | ||
345 | + .firstWhere( | ||
346 | + (int element) => !_isArabicDiacritic(element), | ||
347 | + orElse: () => 0, | ||
348 | + ); | ||
349 | + | ||
350 | + if (_isArabicLetter(currentLetter)) { | ||
351 | + isArabic = true; | ||
352 | + | ||
353 | + final int position = | ||
354 | + _getCorrectForm(currentLetter, prevLetter, nextLetter); | ||
355 | + if (position != -1) { | ||
356 | + newWord.insert(0, _arabicSubstitionA[currentLetter][position]); | ||
357 | + } else { | ||
358 | + newWord.add(currentLetter); | ||
359 | + } | ||
360 | + } else { | ||
361 | + if (isArabic && currentLetter > 32) { | ||
362 | + newWord.insert(0, currentLetter); | ||
363 | + } else { | ||
364 | + newWord.add(currentLetter); | ||
365 | + } | ||
366 | + } | ||
367 | + prevLetter = currentLetter; | ||
368 | + } | ||
369 | + | ||
370 | + if (!first) { | ||
371 | + yield ' '; | ||
372 | + } | ||
373 | + first = false; | ||
374 | + | ||
375 | + yield String.fromCharCodes(_resolveLigatures(newWord)); | ||
376 | + } | ||
377 | + } | ||
378 | + | ||
379 | + static String convert(String input) { | ||
380 | + return List<String>.from(_parse(input)).join(''); | ||
381 | + } | ||
382 | +} |
@@ -59,6 +59,11 @@ class PdfTtfFont extends PdfFont { | @@ -59,6 +59,11 @@ class PdfTtfFont extends PdfFont { | ||
59 | return PdfFontMetrics.zero; | 59 | return PdfFontMetrics.zero; |
60 | } | 60 | } |
61 | 61 | ||
62 | + if (PdfArabic._isArabicDiacriticValue(charCode)) { | ||
63 | + final PdfFontMetrics metric = font.glyphInfoMap[g] ?? PdfFontMetrics.zero; | ||
64 | + return metric.copyWith(advanceWidth: 0); | ||
65 | + } | ||
66 | + | ||
62 | return font.glyphInfoMap[g] ?? PdfFontMetrics.zero; | 67 | return font.glyphInfoMap[g] ?? PdfFontMetrics.zero; |
63 | } | 68 | } |
64 | 69 |
@@ -20,6 +20,8 @@ part of widget; | @@ -20,6 +20,8 @@ part of widget; | ||
20 | 20 | ||
21 | enum TextAlign { left, right, center, justify } | 21 | enum TextAlign { left, right, center, justify } |
22 | 22 | ||
23 | +enum TextDirection { ltr, rtl } | ||
24 | + | ||
23 | abstract class _Span { | 25 | abstract class _Span { |
24 | _Span(this.style); | 26 | _Span(this.style); |
25 | 27 | ||
@@ -443,12 +445,14 @@ class RichText extends Widget { | @@ -443,12 +445,14 @@ class RichText extends Widget { | ||
443 | RichText( | 445 | RichText( |
444 | {@required this.text, | 446 | {@required this.text, |
445 | TextAlign textAlign, | 447 | TextAlign textAlign, |
448 | + TextDirection textDirection, | ||
446 | bool softWrap, | 449 | bool softWrap, |
447 | this.tightBounds = false, | 450 | this.tightBounds = false, |
448 | this.textScaleFactor = 1.0, | 451 | this.textScaleFactor = 1.0, |
449 | int maxLines}) | 452 | int maxLines}) |
450 | : assert(text != null), | 453 | : assert(text != null), |
451 | _textAlign = textAlign, | 454 | _textAlign = textAlign, |
455 | + textDirection = textDirection ?? TextDirection.ltr, | ||
452 | _softWrap = softWrap, | 456 | _softWrap = softWrap, |
453 | _maxLines = maxLines; | 457 | _maxLines = maxLines; |
454 | 458 | ||
@@ -459,6 +463,8 @@ class RichText extends Widget { | @@ -459,6 +463,8 @@ class RichText extends Widget { | ||
459 | TextAlign get textAlign => _textAlign; | 463 | TextAlign get textAlign => _textAlign; |
460 | TextAlign _textAlign; | 464 | TextAlign _textAlign; |
461 | 465 | ||
466 | + final TextDirection textDirection; | ||
467 | + | ||
462 | final double textScaleFactor; | 468 | final double textScaleFactor; |
463 | 469 | ||
464 | bool get softWrap => _softWrap; | 470 | bool get softWrap => _softWrap; |
@@ -505,6 +511,17 @@ class RichText extends Widget { | @@ -505,6 +511,17 @@ class RichText extends Widget { | ||
505 | return totalWidth; | 511 | return totalWidth; |
506 | } | 512 | } |
507 | 513 | ||
514 | + if (textDirection == TextDirection.rtl) { | ||
515 | + for (_Span span in spans) { | ||
516 | + span.offset = PdfPoint( | ||
517 | + totalWidth - (span.offset.x + span.width) - delta, | ||
518 | + span.offset.y - baseline, | ||
519 | + ); | ||
520 | + } | ||
521 | + | ||
522 | + return totalWidth; | ||
523 | + } | ||
524 | + | ||
508 | for (_Span span in spans) { | 525 | for (_Span span in spans) { |
509 | span.offset = span.offset.translate(delta, -baseline); | 526 | span.offset = span.offset.translate(delta, -baseline); |
510 | } | 527 | } |
@@ -566,7 +583,11 @@ class RichText extends Widget { | @@ -566,7 +583,11 @@ class RichText extends Widget { | ||
566 | final PdfFontMetrics space = | 583 | final PdfFontMetrics space = |
567 | font.stringMetrics(' ') * (style.fontSize * textScaleFactor); | 584 | font.stringMetrics(' ') * (style.fontSize * textScaleFactor); |
568 | 585 | ||
569 | - final List<String> spanLines = span.text.split('\n'); | 586 | + final List<String> spanLines = (textDirection == TextDirection.rtl |
587 | + ? PdfArabic.convert(span.text) | ||
588 | + : span.text) | ||
589 | + .split('\n'); | ||
590 | + | ||
570 | for (int line = 0; line < spanLines.length; line++) { | 591 | for (int line = 0; line < spanLines.length; line++) { |
571 | for (String word in spanLines[line].split(RegExp(r'\s'))) { | 592 | for (String word in spanLines[line].split(RegExp(r'\s'))) { |
572 | if (word.isEmpty) { | 593 | if (word.isEmpty) { |
@@ -846,6 +867,7 @@ class Text extends RichText { | @@ -846,6 +867,7 @@ class Text extends RichText { | ||
846 | String text, { | 867 | String text, { |
847 | TextStyle style, | 868 | TextStyle style, |
848 | TextAlign textAlign, | 869 | TextAlign textAlign, |
870 | + TextDirection textDirection, | ||
849 | bool softWrap, | 871 | bool softWrap, |
850 | bool tightBounds = false, | 872 | bool tightBounds = false, |
851 | double textScaleFactor = 1.0, | 873 | double textScaleFactor = 1.0, |
@@ -856,6 +878,7 @@ class Text extends RichText { | @@ -856,6 +878,7 @@ class Text extends RichText { | ||
856 | textAlign: textAlign, | 878 | textAlign: textAlign, |
857 | softWrap: softWrap, | 879 | softWrap: softWrap, |
858 | tightBounds: tightBounds, | 880 | tightBounds: tightBounds, |
881 | + textDirection: textDirection, | ||
859 | textScaleFactor: textScaleFactor, | 882 | textScaleFactor: textScaleFactor, |
860 | maxLines: maxLines); | 883 | maxLines: maxLines); |
861 | } | 884 | } |
@@ -18,6 +18,7 @@ library pdf.all_tests; | @@ -18,6 +18,7 @@ library pdf.all_tests; | ||
18 | 18 | ||
19 | import '../example/main.dart' as example; | 19 | import '../example/main.dart' as example; |
20 | import 'annotations_test.dart' as annotations; | 20 | import 'annotations_test.dart' as annotations; |
21 | +import 'arabic_test.dart' as arabic; | ||
21 | import 'colors_test.dart' as colors; | 22 | import 'colors_test.dart' as colors; |
22 | import 'complex_test.dart' as complex; | 23 | import 'complex_test.dart' as complex; |
23 | import 'data_types_test.dart' as data_types; | 24 | import 'data_types_test.dart' as data_types; |
@@ -48,6 +49,7 @@ import 'widget_wrap_test.dart' as widget_wrap; | @@ -48,6 +49,7 @@ import 'widget_wrap_test.dart' as widget_wrap; | ||
48 | 49 | ||
49 | void main() { | 50 | void main() { |
50 | annotations.main(); | 51 | annotations.main(); |
52 | + arabic.main(); | ||
51 | colors.main(); | 53 | colors.main(); |
52 | complex.main(); | 54 | complex.main(); |
53 | data_types.main(); | 55 | data_types.main(); |
pdf/test/arabic_test.dart
0 → 100644
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 | +// ignore_for_file: omit_local_variable_types | ||
18 | + | ||
19 | +import 'dart:io'; | ||
20 | + | ||
21 | +import 'package:pdf/pdf.dart'; | ||
22 | +import 'package:pdf/widgets.dart'; | ||
23 | +import 'package:test/test.dart'; | ||
24 | + | ||
25 | +import 'utils.dart'; | ||
26 | + | ||
27 | +class ArabicText { | ||
28 | + ArabicText(this.original, this.reshaped); | ||
29 | + | ||
30 | + final String original; | ||
31 | + final List<int> reshaped; | ||
32 | + | ||
33 | + String get originalRev => original.split('').reversed.join(''); | ||
34 | +} | ||
35 | + | ||
36 | +Document pdf; | ||
37 | +Font arabicFont; | ||
38 | +TextStyle style; | ||
39 | + | ||
40 | +void main() { | ||
41 | + setUpAll(() { | ||
42 | + Document.debug = false; | ||
43 | + RichText.debug = true; | ||
44 | + pdf = Document(); | ||
45 | + | ||
46 | + arabicFont = loadFont('hacen-tunisia.ttf'); | ||
47 | + style = TextStyle(font: arabicFont, fontSize: 30); | ||
48 | + }); | ||
49 | + | ||
50 | + test('Arabic Diacritics', () { | ||
51 | + final ArabicText a = | ||
52 | + ArabicText('السلام', <int>[65249, 65276, 65204, 65247, 65165]); | ||
53 | + final ArabicText b = ArabicText('السَلَاْمٌ', | ||
54 | + <int>[1612, 65249, 1618, 1614, 65276, 1614, 65204, 65247, 65165]); | ||
55 | + | ||
56 | + expect( | ||
57 | + PdfArabic.convert(a.original).codeUnits, | ||
58 | + equals(a.reshaped), | ||
59 | + ); | ||
60 | + expect( | ||
61 | + PdfArabic.convert(b.original).codeUnits, | ||
62 | + equals(b.reshaped), | ||
63 | + ); | ||
64 | + }); | ||
65 | + | ||
66 | + test('Arabic Default Reshaping', () { | ||
67 | + final List<ArabicText> cases = <ArabicText>[ | ||
68 | + ArabicText('السَلاْمُ عَلَيْكُمْ', <int>[ | ||
69 | + 1615, | ||
70 | + 65249, | ||
71 | + 1618, | ||
72 | + 65276, | ||
73 | + 1614, | ||
74 | + 65204, | ||
75 | + 65247, | ||
76 | + 65165, | ||
77 | + 32, | ||
78 | + 1618, | ||
79 | + 65250, | ||
80 | + 1615, | ||
81 | + 65244, | ||
82 | + 1618, | ||
83 | + 65268, | ||
84 | + 1614, | ||
85 | + 65248, | ||
86 | + 1614, | ||
87 | + 65227 | ||
88 | + ]), | ||
89 | + ArabicText('اللغة العربيَّة هي أكثرُ اللغاتِ', <int>[ | ||
90 | + 65172, | ||
91 | + 65232, | ||
92 | + 65248, | ||
93 | + 65247, | ||
94 | + 65165, | ||
95 | + 32, | ||
96 | + 65172, | ||
97 | + 64608, | ||
98 | + 65268, | ||
99 | + 65169, | ||
100 | + 65198, | ||
101 | + 65228, | ||
102 | + 65247, | ||
103 | + 65165, | ||
104 | + 32, | ||
105 | + 65266, | ||
106 | + 65259, | ||
107 | + 32, | ||
108 | + 1615, | ||
109 | + 65198, | ||
110 | + 65180, | ||
111 | + 65243, | ||
112 | + 65155, | ||
113 | + 32, | ||
114 | + 1616, | ||
115 | + 65173, | ||
116 | + 65166, | ||
117 | + 65232, | ||
118 | + 65248, | ||
119 | + 65247, | ||
120 | + 65165 | ||
121 | + ]), | ||
122 | + ArabicText('تحدُّثاً ونُطقاً ضِمْنَ مَجمُوعَة', <int>[ | ||
123 | + 1611, | ||
124 | + 65166, | ||
125 | + 65179, | ||
126 | + 64609, | ||
127 | + 65194, | ||
128 | + 65188, | ||
129 | + 65175, | ||
130 | + 32, | ||
131 | + 1611, | ||
132 | + 65166, | ||
133 | + 65240, | ||
134 | + 65220, | ||
135 | + 1615, | ||
136 | + 65255, | ||
137 | + 65261, | ||
138 | + 32, | ||
139 | + 1614, | ||
140 | + 65254, | ||
141 | + 1618, | ||
142 | + 65252, | ||
143 | + 1616, | ||
144 | + 65215, | ||
145 | + 32, | ||
146 | + 65172, | ||
147 | + 1614, | ||
148 | + 65227, | ||
149 | + 65262, | ||
150 | + 1615, | ||
151 | + 65252, | ||
152 | + 65184, | ||
153 | + 1614, | ||
154 | + 65251 | ||
155 | + ]), | ||
156 | + ArabicText('اللغات السامية', <int>[ | ||
157 | + 65173, | ||
158 | + 65166, | ||
159 | + 65232, | ||
160 | + 65248, | ||
161 | + 65247, | ||
162 | + 65165, | ||
163 | + 32, | ||
164 | + 65172, | ||
165 | + 65268, | ||
166 | + 65251, | ||
167 | + 65166, | ||
168 | + 65204, | ||
169 | + 65247, | ||
170 | + 65165 | ||
171 | + ]), | ||
172 | + ArabicText('العربية لغةٌ رسميةٌ في', <int>[ | ||
173 | + 65172, | ||
174 | + 65268, | ||
175 | + 65169, | ||
176 | + 65198, | ||
177 | + 65228, | ||
178 | + 65247, | ||
179 | + 65165, | ||
180 | + 32, | ||
181 | + 1612, | ||
182 | + 65172, | ||
183 | + 65232, | ||
184 | + 65247, | ||
185 | + 32, | ||
186 | + 1612, | ||
187 | + 65172, | ||
188 | + 65268, | ||
189 | + 65252, | ||
190 | + 65203, | ||
191 | + 65197, | ||
192 | + 32, | ||
193 | + 65266, | ||
194 | + 65235 | ||
195 | + ]), | ||
196 | + ArabicText('كلِّ دولِ الوطنِ العربيِّ', <int>[ | ||
197 | + 64610, | ||
198 | + 65246, | ||
199 | + 65243, | ||
200 | + 32, | ||
201 | + 1616, | ||
202 | + 65245, | ||
203 | + 65261, | ||
204 | + 65193, | ||
205 | + 32, | ||
206 | + 1616, | ||
207 | + 65254, | ||
208 | + 65219, | ||
209 | + 65262, | ||
210 | + 65247, | ||
211 | + 65165, | ||
212 | + 32, | ||
213 | + 64610, | ||
214 | + 65266, | ||
215 | + 65169, | ||
216 | + 65198, | ||
217 | + 65228, | ||
218 | + 65247, | ||
219 | + 65165 | ||
220 | + ]), | ||
221 | + ArabicText('إضافة إلى كونها لغة', <int>[ | ||
222 | + 65172, | ||
223 | + 65235, | ||
224 | + 65166, | ||
225 | + 65215, | ||
226 | + 65159, | ||
227 | + 32, | ||
228 | + 65264, | ||
229 | + 65247, | ||
230 | + 65159, | ||
231 | + 32, | ||
232 | + 65166, | ||
233 | + 65260, | ||
234 | + 65255, | ||
235 | + 65262, | ||
236 | + 65243, | ||
237 | + 32, | ||
238 | + 65172, | ||
239 | + 65232, | ||
240 | + 65247 | ||
241 | + ]), | ||
242 | + ArabicText('رسمية في تشاد وإريتريا', <int>[ | ||
243 | + 65172, | ||
244 | + 65268, | ||
245 | + 65252, | ||
246 | + 65203, | ||
247 | + 65197, | ||
248 | + 32, | ||
249 | + 65266, | ||
250 | + 65235, | ||
251 | + 32, | ||
252 | + 65193, | ||
253 | + 65166, | ||
254 | + 65208, | ||
255 | + 65175, | ||
256 | + 32, | ||
257 | + 65166, | ||
258 | + 65267, | ||
259 | + 65198, | ||
260 | + 65176, | ||
261 | + 65267, | ||
262 | + 65197, | ||
263 | + 65159, | ||
264 | + 65261 | ||
265 | + ]), | ||
266 | + ArabicText('وإسرائيل. وهي إحدى اللغات', <int>[ | ||
267 | + 46, | ||
268 | + 65246, | ||
269 | + 65268, | ||
270 | + 65163, | ||
271 | + 65165, | ||
272 | + 65198, | ||
273 | + 65203, | ||
274 | + 65159, | ||
275 | + 65261, | ||
276 | + 32, | ||
277 | + 65266, | ||
278 | + 65259, | ||
279 | + 65261, | ||
280 | + 32, | ||
281 | + 65263, | ||
282 | + 65194, | ||
283 | + 65187, | ||
284 | + 65159, | ||
285 | + 32, | ||
286 | + 65173, | ||
287 | + 65166, | ||
288 | + 65232, | ||
289 | + 65248, | ||
290 | + 65247, | ||
291 | + 65165 | ||
292 | + ]), | ||
293 | + ArabicText('الرسمية الست في منظمة', <int>[ | ||
294 | + 65172, | ||
295 | + 65268, | ||
296 | + 65252, | ||
297 | + 65203, | ||
298 | + 65198, | ||
299 | + 65247, | ||
300 | + 65165, | ||
301 | + 32, | ||
302 | + 65174, | ||
303 | + 65204, | ||
304 | + 65247, | ||
305 | + 65165, | ||
306 | + 32, | ||
307 | + 65266, | ||
308 | + 65235, | ||
309 | + 32, | ||
310 | + 65172, | ||
311 | + 65252, | ||
312 | + 65224, | ||
313 | + 65256, | ||
314 | + 65251 | ||
315 | + ]), | ||
316 | + ArabicText('الأمم المتحدة، ويُحتفل', <int>[ | ||
317 | + 65250, | ||
318 | + 65251, | ||
319 | + 65271, | ||
320 | + 65165, | ||
321 | + 32, | ||
322 | + 65171, | ||
323 | + 65194, | ||
324 | + 65188, | ||
325 | + 65176, | ||
326 | + 65252, | ||
327 | + 65247, | ||
328 | + 65165, | ||
329 | + 1548, | ||
330 | + 32, | ||
331 | + 65246, | ||
332 | + 65236, | ||
333 | + 65176, | ||
334 | + 65188, | ||
335 | + 1615, | ||
336 | + 65267, | ||
337 | + 65261 | ||
338 | + ]), | ||
339 | + ArabicText('باليوم العالمي للغة العربية', <int>[ | ||
340 | + 65249, | ||
341 | + 65262, | ||
342 | + 65268, | ||
343 | + 65247, | ||
344 | + 65166, | ||
345 | + 65169, | ||
346 | + 32, | ||
347 | + 65266, | ||
348 | + 65252, | ||
349 | + 65247, | ||
350 | + 65166, | ||
351 | + 65228, | ||
352 | + 65247, | ||
353 | + 65165, | ||
354 | + 32, | ||
355 | + 65172, | ||
356 | + 65232, | ||
357 | + 65248, | ||
358 | + 65247, | ||
359 | + 32, | ||
360 | + 65172, | ||
361 | + 65268, | ||
362 | + 65169, | ||
363 | + 65198, | ||
364 | + 65228, | ||
365 | + 65247, | ||
366 | + 65165 | ||
367 | + ]), | ||
368 | + ArabicText('في 18 ديسمبر كذكرى اعتماد', <int>[ | ||
369 | + 65266, | ||
370 | + 65235, | ||
371 | + 32, | ||
372 | + 49, | ||
373 | + 56, | ||
374 | + 32, | ||
375 | + 65198, | ||
376 | + 65170, | ||
377 | + 65252, | ||
378 | + 65204, | ||
379 | + 65267, | ||
380 | + 65193, | ||
381 | + 32, | ||
382 | + 65263, | ||
383 | + 65198, | ||
384 | + 65243, | ||
385 | + 65196, | ||
386 | + 65243, | ||
387 | + 32, | ||
388 | + 65193, | ||
389 | + 65166, | ||
390 | + 65252, | ||
391 | + 65176, | ||
392 | + 65227, | ||
393 | + 65165 | ||
394 | + ]), | ||
395 | + ArabicText('العربية بين لغات العمل في', <int>[ | ||
396 | + 65172, | ||
397 | + 65268, | ||
398 | + 65169, | ||
399 | + 65198, | ||
400 | + 65228, | ||
401 | + 65247, | ||
402 | + 65165, | ||
403 | + 32, | ||
404 | + 65254, | ||
405 | + 65268, | ||
406 | + 65169, | ||
407 | + 32, | ||
408 | + 65173, | ||
409 | + 65166, | ||
410 | + 65232, | ||
411 | + 65247, | ||
412 | + 32, | ||
413 | + 65246, | ||
414 | + 65252, | ||
415 | + 65228, | ||
416 | + 65247, | ||
417 | + 65165, | ||
418 | + 32, | ||
419 | + 65266, | ||
420 | + 65235 | ||
421 | + ]), | ||
422 | + ArabicText('الأمم المتحدة.', <int>[ | ||
423 | + 65250, | ||
424 | + 65251, | ||
425 | + 65271, | ||
426 | + 65165, | ||
427 | + 32, | ||
428 | + 46, | ||
429 | + 65171, | ||
430 | + 65194, | ||
431 | + 65188, | ||
432 | + 65176, | ||
433 | + 65252, | ||
434 | + 65247, | ||
435 | + 65165 | ||
436 | + ]), | ||
437 | + ]; | ||
438 | + | ||
439 | + pdf.addPage( | ||
440 | + MultiPage( | ||
441 | + crossAxisAlignment: CrossAxisAlignment.end, | ||
442 | + build: (Context context) => <Widget>[ | ||
443 | + for (ArabicText item in cases) | ||
444 | + Padding( | ||
445 | + padding: const EdgeInsets.only(bottom: 15), | ||
446 | + child: Text( | ||
447 | + item.original, | ||
448 | + textDirection: TextDirection.rtl, | ||
449 | + style: style, | ||
450 | + ), | ||
451 | + ), | ||
452 | + ], | ||
453 | + ), | ||
454 | + ); | ||
455 | + | ||
456 | + for (ArabicText item in cases) { | ||
457 | + expect( | ||
458 | + PdfArabic.convert(item.original).codeUnits, | ||
459 | + equals(item.reshaped), | ||
460 | + ); | ||
461 | + } | ||
462 | + }); | ||
463 | + | ||
464 | + test('Text Widgets Arabic', () { | ||
465 | + pdf.addPage(Page( | ||
466 | + build: (Context context) => RichText( | ||
467 | + textDirection: TextDirection.rtl, | ||
468 | + text: TextSpan( | ||
469 | + text: 'قهوة\n', | ||
470 | + style: TextStyle( | ||
471 | + font: arabicFont, | ||
472 | + fontSize: 30, | ||
473 | + ), | ||
474 | + children: const <TextSpan>[ | ||
475 | + TextSpan( | ||
476 | + text: | ||
477 | + 'القهوة مشروب يعد من بذور الب المحمصة، وينمو في أكثر من 70 لداً. خصوصاً في المناطق الاستوائية في أمريكا الشمالية والجنوبية وجنوب شرق آسيا وشبه القارة الهندية وأفريقيا. ويقال أن البن الأخضر هو ثاني أكثر السلع تداولاً في العالم بعد النفط الخام.', | ||
478 | + style: TextStyle( | ||
479 | + fontSize: 20, | ||
480 | + ), | ||
481 | + ), | ||
482 | + ], | ||
483 | + ), | ||
484 | + ), | ||
485 | + )); | ||
486 | + }); | ||
487 | + | ||
488 | + tearDownAll(() { | ||
489 | + final File file = File('arabic.pdf'); | ||
490 | + file.writeAsBytesSync(pdf.save()); | ||
491 | + }); | ||
492 | +} |
test/golden/arabic.pdf
0 → 100644
No preview for this file type
-
Please register or login to post a comment