1 | /* |
2 | * Copyright (C) 2015 The Qt Company Ltd |
3 | * |
4 | * This is part of HarfBuzz, an OpenType Layout engine library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | */ |
24 | |
25 | #include "harfbuzz-shaper.h" |
26 | #include "harfbuzz-shaper-private.h" |
27 | |
28 | #include <assert.h> |
29 | #include <stdio.h> |
30 | |
31 | enum MymrCharClassValues |
32 | { |
33 | Mymr_CC_RESERVED = 0, |
34 | Mymr_CC_CONSONANT = 1, /* Consonant of type 1, that has subscript form */ |
35 | Mymr_CC_CONSONANT2 = 2, /* Consonant of type 2, that has no subscript form */ |
36 | Mymr_CC_NGA = 3, /* Consonant NGA */ |
37 | Mymr_CC_YA = 4, /* Consonant YA */ |
38 | Mymr_CC_RA = 5, /* Consonant RA */ |
39 | Mymr_CC_WA = 6, /* Consonant WA */ |
40 | Mymr_CC_HA = 7, /* Consonant HA */ |
41 | Mymr_CC_IND_VOWEL = 8, /* Independent vowel */ |
42 | Mymr_CC_ZERO_WIDTH_NJ_MARK = 9, /* Zero Width non joiner character (0x200C) */ |
43 | Mymr_CC_VIRAMA = 10, /* Subscript consonant combining character */ |
44 | Mymr_CC_PRE_VOWEL = 11, /* Dependent vowel, prebase (Vowel e) */ |
45 | Mymr_CC_BELOW_VOWEL = 12, /* Dependent vowel, prebase (Vowel u, uu) */ |
46 | Mymr_CC_ABOVE_VOWEL = 13, /* Dependent vowel, prebase (Vowel i, ii, ai) */ |
47 | Mymr_CC_POST_VOWEL = 14, /* Dependent vowel, prebase (Vowel aa) */ |
48 | Mymr_CC_SIGN_ABOVE = 15, |
49 | Mymr_CC_SIGN_BELOW = 16, |
50 | Mymr_CC_SIGN_AFTER = 17, |
51 | Mymr_CC_ZERO_WIDTH_J_MARK = 18, /* Zero width joiner character */ |
52 | Mymr_CC_COUNT = 19 /* This is the number of character classes */ |
53 | }; |
54 | |
55 | enum MymrCharClassFlags |
56 | { |
57 | Mymr_CF_CLASS_MASK = 0x0000FFFF, |
58 | |
59 | Mymr_CF_CONSONANT = 0x01000000, /* flag to speed up comparing */ |
60 | Mymr_CF_MEDIAL = 0x02000000, /* flag to speed up comparing */ |
61 | Mymr_CF_IND_VOWEL = 0x04000000, /* flag to speed up comparing */ |
62 | Mymr_CF_DEP_VOWEL = 0x08000000, /* flag to speed up comparing */ |
63 | Mymr_CF_DOTTED_CIRCLE = 0x10000000, /* add a dotted circle if a character with this flag is the first in a syllable */ |
64 | Mymr_CF_VIRAMA = 0x20000000, /* flag to speed up comparing */ |
65 | |
66 | /* position flags */ |
67 | Mymr_CF_POS_BEFORE = 0x00080000, |
68 | Mymr_CF_POS_BELOW = 0x00040000, |
69 | Mymr_CF_POS_ABOVE = 0x00020000, |
70 | Mymr_CF_POS_AFTER = 0x00010000, |
71 | Mymr_CF_POS_MASK = 0x000f0000, |
72 | |
73 | Mymr_CF_AFTER_KINZI = 0x00100000 |
74 | }; |
75 | |
76 | /* Characters that get refrered to by name */ |
77 | enum MymrChar |
78 | { |
79 | Mymr_C_SIGN_ZWNJ = 0x200C, |
80 | Mymr_C_SIGN_ZWJ = 0x200D, |
81 | Mymr_C_DOTTED_CIRCLE = 0x25CC, |
82 | Mymr_C_RA = 0x101B, |
83 | Mymr_C_YA = 0x101A, |
84 | Mymr_C_NGA = 0x1004, |
85 | Mymr_C_VOWEL_E = 0x1031, |
86 | Mymr_C_VIRAMA = 0x1039 |
87 | }; |
88 | |
89 | enum |
90 | { |
91 | Mymr_xx = Mymr_CC_RESERVED, |
92 | Mymr_c1 = Mymr_CC_CONSONANT | Mymr_CF_CONSONANT | Mymr_CF_POS_BELOW, |
93 | Mymr_c2 = Mymr_CC_CONSONANT2 | Mymr_CF_CONSONANT, |
94 | Mymr_ng = Mymr_CC_NGA | Mymr_CF_CONSONANT | Mymr_CF_POS_ABOVE, |
95 | Mymr_ya = Mymr_CC_YA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_AFTER | Mymr_CF_AFTER_KINZI, |
96 | Mymr_ra = Mymr_CC_RA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BEFORE, |
97 | Mymr_wa = Mymr_CC_WA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BELOW, |
98 | Mymr_ha = Mymr_CC_HA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BELOW, |
99 | Mymr_id = Mymr_CC_IND_VOWEL | Mymr_CF_IND_VOWEL, |
100 | Mymr_vi = Mymr_CC_VIRAMA | Mymr_CF_VIRAMA | Mymr_CF_POS_ABOVE | Mymr_CF_DOTTED_CIRCLE, |
101 | Mymr_dl = Mymr_CC_PRE_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_BEFORE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI, |
102 | Mymr_db = Mymr_CC_BELOW_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_BELOW | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI, |
103 | Mymr_da = Mymr_CC_ABOVE_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_ABOVE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI, |
104 | Mymr_dr = Mymr_CC_POST_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_AFTER | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI, |
105 | Mymr_sa = Mymr_CC_SIGN_ABOVE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_POS_ABOVE | Mymr_CF_AFTER_KINZI, |
106 | Mymr_sb = Mymr_CC_SIGN_BELOW | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_POS_BELOW | Mymr_CF_AFTER_KINZI, |
107 | Mymr_sp = Mymr_CC_SIGN_AFTER | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI |
108 | }; |
109 | |
110 | |
111 | typedef int MymrCharClass; |
112 | |
113 | |
114 | static const MymrCharClass mymrCharClasses[] = |
115 | { |
116 | Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_ng, Mymr_c1, Mymr_c1, Mymr_c1, |
117 | Mymr_c1, Mymr_c1, Mymr_c2, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, /* 1000 - 100F */ |
118 | Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, |
119 | Mymr_c1, Mymr_c1, Mymr_ya, Mymr_ra, Mymr_c1, Mymr_wa, Mymr_c1, Mymr_ha, /* 1010 - 101F */ |
120 | Mymr_c2, Mymr_c2, Mymr_xx, Mymr_id, Mymr_id, Mymr_id, Mymr_id, Mymr_id, |
121 | Mymr_xx, Mymr_id, Mymr_id, Mymr_xx, Mymr_dr, Mymr_da, Mymr_da, Mymr_db, /* 1020 - 102F */ |
122 | Mymr_db, Mymr_dl, Mymr_da, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_sa, Mymr_sb, |
123 | Mymr_sp, Mymr_vi, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1030 - 103F */ |
124 | Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, |
125 | Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1040 - 104F */ |
126 | Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, |
127 | Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1050 - 105F */ |
128 | }; |
129 | |
130 | static MymrCharClass |
131 | getMyanmarCharClass (HB_UChar16 ch) |
132 | { |
133 | if (ch == Mymr_C_SIGN_ZWJ) |
134 | return Mymr_CC_ZERO_WIDTH_J_MARK; |
135 | |
136 | if (ch == Mymr_C_SIGN_ZWNJ) |
137 | return Mymr_CC_ZERO_WIDTH_NJ_MARK; |
138 | |
139 | if (ch < 0x1000 || ch > 0x105f) |
140 | return Mymr_CC_RESERVED; |
141 | |
142 | return mymrCharClasses[ch - 0x1000]; |
143 | } |
144 | |
145 | static const signed char mymrStateTable[][Mymr_CC_COUNT] = |
146 | { |
147 | /* xx c1, c2 ng ya ra wa ha id zwnj vi dl db da dr sa sb sp zwj */ |
148 | { 1, 4, 4, 2, 4, 4, 4, 4, 24, 1, 27, 17, 18, 19, 20, 21, 1, 1, 4}, /* 0 - ground state */ |
149 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 1 - exit state (or sp to the right of the syllable) */ |
150 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 17, 18, 19, 20, 21, -1, -1, 4}, /* 2 - NGA */ |
151 | {-1, 4, 4, 4, 4, 4, 4, 4, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 3 - Virama after NGA */ |
152 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 17, 18, 19, 20, 21, 1, 1, -1}, /* 4 - Base consonant */ |
153 | {-2, 6, -2, -2, 7, 8, 9, 10, -2, 23, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 5 - First virama */ |
154 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 25, 17, 18, 19, 20, 21, -1, -1, -1}, /* 6 - c1 after virama */ |
155 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /* 7 - ya after virama */ |
156 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /* 8 - ra after virama */ |
157 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /* 9 - wa after virama */ |
158 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 10 - ha after virama */ |
159 | {-1, -1, -1, -1, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 11 - Virama after NGA+zwj */ |
160 | {-2, -2, -2, -2, -2, -2, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 12 - Second virama */ |
161 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 17, 18, 19, 20, 21, -1, -1, -1}, /* 13 - wa after virama */ |
162 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 14 - ha after virama */ |
163 | {-2, -2, -2, -2, -2, -2, -2, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 15 - Third virama */ |
164 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 16 - ha after virama */ |
165 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 21, 1, 1, -1}, /* 17 - dl, Dependent vowel e */ |
166 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19, -1, 21, 1, 1, -1}, /* 18 - db, Dependent vowel u,uu */ |
167 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1}, /* 19 - da, Dependent vowel i,ii,ai */ |
168 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, 1, 1, -1}, /* 20 - dr, Dependent vowel aa */ |
169 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1}, /* 21 - sa, Sign anusvara */ |
170 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 22 - atha */ |
171 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1}, /* 23 - zwnj for atha */ |
172 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1}, /* 24 - Independent vowel */ |
173 | {-2, -2, -2, -2, 26, 26, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 25 - Virama after subscript consonant */ |
174 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, 1, -1}, /* 26 - ra/ya after subscript consonant + virama */ |
175 | {-1, 6, -1, -1, 7, 8, 9, 10, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 27 - Virama after ground state */ |
176 | /* exit state -2 is for invalid order of medials and combination of invalids |
177 | with virama where virama should treat as start of next syllable |
178 | */ |
179 | }; |
180 | |
181 | |
182 | |
183 | /*#define MYANMAR_DEBUG */ |
184 | #ifdef MYANMAR_DEBUG |
185 | #define MMDEBUG qDebug |
186 | #else |
187 | #define MMDEBUG if(0) printf |
188 | #endif |
189 | |
190 | /* |
191 | // Given an input string of characters and a location in which to start looking |
192 | // calculate, using the state table, which one is the last character of the syllable |
193 | // that starts in the starting position. |
194 | */ |
195 | static int myanmar_nextSyllableBoundary(const HB_UChar16 *s, int start, int end, HB_Bool *invalid) |
196 | { |
197 | const HB_UChar16 *uc = s + start; |
198 | int state = 0; |
199 | int pos = start; |
200 | *invalid = FALSE; |
201 | |
202 | while (pos < end) { |
203 | MymrCharClass charClass = getMyanmarCharClass(ch: *uc); |
204 | state = mymrStateTable[state][charClass & Mymr_CF_CLASS_MASK]; |
205 | if (pos == start) |
206 | *invalid = (HB_Bool)(charClass & Mymr_CF_DOTTED_CIRCLE); |
207 | |
208 | MMDEBUG(format: "state[%d]=%d class=%8x (uc=%4x)" , pos - start, state, charClass, *uc); |
209 | |
210 | if (state < 0) { |
211 | if (state < -1) |
212 | --pos; |
213 | break; |
214 | } |
215 | ++uc; |
216 | ++pos; |
217 | } |
218 | return pos; |
219 | } |
220 | |
221 | #ifndef NO_OPENTYPE |
222 | /* ###### might have to change order of above and below forms and substitutions, |
223 | but according to Unicode below comes before above */ |
224 | static const HB_OpenTypeFeature myanmar_features[] = { |
225 | { HB_MAKE_TAG('p', 'r', 'e', 'f'), .property: PreFormProperty }, |
226 | { HB_MAKE_TAG('b', 'l', 'w', 'f'), .property: BelowFormProperty }, |
227 | { HB_MAKE_TAG('a', 'b', 'v', 'f'), .property: AboveFormProperty }, |
228 | { HB_MAKE_TAG('p', 's', 't', 'f'), .property: PostFormProperty }, |
229 | { HB_MAKE_TAG('p', 'r', 'e', 's'), .property: PreSubstProperty }, |
230 | { HB_MAKE_TAG('b', 'l', 'w', 's'), .property: BelowSubstProperty }, |
231 | { HB_MAKE_TAG('a', 'b', 'v', 's'), .property: AboveSubstProperty }, |
232 | { HB_MAKE_TAG('p', 's', 't', 's'), .property: PostSubstProperty }, |
233 | { HB_MAKE_TAG('r', 'l', 'i', 'g'), .property: CligProperty }, /* Myanmar1 uses this instead of the other features */ |
234 | { .tag: 0, .property: 0 } |
235 | }; |
236 | #endif |
237 | |
238 | |
239 | /* |
240 | // Visual order before shaping should be: |
241 | // |
242 | // [Vowel Mark E] |
243 | // [Virama + Medial Ra] |
244 | // [Base] |
245 | // [Virama + Consonant] |
246 | // [Nga + Virama] (Kinzi) ### should probably come before post forms (medial ya) |
247 | // [Vowels] |
248 | // [Marks] |
249 | // |
250 | // This means that we can keep the logical order apart from having to |
251 | // move the pre vowel, medial ra and kinzi |
252 | */ |
253 | |
254 | static HB_Bool myanmar_shape_syllable(HB_Bool openType, HB_ShaperItem *item, HB_Bool invalid) |
255 | { |
256 | /* |
257 | // MMDEBUG("\nsyllable from %d len %d, str='%s'", item->item.pos, item->item.length, |
258 | // item->string->mid(item->from, item->length).toUtf8().data()); |
259 | */ |
260 | |
261 | #ifndef NO_OPENTYPE |
262 | const int availableGlyphs = item->num_glyphs; |
263 | #endif |
264 | const HB_UChar16 *uc = item->string + item->item.pos; |
265 | int vowel_e = -1; |
266 | int kinzi = -1; |
267 | int medial_ra = -1; |
268 | int base = -1; |
269 | int i; |
270 | int len = 0; |
271 | unsigned short reordered[32]; |
272 | unsigned char properties[32]; |
273 | enum { |
274 | AboveForm = 0x01, |
275 | PreForm = 0x02, |
276 | PostForm = 0x04, |
277 | BelowForm = 0x08 |
278 | }; |
279 | HB_Bool lastWasVirama = FALSE; |
280 | int basePos = -1; |
281 | |
282 | memset(s: properties, c: 0, n: 32*sizeof(unsigned char)); |
283 | |
284 | /* according to the table the max length of a syllable should be around 14 chars */ |
285 | assert(item->item.length < 32); |
286 | |
287 | #ifdef MYANMAR_DEBUG |
288 | printf("original:" ); |
289 | for (i = 0; i < (int)item->item.length; i++) { |
290 | printf(" %d: %4x" , i, uc[i]); |
291 | } |
292 | #endif |
293 | for (i = 0; i < (int)item->item.length; ++i) { |
294 | HB_UChar16 chr = uc[i]; |
295 | |
296 | if (chr == Mymr_C_VOWEL_E) { |
297 | vowel_e = i; |
298 | continue; |
299 | } |
300 | if (i == 0 |
301 | && chr == Mymr_C_NGA |
302 | && i + 2 < (int)item->item.length |
303 | && uc[i+1] == Mymr_C_VIRAMA) { |
304 | int mc = getMyanmarCharClass(ch: uc[i+2]); |
305 | /*MMDEBUG("maybe kinzi: mc=%x", mc);*/ |
306 | if ((mc & Mymr_CF_CONSONANT) == Mymr_CF_CONSONANT) { |
307 | kinzi = i; |
308 | continue; |
309 | } |
310 | } |
311 | if (base >= 0 |
312 | && chr == Mymr_C_VIRAMA |
313 | && i + 1 < (int)item->item.length |
314 | && uc[i+1] == Mymr_C_RA) { |
315 | medial_ra = i; |
316 | continue; |
317 | } |
318 | if (base < 0) |
319 | base = i; |
320 | } |
321 | |
322 | MMDEBUG(format: "\n base=%d, vowel_e=%d, kinzi=%d, medial_ra=%d" , base, vowel_e, kinzi, medial_ra); |
323 | /* write vowel_e if found */ |
324 | if (vowel_e >= 0) { |
325 | reordered[0] = Mymr_C_VOWEL_E; |
326 | len = 1; |
327 | } |
328 | /* write medial_ra */ |
329 | if (medial_ra >= 0) { |
330 | reordered[len] = Mymr_C_VIRAMA; |
331 | reordered[len+1] = Mymr_C_RA; |
332 | properties[len] = PreForm; |
333 | properties[len+1] = PreForm; |
334 | len += 2; |
335 | } |
336 | |
337 | /* shall we add a dotted circle? |
338 | If in the position in which the base should be (first char in the string) there is |
339 | a character that has the Dotted circle flag (a character that cannot be a base) |
340 | then write a dotted circle */ |
341 | if (invalid) { |
342 | reordered[len] = C_DOTTED_CIRCLE; |
343 | ++len; |
344 | } |
345 | |
346 | /* copy the rest of the syllable to the output, inserting the kinzi |
347 | at the correct place */ |
348 | for (i = 0; i < (int)item->item.length; ++i) { |
349 | hb_uint16 chr = uc[i]; |
350 | MymrCharClass cc; |
351 | if (i == vowel_e) |
352 | continue; |
353 | if (i == medial_ra || i == kinzi) { |
354 | ++i; |
355 | continue; |
356 | } |
357 | |
358 | cc = getMyanmarCharClass(ch: uc[i]); |
359 | if (kinzi >= 0 && i > base && (cc & Mymr_CF_AFTER_KINZI)) { |
360 | reordered[len] = Mymr_C_NGA; |
361 | reordered[len+1] = Mymr_C_VIRAMA; |
362 | if (len > 0) |
363 | properties[len-1] = AboveForm; |
364 | properties[len] = AboveForm; |
365 | len += 2; |
366 | kinzi = -1; |
367 | } |
368 | |
369 | if (lastWasVirama) { |
370 | int prop = 0; |
371 | switch(cc & Mymr_CF_POS_MASK) { |
372 | case Mymr_CF_POS_BEFORE: |
373 | prop = PreForm; |
374 | break; |
375 | case Mymr_CF_POS_BELOW: |
376 | prop = BelowForm; |
377 | break; |
378 | case Mymr_CF_POS_ABOVE: |
379 | prop = AboveForm; |
380 | break; |
381 | case Mymr_CF_POS_AFTER: |
382 | prop = PostForm; |
383 | break; |
384 | default: |
385 | break; |
386 | } |
387 | properties[len-1] = prop; |
388 | properties[len] = prop; |
389 | if(basePos >= 0 && basePos == len-2) |
390 | properties[len-2] = prop; |
391 | } |
392 | lastWasVirama = (chr == Mymr_C_VIRAMA); |
393 | if(i == base) |
394 | basePos = len; |
395 | |
396 | if ((chr != Mymr_C_SIGN_ZWNJ && chr != Mymr_C_SIGN_ZWJ) || !len) { |
397 | reordered[len] = chr; |
398 | ++len; |
399 | } |
400 | } |
401 | if (kinzi >= 0) { |
402 | reordered[len] = Mymr_C_NGA; |
403 | reordered[len+1] = Mymr_C_VIRAMA; |
404 | properties[len] = AboveForm; |
405 | properties[len+1] = AboveForm; |
406 | len += 2; |
407 | } |
408 | |
409 | if (!item->font->klass->convertStringToGlyphIndices(item->font, |
410 | reordered, len, |
411 | item->glyphs, &item->num_glyphs, |
412 | item->item.bidiLevel % 2)) |
413 | return FALSE; |
414 | |
415 | MMDEBUG(format: "after shaping: len=%d" , len); |
416 | for (i = 0; i < len; i++) { |
417 | item->attributes[i].mark = FALSE; |
418 | item->attributes[i].clusterStart = FALSE; |
419 | item->attributes[i].justification = 0; |
420 | item->attributes[i].zeroWidth = FALSE; |
421 | MMDEBUG(format: " %d: %4x property=%x" , i, reordered[i], properties[i]); |
422 | } |
423 | |
424 | /* now we have the syllable in the right order, and can start running it through open type. */ |
425 | |
426 | #ifndef NO_OPENTYPE |
427 | if (openType) { |
428 | hb_uint32 where[32]; |
429 | |
430 | for (i = 0; i < len; ++i) { |
431 | where[i] = ~(PreSubstProperty |
432 | | BelowSubstProperty |
433 | | AboveSubstProperty |
434 | | PostSubstProperty |
435 | | CligProperty |
436 | | PositioningProperties); |
437 | if (properties[i] & PreForm) |
438 | where[i] &= ~PreFormProperty; |
439 | if (properties[i] & BelowForm) |
440 | where[i] &= ~BelowFormProperty; |
441 | if (properties[i] & AboveForm) |
442 | where[i] &= ~AboveFormProperty; |
443 | if (properties[i] & PostForm) |
444 | where[i] &= ~PostFormProperty; |
445 | } |
446 | |
447 | HB_OpenTypeShape(item, properties: where); |
448 | if (!HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE)) |
449 | return FALSE; |
450 | } else |
451 | #endif |
452 | { |
453 | MMDEBUG(format: "Not using openType" ); |
454 | HB_HeuristicPosition(item); |
455 | } |
456 | |
457 | item->attributes[0].clusterStart = TRUE; |
458 | return TRUE; |
459 | } |
460 | |
461 | HB_Bool HB_MyanmarShape(HB_ShaperItem *item) |
462 | { |
463 | HB_Bool openType = FALSE; |
464 | unsigned short *logClusters = item->log_clusters; |
465 | |
466 | HB_ShaperItem syllable = *item; |
467 | int first_glyph = 0; |
468 | |
469 | int sstart = item->item.pos; |
470 | int end = sstart + item->item.length; |
471 | int i = 0; |
472 | |
473 | assert(item->item.script == HB_Script_Myanmar); |
474 | #ifndef NO_OPENTYPE |
475 | openType = HB_SelectScript(shaper_item: item, features: myanmar_features); |
476 | #endif |
477 | |
478 | MMDEBUG(format: "myanmar_shape: from %d length %d" , item->item.pos, item->item.length); |
479 | while (sstart < end) { |
480 | HB_Bool invalid; |
481 | int send = myanmar_nextSyllableBoundary(s: item->string, start: sstart, end, invalid: &invalid); |
482 | MMDEBUG(format: "syllable from %d, length %d, invalid=%s" , sstart, send-sstart, |
483 | invalid ? "TRUE" : "FALSE" ); |
484 | syllable.item.pos = sstart; |
485 | syllable.item.length = send-sstart; |
486 | syllable.glyphs = item->glyphs + first_glyph; |
487 | syllable.attributes = item->attributes + first_glyph; |
488 | syllable.advances = item->advances + first_glyph; |
489 | syllable.offsets = item->offsets + first_glyph; |
490 | syllable.num_glyphs = item->num_glyphs - first_glyph; |
491 | if (!myanmar_shape_syllable(openType, item: &syllable, invalid)) { |
492 | MMDEBUG(format: "syllable shaping failed, syllable requests %d glyphs" , syllable.num_glyphs); |
493 | item->num_glyphs += syllable.num_glyphs; |
494 | return FALSE; |
495 | } |
496 | |
497 | /* fix logcluster array */ |
498 | MMDEBUG(format: "syllable:" ); |
499 | for (i = first_glyph; i < first_glyph + (int)syllable.num_glyphs; ++i) |
500 | MMDEBUG(format: " %d -> glyph %x" , i, item->glyphs[i]); |
501 | MMDEBUG(format: " logclusters:" ); |
502 | for (i = sstart; i < send; ++i) { |
503 | MMDEBUG(format: " %d -> glyph %d" , i, first_glyph); |
504 | logClusters[i-item->item.pos] = first_glyph; |
505 | } |
506 | sstart = send; |
507 | first_glyph += syllable.num_glyphs; |
508 | } |
509 | item->num_glyphs = first_glyph; |
510 | return TRUE; |
511 | } |
512 | |
513 | void HB_MyanmarAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes) |
514 | { |
515 | int end = from + len; |
516 | const HB_UChar16 *uc = text + from; |
517 | hb_uint32 i = 0; |
518 | HB_UNUSED(script); |
519 | attributes += from; |
520 | while (i < len) { |
521 | HB_Bool invalid; |
522 | hb_uint32 boundary = myanmar_nextSyllableBoundary(s: text, start: from+i, end, invalid: &invalid) - from; |
523 | |
524 | attributes[i].graphemeBoundary = TRUE; |
525 | attributes[i].lineBreak = TRUE; |
526 | |
527 | if (boundary > len-1) |
528 | boundary = len; |
529 | i++; |
530 | while (i < boundary) { |
531 | attributes[i].graphemeBoundary = FALSE; |
532 | ++uc; |
533 | ++i; |
534 | } |
535 | assert(i == boundary); |
536 | } |
537 | } |
538 | |
539 | |