OLD | NEW |
1 /* | 1 /* |
2 * Copyright © 2011,2012 Google, Inc. | 2 * Copyright © 2011,2012 Google, Inc. |
3 * | 3 * |
4 * This is part of HarfBuzz, a text shaping library. | 4 * This is part of HarfBuzz, a text shaping library. |
5 * | 5 * |
6 * Permission is hereby granted, without written agreement and without | 6 * Permission is hereby granted, without written agreement and without |
7 * license or royalty fees, to use, copy, modify, and distribute this | 7 * license or royalty fees, to use, copy, modify, and distribute this |
8 * software and its documentation for any purpose, provided that the | 8 * software and its documentation for any purpose, provided that the |
9 * above copyright notice and the following two paragraphs appear in | 9 * above copyright notice and the following two paragraphs appear in |
10 * all copies of this software. | 10 * all copies of this software. |
11 * | 11 * |
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 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 | 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 * DAMAGE. | 16 * DAMAGE. |
17 * | 17 * |
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | 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 | 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 * | 23 * |
24 * Google Author(s): Behdad Esfahbod | 24 * Google Author(s): Behdad Esfahbod |
25 */ | 25 */ |
26 | 26 |
27 #include "hb-ot-shape-complex-indic-private.hh" | 27 #include "hb-ot-shape-complex-indic-private.hh" |
28 #include "hb-ot-layout-private.hh" | 28 #include "hb-ot-layout-private.hh" |
29 | 29 |
| 30 /* buffer var allocations */ |
| 31 #define indic_category() complex_var_u8_0() /* indic_category_t */ |
| 32 #define indic_position() complex_var_u8_1() /* indic_position_t */ |
| 33 |
30 | 34 |
31 /* | 35 /* |
32 * Global Indic shaper options. | 36 * Indic shaper. |
33 */ | 37 */ |
34 | 38 |
35 struct indic_options_t | 39 |
36 { | 40 #define IN_HALF_BLOCK(u, Base) (((u) & ~0x7F) == (Base)) |
37 int initialized : 1; | 41 |
38 int uniscribe_bug_compatible : 1; | 42 #define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900)) |
| 43 #define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980)) |
| 44 #define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00)) |
| 45 #define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80)) |
| 46 #define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00)) |
| 47 #define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80)) |
| 48 #define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00)) |
| 49 #define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80)) |
| 50 #define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00)) |
| 51 #define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80)) |
| 52 #define IS_KHMR(u) (IN_HALF_BLOCK (u, 0x1780)) |
| 53 |
| 54 |
| 55 #define MATRA_POS_LEFT(u)» POS_PRE_M |
| 56 #define MATRA_POS_RIGHT(u)» ( \ |
| 57 » » » » IS_DEVA(u) ? POS_AFTER_SUB : \ |
| 58 » » » » IS_BENG(u) ? POS_AFTER_POST : \ |
| 59 » » » » IS_GURU(u) ? POS_AFTER_POST : \ |
| 60 » » » » IS_GUJR(u) ? POS_AFTER_POST : \ |
| 61 » » » » IS_ORYA(u) ? POS_AFTER_POST : \ |
| 62 » » » » IS_TAML(u) ? POS_AFTER_POST : \ |
| 63 » » » » IS_TELU(u) ? (u <= 0x0C42 ? POS_BEFORE_SUB : P
OS_AFTER_SUB) : \ |
| 64 » » » » IS_KNDA(u) ? (u < 0x0CC3 || u > 0xCD6 ? POS_BE
FORE_SUB : POS_AFTER_SUB) : \ |
| 65 » » » » IS_MLYM(u) ? POS_AFTER_POST : \ |
| 66 » » » » IS_SINH(u) ? POS_AFTER_SUB : \ |
| 67 » » » » IS_KHMR(u) ? POS_AFTER_POST : \ |
| 68 » » » » /*default*/ POS_AFTER_SUB \ |
| 69 » » » » ) |
| 70 #define MATRA_POS_TOP(u)» ( /* BENG and MLYM don't have top matras. */ \ |
| 71 » » » » IS_DEVA(u) ? POS_AFTER_SUB : \ |
| 72 » » » » IS_GURU(u) ? POS_AFTER_POST : /* Deviate from
spec */ \ |
| 73 » » » » IS_GUJR(u) ? POS_AFTER_SUB : \ |
| 74 » » » » IS_ORYA(u) ? POS_AFTER_MAIN : \ |
| 75 » » » » IS_TAML(u) ? POS_AFTER_SUB : \ |
| 76 » » » » IS_TELU(u) ? POS_BEFORE_SUB : \ |
| 77 » » » » IS_KNDA(u) ? POS_BEFORE_SUB : \ |
| 78 » » » » IS_SINH(u) ? POS_AFTER_SUB : \ |
| 79 » » » » IS_KHMR(u) ? POS_AFTER_POST : \ |
| 80 » » » » /*default*/ POS_AFTER_SUB \ |
| 81 » » » » ) |
| 82 #define MATRA_POS_BOTTOM(u)» ( \ |
| 83 » » » » IS_DEVA(u) ? POS_AFTER_SUB : \ |
| 84 » » » » IS_BENG(u) ? POS_AFTER_SUB : \ |
| 85 » » » » IS_GURU(u) ? POS_AFTER_POST : \ |
| 86 » » » » IS_GUJR(u) ? POS_AFTER_POST : \ |
| 87 » » » » IS_ORYA(u) ? POS_AFTER_SUB : \ |
| 88 » » » » IS_TAML(u) ? POS_AFTER_POST : \ |
| 89 » » » » IS_TELU(u) ? POS_BEFORE_SUB : \ |
| 90 » » » » IS_KNDA(u) ? POS_BEFORE_SUB : \ |
| 91 » » » » IS_MLYM(u) ? POS_AFTER_POST : \ |
| 92 » » » » IS_SINH(u) ? POS_AFTER_SUB : \ |
| 93 » » » » IS_KHMR(u) ? POS_AFTER_POST : \ |
| 94 » » » » /*default*/ POS_AFTER_SUB \ |
| 95 » » » » ) |
| 96 |
| 97 static inline indic_position_t |
| 98 matra_position (hb_codepoint_t u, indic_position_t side) |
| 99 { |
| 100 switch ((int) side) |
| 101 { |
| 102 case POS_PRE_C:» return MATRA_POS_LEFT (u); |
| 103 case POS_POST_C:» return MATRA_POS_RIGHT (u); |
| 104 case POS_ABOVE_C:» return MATRA_POS_TOP (u); |
| 105 case POS_BELOW_C:» return MATRA_POS_BOTTOM (u); |
| 106 }; |
| 107 return side; |
| 108 } |
| 109 |
| 110 /* XXX |
| 111 * This is a hack for now. We should move this data into the main Indic table. |
| 112 * Or completely remove it and just check in the tables. |
| 113 */ |
| 114 static const hb_codepoint_t ra_chars[] = { |
| 115 0x0930, /* Devanagari */ |
| 116 0x09B0, /* Bengali */ |
| 117 0x09F0, /* Bengali */ |
| 118 0x0A30, /* Gurmukhi */» /* No Reph */ |
| 119 0x0AB0, /* Gujarati */ |
| 120 0x0B30, /* Oriya */ |
| 121 0x0BB0, /* Tamil */» » /* No Reph */ |
| 122 0x0C30, /* Telugu */» » /* Reph formed only with ZWJ */ |
| 123 0x0CB0, /* Kannada */ |
| 124 0x0D30, /* Malayalam */» /* No Reph, Logical Repha */ |
| 125 |
| 126 0x0DBB, /* Sinhala */»» /* Reph formed only with ZWJ */ |
| 127 |
| 128 0x179A, /* Khmer */» » /* No Reph, Visual Repha */ |
39 }; | 129 }; |
40 | 130 |
41 union indic_options_union_t { | 131 static inline indic_position_t |
42 int i; | 132 consonant_position (hb_codepoint_t u) |
43 indic_options_t opts; | 133 { |
44 }; | 134 if ((u & ~0x007F) == 0x1780) |
45 ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t)); | 135 return POS_BELOW_C; /* In Khmer coeng model, post and below forms should not
be reordered. */ |
46 | 136 return POS_BASE_C; /* Will recategorize later based on font lookups. */ |
47 static indic_options_union_t | 137 } |
48 indic_options_init (void) | 138 |
49 { | 139 static inline bool |
50 indic_options_union_t u; | 140 is_ra (hb_codepoint_t u) |
51 u.i = 0; | 141 { |
52 u.opts.initialized = 1; | 142 for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++) |
53 | 143 if (u == ra_chars[i]) |
54 char *c = getenv ("HB_OT_INDIC_OPTIONS"); | 144 return true; |
55 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); | 145 return false; |
56 | 146 } |
57 return u; | 147 |
58 } | 148 static inline bool |
59 | 149 is_one_of (const hb_glyph_info_t &info, unsigned int flags) |
60 static inline indic_options_t | 150 { |
61 indic_options (void) | 151 /* If it ligated, all bets are off. */ |
62 { | 152 if (is_a_ligature (info)) return false; |
63 static indic_options_union_t options; | 153 return !!(FLAG (info.indic_category()) & flags); |
64 | 154 } |
65 if (unlikely (!options.i)) { | 155 |
66 /* This is idempotent and threadsafe. */ | 156 #define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)) |
67 options = indic_options_init (); | 157 static inline bool |
68 } | 158 is_joiner (const hb_glyph_info_t &info) |
69 | 159 { |
70 return options.opts; | 160 return is_one_of (info, JOINER_FLAGS); |
71 } | 161 } |
| 162 |
| 163 /* Note: |
| 164 * |
| 165 * We treat Vowels and placeholders as if they were consonants. This is safe be
cause Vowels |
| 166 * cannot happen in a consonant syllable. The plus side however is, we can call
the |
| 167 * consonant syllable logic from the vowel syllable function and get it all righ
t! */ |
| 168 #define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CM) | FLAG (OT_Ra) | FLAG (OT_V)
| FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)) |
| 169 static inline bool |
| 170 is_consonant (const hb_glyph_info_t &info) |
| 171 { |
| 172 return is_one_of (info, CONSONANT_FLAGS); |
| 173 } |
| 174 |
| 175 #define HALANT_OR_COENG_FLAGS (FLAG (OT_H) | FLAG (OT_Coeng)) |
| 176 static inline bool |
| 177 is_halant_or_coeng (const hb_glyph_info_t &info) |
| 178 { |
| 179 return is_one_of (info, HALANT_OR_COENG_FLAGS); |
| 180 } |
| 181 |
| 182 static inline void |
| 183 set_indic_properties (hb_glyph_info_t &info) |
| 184 { |
| 185 hb_codepoint_t u = info.codepoint; |
| 186 unsigned int type = hb_indic_get_categories (u); |
| 187 indic_category_t cat = (indic_category_t) (type & 0x7F); |
| 188 indic_position_t pos = (indic_position_t) (type >> 8); |
| 189 |
| 190 |
| 191 /* |
| 192 * Re-assign category |
| 193 */ |
| 194 |
| 195 |
| 196 /* The spec says U+0952 is OT_A. However, testing shows that Uniscribe |
| 197 * treats U+0951..U+0952 all as OT_VD. |
| 198 * TESTS: |
| 199 * U+092E,U+0947,U+0952 |
| 200 * U+092E,U+0952,U+0947 |
| 201 * U+092E,U+0947,U+0951 |
| 202 * U+092E,U+0951,U+0947 |
| 203 * */ |
| 204 if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0951, 0x0954))) |
| 205 cat = OT_VD; |
| 206 |
| 207 if (unlikely (u == 0x17D1)) |
| 208 cat = OT_X; |
| 209 if (cat == OT_X && |
| 210 unlikely (hb_in_range<hb_codepoint_t> (u, 0x17CB, 0x17D3))) /* Khmer Vario
us signs */ |
| 211 { |
| 212 /* These are like Top Matras. */ |
| 213 cat = OT_M; |
| 214 pos = POS_ABOVE_C; |
| 215 } |
| 216 if (u == 0x17C6) /* Khmer Bindu doesn't like to be repositioned. */ |
| 217 cat = OT_N; |
| 218 |
| 219 if (unlikely (u == 0x17D2)) cat = OT_Coeng; /* Khmer coeng */ |
| 220 else if (unlikely (u == 0x200C)) cat = OT_ZWNJ; |
| 221 else if (unlikely (u == 0x200D)) cat = OT_ZWJ; |
| 222 else if (unlikely (u == 0x25CC)) cat = OT_DOTTEDCIRCLE; |
| 223 else if (unlikely (u == 0x0A71)) cat = OT_SM; /* GURMUKHI ADDAK. More like co
nsonant medial. like 0A75. */ |
| 224 |
| 225 if (cat == OT_Repha) { |
| 226 /* There are two kinds of characters marked as Repha: |
| 227 * - The ones that are GenCat=Mn are already positioned visually, ie. after
base. (eg. Khmer) |
| 228 * - The ones that are GenCat=Lo is encoded logically, ie. beginning of syll
able. (eg. Malayalam) |
| 229 * |
| 230 * We recategorize the first kind to look like a Nukta and attached to the b
ase directly. |
| 231 */ |
| 232 if (_hb_glyph_info_get_general_category (&info) == HB_UNICODE_GENERAL_CATEGO
RY_NON_SPACING_MARK) |
| 233 cat = OT_N; |
| 234 } |
| 235 |
| 236 |
| 237 |
| 238 /* |
| 239 * Re-assign position. |
| 240 */ |
| 241 |
| 242 if ((FLAG (cat) & CONSONANT_FLAGS)) |
| 243 { |
| 244 pos = consonant_position (u); |
| 245 if (is_ra (u)) |
| 246 cat = OT_Ra; |
| 247 } |
| 248 else if (cat == OT_M) |
| 249 { |
| 250 pos = matra_position (u, pos); |
| 251 } |
| 252 else if (cat == OT_SM || cat == OT_VD) |
| 253 { |
| 254 pos = POS_SMVD; |
| 255 } |
| 256 |
| 257 if (unlikely (u == 0x0B01)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub
in the spec. */ |
| 258 |
| 259 |
| 260 |
| 261 info.indic_category() = cat; |
| 262 info.indic_position() = pos; |
| 263 } |
| 264 |
| 265 /* |
| 266 * Things above this line should ideally be moved to the Indic table itself. |
| 267 */ |
72 | 268 |
73 | 269 |
74 /* | 270 /* |
75 * Indic configurations. Note that we do not want to keep every single script-s
pecific | 271 * Indic configurations. Note that we do not want to keep every single script-s
pecific |
76 * behavior in these tables necessarily. This should mainly be used for per-scr
ipt | 272 * behavior in these tables necessarily. This should mainly be used for per-scr
ipt |
77 * properties that are cheaper keeping here, than in the code. Ie. if, say, one
and | 273 * properties that are cheaper keeping here, than in the code. Ie. if, say, one
and |
78 * only one script has an exception, that one script can be if'ed directly in th
e code, | 274 * only one script has an exception, that one script can be if'ed directly in th
e code, |
79 * instead of adding a new flag in these structs. | 275 * instead of adding a new flag in these structs. |
80 */ | 276 */ |
81 | 277 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
116 {HB_SCRIPT_BENGALI, true, 0x09CD,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MO
DE_IMPLICIT}, | 312 {HB_SCRIPT_BENGALI, true, 0x09CD,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MO
DE_IMPLICIT}, |
117 {HB_SCRIPT_GURMUKHI, true, 0x0A4D,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MO
DE_IMPLICIT}, | 313 {HB_SCRIPT_GURMUKHI, true, 0x0A4D,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MO
DE_IMPLICIT}, |
118 {HB_SCRIPT_GUJARATI, true, 0x0ACD,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MO
DE_IMPLICIT}, | 314 {HB_SCRIPT_GUJARATI, true, 0x0ACD,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MO
DE_IMPLICIT}, |
119 {HB_SCRIPT_ORIYA, true, 0x0B4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MO
DE_IMPLICIT}, | 315 {HB_SCRIPT_ORIYA, true, 0x0B4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MO
DE_IMPLICIT}, |
120 {HB_SCRIPT_TAMIL, true, 0x0BCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MO
DE_IMPLICIT}, | 316 {HB_SCRIPT_TAMIL, true, 0x0BCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MO
DE_IMPLICIT}, |
121 {HB_SCRIPT_TELUGU, true, 0x0C4D,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MO
DE_EXPLICIT}, | 317 {HB_SCRIPT_TELUGU, true, 0x0C4D,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MO
DE_EXPLICIT}, |
122 {HB_SCRIPT_KANNADA, true, 0x0CCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MO
DE_IMPLICIT}, | 318 {HB_SCRIPT_KANNADA, true, 0x0CCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MO
DE_IMPLICIT}, |
123 {HB_SCRIPT_MALAYALAM, true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MO
DE_LOG_REPHA}, | 319 {HB_SCRIPT_MALAYALAM, true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MO
DE_LOG_REPHA}, |
124 {HB_SCRIPT_SINHALA, false,0x0DCA,BASE_POS_FIRST,REPH_POS_AFTER_MAIN, REPH_MO
DE_EXPLICIT}, | 320 {HB_SCRIPT_SINHALA, false,0x0DCA,BASE_POS_FIRST,REPH_POS_AFTER_MAIN, REPH_MO
DE_EXPLICIT}, |
125 {HB_SCRIPT_KHMER, false,0x17D2,BASE_POS_FIRST,REPH_POS_DEFAULT, REPH_MO
DE_VIS_REPHA}, | 321 {HB_SCRIPT_KHMER, false,0x17D2,BASE_POS_FIRST,REPH_POS_DEFAULT, REPH_MO
DE_VIS_REPHA}, |
126 /* Myanmar does not have the "old_indic" behavior, even though it has a "new"
tag. */ | |
127 {HB_SCRIPT_MYANMAR, false,0x1039,BASE_POS_LAST, REPH_POS_DEFAULT, REPH_MO
DE_EXPLICIT}, | |
128 }; | 322 }; |
129 | 323 |
130 | 324 |
131 | 325 |
132 /* | 326 /* |
133 * Indic shaper. | 327 * Indic shaper. |
134 */ | 328 */ |
135 | 329 |
136 struct feature_list_t { | 330 struct feature_list_t { |
137 hb_tag_t tag; | 331 hb_tag_t tag; |
138 hb_bool_t is_global; | 332 hb_ot_map_feature_flags_t flags; |
139 }; | 333 }; |
140 | 334 |
141 static const feature_list_t | 335 static const feature_list_t |
142 indic_features[] = | 336 indic_features[] = |
143 { | 337 { |
144 /* | 338 /* |
145 * Basic features. | 339 * Basic features. |
146 * These features are applied in order, one at a time, after initial_reorderin
g. | 340 * These features are applied in order, one at a time, after initial_reorderin
g. |
147 */ | 341 */ |
148 {HB_TAG('n','u','k','t'), true}, | 342 {HB_TAG('n','u','k','t'), F_GLOBAL}, |
149 {HB_TAG('a','k','h','n'), true}, | 343 {HB_TAG('a','k','h','n'), F_GLOBAL}, |
150 {HB_TAG('r','p','h','f'), false}, | 344 {HB_TAG('r','p','h','f'), F_NONE}, |
151 {HB_TAG('r','k','r','f'), true}, | 345 {HB_TAG('r','k','r','f'), F_GLOBAL}, |
152 {HB_TAG('p','r','e','f'), false}, | 346 {HB_TAG('p','r','e','f'), F_NONE}, |
153 {HB_TAG('h','a','l','f'), false}, | 347 {HB_TAG('b','l','w','f'), F_NONE}, |
154 {HB_TAG('b','l','w','f'), false}, | 348 {HB_TAG('h','a','l','f'), F_NONE}, |
155 {HB_TAG('a','b','v','f'), false}, | 349 {HB_TAG('a','b','v','f'), F_NONE}, |
156 {HB_TAG('p','s','t','f'), false}, | 350 {HB_TAG('p','s','t','f'), F_NONE}, |
157 {HB_TAG('c','f','a','r'), false}, | 351 {HB_TAG('c','f','a','r'), F_NONE}, |
158 {HB_TAG('c','j','c','t'), true}, | 352 {HB_TAG('v','a','t','u'), F_GLOBAL}, |
159 {HB_TAG('v','a','t','u'), true}, | 353 {HB_TAG('c','j','c','t'), F_GLOBAL}, |
160 /* | 354 /* |
161 * Other features. | 355 * Other features. |
162 * These features are applied all at once, after final_reordering. | 356 * These features are applied all at once, after final_reordering. |
163 */ | 357 */ |
164 {HB_TAG('i','n','i','t'), false}, | 358 {HB_TAG('i','n','i','t'), F_NONE}, |
165 {HB_TAG('p','r','e','s'), true}, | 359 {HB_TAG('p','r','e','s'), F_GLOBAL}, |
166 {HB_TAG('a','b','v','s'), true}, | 360 {HB_TAG('a','b','v','s'), F_GLOBAL}, |
167 {HB_TAG('b','l','w','s'), true}, | 361 {HB_TAG('b','l','w','s'), F_GLOBAL}, |
168 {HB_TAG('p','s','t','s'), true}, | 362 {HB_TAG('p','s','t','s'), F_GLOBAL}, |
169 {HB_TAG('h','a','l','n'), true}, | 363 {HB_TAG('h','a','l','n'), F_GLOBAL}, |
170 /* Positioning features, though we don't care about the types. */ | 364 /* Positioning features, though we don't care about the types. */ |
171 {HB_TAG('d','i','s','t'), true}, | 365 {HB_TAG('d','i','s','t'), F_GLOBAL}, |
172 {HB_TAG('a','b','v','m'), true}, | 366 {HB_TAG('a','b','v','m'), F_GLOBAL}, |
173 {HB_TAG('b','l','w','m'), true}, | 367 {HB_TAG('b','l','w','m'), F_GLOBAL}, |
174 }; | 368 }; |
175 | 369 |
176 /* | 370 /* |
177 * Must be in the same order as the indic_features array. | 371 * Must be in the same order as the indic_features array. |
178 */ | 372 */ |
179 enum { | 373 enum { |
180 _NUKT, | 374 _NUKT, |
181 _AKHN, | 375 _AKHN, |
182 RPHF, | 376 RPHF, |
183 _RKRF, | 377 _RKRF, |
184 PREF, | 378 PREF, |
| 379 BLWF, |
185 HALF, | 380 HALF, |
186 BLWF, | |
187 ABVF, | 381 ABVF, |
188 PSTF, | 382 PSTF, |
189 CFAR, | 383 CFAR, |
| 384 _VATU, |
190 _CJCT, | 385 _CJCT, |
191 _VATU, | |
192 | 386 |
193 INIT, | 387 INIT, |
194 _PRES, | 388 _PRES, |
195 _ABVS, | 389 _ABVS, |
196 _BLWS, | 390 _BLWS, |
197 _PSTS, | 391 _PSTS, |
198 _HALN, | 392 _HALN, |
199 _DIST, | 393 _DIST, |
200 _ABVM, | 394 _ABVM, |
201 _BLWM, | 395 _BLWM, |
(...skipping 16 matching lines...) Expand all Loading... |
218 hb_buffer_t *buffer); | 412 hb_buffer_t *buffer); |
219 | 413 |
220 static void | 414 static void |
221 collect_features_indic (hb_ot_shape_planner_t *plan) | 415 collect_features_indic (hb_ot_shape_planner_t *plan) |
222 { | 416 { |
223 hb_ot_map_builder_t *map = &plan->map; | 417 hb_ot_map_builder_t *map = &plan->map; |
224 | 418 |
225 /* Do this before any lookups have been applied. */ | 419 /* Do this before any lookups have been applied. */ |
226 map->add_gsub_pause (setup_syllables); | 420 map->add_gsub_pause (setup_syllables); |
227 | 421 |
228 map->add_bool_feature (HB_TAG('l','o','c','l')); | 422 map->add_global_bool_feature (HB_TAG('l','o','c','l')); |
229 /* The Indic specs do not require ccmp, but we apply it here since if | 423 /* The Indic specs do not require ccmp, but we apply it here since if |
230 * there is a use of it, it's typically at the beginning. */ | 424 * there is a use of it, it's typically at the beginning. */ |
231 map->add_bool_feature (HB_TAG('c','c','m','p')); | 425 map->add_global_bool_feature (HB_TAG('c','c','m','p')); |
232 | 426 |
233 | 427 |
234 unsigned int i = 0; | 428 unsigned int i = 0; |
235 map->add_gsub_pause (initial_reordering); | 429 map->add_gsub_pause (initial_reordering); |
236 for (; i < INDIC_BASIC_FEATURES; i++) { | 430 for (; i < INDIC_BASIC_FEATURES; i++) { |
237 map->add_bool_feature (indic_features[i].tag, indic_features[i].is_global); | 431 map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANU
AL_ZWJ); |
238 map->add_gsub_pause (NULL); | 432 map->add_gsub_pause (NULL); |
239 } | 433 } |
240 map->add_gsub_pause (final_reordering); | 434 map->add_gsub_pause (final_reordering); |
241 for (; i < INDIC_NUM_FEATURES; i++) { | 435 for (; i < INDIC_NUM_FEATURES; i++) { |
242 map->add_bool_feature (indic_features[i].tag, indic_features[i].is_global); | 436 map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANU
AL_ZWJ); |
243 } | 437 } |
244 } | 438 } |
245 | 439 |
246 static void | 440 static void |
247 override_features_indic (hb_ot_shape_planner_t *plan) | 441 override_features_indic (hb_ot_shape_planner_t *plan) |
248 { | 442 { |
249 /* Uniscribe does not apply 'kern'. */ | 443 /* Uniscribe does not apply 'kern'. */ |
250 if (indic_options ().uniscribe_bug_compatible) | 444 if (hb_options ().uniscribe_bug_compatible) |
251 plan->map.add_feature (HB_TAG('k','e','r','n'), 0, true); | 445 plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL); |
252 | 446 |
253 plan->map.add_feature (HB_TAG('l','i','g','a'), 0, true); | 447 plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL); |
254 } | 448 } |
255 | 449 |
256 | 450 |
257 struct would_substitute_feature_t | 451 struct would_substitute_feature_t |
258 { | 452 { |
259 inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag) | 453 inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag) |
260 { | 454 { |
261 map->get_stage_lookups (0/*GSUB*/, | 455 map->get_stage_lookups (0/*GSUB*/, |
262 map->get_feature_stage (0/*GSUB*/, feature_tag), | 456 map->get_feature_stage (0/*GSUB*/, feature_tag), |
263 &lookups, &count); | 457 &lookups, &count); |
264 } | 458 } |
265 | 459 |
266 inline bool would_substitute (hb_codepoint_t *glyphs, | 460 inline bool would_substitute (const hb_codepoint_t *glyphs, |
267 » » » » unsigned int glyphs_count, | 461 » » » » unsigned int glyphs_count, |
268 » » » » bool zero_context, | 462 » » » » bool zero_context, |
269 » » » » hb_face_t *face) const | 463 » » » » hb_face_t *face) const |
270 { | 464 { |
271 for (unsigned int i = 0; i < count; i++) | 465 for (unsigned int i = 0; i < count; i++) |
272 if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, gly
phs, glyphs_count, zero_context)) | 466 if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, gly
phs, glyphs_count, zero_context)) |
273 return true; | 467 return true; |
274 return false; | 468 return false; |
275 } | 469 } |
276 | 470 |
277 private: | 471 private: |
278 const hb_ot_map_t::lookup_map_t *lookups; | 472 const hb_ot_map_t::lookup_map_t *lookups; |
279 unsigned int count; | 473 unsigned int count; |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 | 525 |
332 indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chos
en_script[0] & 0x000000FF) != '2'); | 526 indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chos
en_script[0] & 0x000000FF) != '2'); |
333 indic_plan->virama_glyph = (hb_codepoint_t) -1; | 527 indic_plan->virama_glyph = (hb_codepoint_t) -1; |
334 | 528 |
335 indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f')); | 529 indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f')); |
336 indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f')); | 530 indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f')); |
337 indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f')); | 531 indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f')); |
338 indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f')); | 532 indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f')); |
339 | 533 |
340 for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) | 534 for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) |
341 indic_plan->mask_array[i] = indic_features[i].is_global ? 0 : plan->map.get_
1_mask (indic_features[i].tag); | 535 indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? |
| 536 » » » » 0 : plan->map.get_1_mask (indic_features[i].tag
); |
342 | 537 |
343 return indic_plan; | 538 return indic_plan; |
344 } | 539 } |
345 | 540 |
346 static void | 541 static void |
347 data_destroy_indic (void *data) | 542 data_destroy_indic (void *data) |
348 { | 543 { |
349 free (data); | 544 free (data); |
350 } | 545 } |
351 | 546 |
352 static indic_position_t | 547 static indic_position_t |
353 consonant_position_from_face (const indic_shape_plan_t *indic_plan, | 548 consonant_position_from_face (const indic_shape_plan_t *indic_plan, |
354 » » » hb_codepoint_t *glyphs, unsigned int glyphs_len, | 549 » » » const hb_codepoint_t glyphs[2], |
355 » » » hb_face_t *face) | 550 » » » hb_face_t *face) |
356 { | 551 { |
| 552 /* For old-spec, the order of glyphs is Consonant,Virama, |
| 553 * whereas for new-spec, it's Virama,Consonant. However, |
| 554 * some broken fonts (like Free Sans) simply copied lookups |
| 555 * from old-spec to new-spec without modification. |
| 556 * And oddly enough, Uniscribe seems to respect those lookups. |
| 557 * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds |
| 558 * base at 0. The font however, only has lookups matching |
| 559 * 930,94D in 'blwf', not the expected 94D,930 (with new-spec |
| 560 * table). As such, we simply match both sequences. Seems |
| 561 * to work. */ |
357 bool zero_context = indic_plan->is_old_spec ? false : true; | 562 bool zero_context = indic_plan->is_old_spec ? false : true; |
358 if (indic_plan->pref.would_substitute (glyphs, glyphs_len, zero_context, face)
) return POS_BELOW_C; | 563 hb_codepoint_t glyphs_r[2] = {glyphs[1], glyphs[0]}; |
359 if (indic_plan->blwf.would_substitute (glyphs, glyphs_len, zero_context, face)
) return POS_BELOW_C; | 564 if (indic_plan->pref.would_substitute (glyphs , 2, zero_context, face) || |
360 if (indic_plan->pstf.would_substitute (glyphs, glyphs_len, zero_context, face)
) return POS_POST_C; | 565 indic_plan->pref.would_substitute (glyphs_r, 2, zero_context, face)) |
| 566 return POS_POST_C; |
| 567 if (indic_plan->blwf.would_substitute (glyphs , 2, zero_context, face) || |
| 568 indic_plan->blwf.would_substitute (glyphs_r, 2, zero_context, face)) |
| 569 return POS_BELOW_C; |
| 570 if (indic_plan->pstf.would_substitute (glyphs , 2, zero_context, face) || |
| 571 indic_plan->pstf.would_substitute (glyphs_r, 2, zero_context, face)) |
| 572 return POS_POST_C; |
361 return POS_BASE_C; | 573 return POS_BASE_C; |
362 } | 574 } |
363 | 575 |
364 | 576 |
365 enum syllable_type_t { | 577 enum syllable_type_t { |
366 consonant_syllable, | 578 consonant_syllable, |
367 vowel_syllable, | 579 vowel_syllable, |
368 standalone_cluster, | 580 standalone_cluster, |
369 broken_cluster, | 581 broken_cluster, |
370 non_indic_cluster, | 582 non_indic_cluster, |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 | 620 |
409 | 621 |
410 | 622 |
411 static void | 623 static void |
412 update_consonant_positions (const hb_ot_shape_plan_t *plan, | 624 update_consonant_positions (const hb_ot_shape_plan_t *plan, |
413 hb_font_t *font, | 625 hb_font_t *font, |
414 hb_buffer_t *buffer) | 626 hb_buffer_t *buffer) |
415 { | 627 { |
416 const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data
; | 628 const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data
; |
417 | 629 |
418 unsigned int consonant_pos = indic_plan->is_old_spec ? 0 : 1; | |
419 hb_codepoint_t glyphs[2]; | 630 hb_codepoint_t glyphs[2]; |
420 if (indic_plan->get_virama_glyph (font, &glyphs[1 - consonant_pos])) | 631 if (indic_plan->get_virama_glyph (font, &glyphs[0])) |
421 { | 632 { |
422 hb_face_t *face = font->face; | 633 hb_face_t *face = font->face; |
423 unsigned int count = buffer->len; | 634 unsigned int count = buffer->len; |
424 for (unsigned int i = 0; i < count; i++) | 635 for (unsigned int i = 0; i < count; i++) |
425 if (buffer->info[i].indic_position() == POS_BASE_C) { | 636 if (buffer->info[i].indic_position() == POS_BASE_C) { |
426 » glyphs[consonant_pos] = buffer->info[i].codepoint; | 637 » glyphs[1] = buffer->info[i].codepoint; |
427 » buffer->info[i].indic_position() = consonant_position_from_face (indic_p
lan, glyphs, 2, face); | 638 » buffer->info[i].indic_position() = consonant_position_from_face (indic_p
lan, glyphs, face); |
428 } | 639 } |
429 } | 640 } |
430 } | 641 } |
431 | 642 |
432 | 643 |
433 /* Rules from: | 644 /* Rules from: |
434 * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */ | 645 * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */ |
435 | 646 |
436 static void | 647 static void |
437 initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, | 648 initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
515 base = i; | 726 base = i; |
516 break; | 727 break; |
517 } | 728 } |
518 if (info[i].indic_position() == POS_BELOW_C) | 729 if (info[i].indic_position() == POS_BELOW_C) |
519 seen_below = true; | 730 seen_below = true; |
520 | 731 |
521 /* -> or that is not a pre-base reordering Ra, | 732 /* -> or that is not a pre-base reordering Ra, |
522 * | 733 * |
523 * IMPLEMENTATION NOTES: | 734 * IMPLEMENTATION NOTES: |
524 * | 735 * |
525 » * Our pre-base reordering Ra's are marked POS_BELOW, so will be ski
pped | 736 » * Our pre-base reordering Ra's are marked POS_POST_C, so will be sk
ipped |
526 * by the logic above already. | 737 * by the logic above already. |
527 */ | 738 */ |
528 | 739 |
529 /* -> or arrive at the first consonant. The consonant stopped at wil
l | 740 /* -> or arrive at the first consonant. The consonant stopped at wil
l |
530 * be the base. */ | 741 * be the base. */ |
531 base = i; | 742 base = i; |
532 } | 743 } |
533 else | 744 else |
534 { | 745 { |
535 /* A ZWJ after a Halant stops the base search, and requests an expli
cit | 746 /* A ZWJ after a Halant stops the base search, and requests an expli
cit |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 info[i].indic_position() = POS_BELOW_C; | 781 info[i].indic_position() = POS_BELOW_C; |
571 } | 782 } |
572 break; | 783 break; |
573 } | 784 } |
574 | 785 |
575 /* -> If the syllable starts with Ra + Halant (in a script that has Reph) | 786 /* -> If the syllable starts with Ra + Halant (in a script that has Reph) |
576 * and has more than one consonant, Ra is excluded from candidates for | 787 * and has more than one consonant, Ra is excluded from candidates for |
577 * base consonants. | 788 * base consonants. |
578 * | 789 * |
579 * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */ | 790 * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */ |
580 if (has_reph && base == start && start - limit <= 2) { | 791 if (has_reph && base == start && limit - base <= 2) { |
581 /* Have no other consonant, so Reph is not formed and Ra becomes base. */ | 792 /* Have no other consonant, so Reph is not formed and Ra becomes base. */ |
582 has_reph = false; | 793 has_reph = false; |
583 } | 794 } |
584 } | 795 } |
585 | 796 |
586 if (base < end) | |
587 info[base].indic_position() = POS_BASE_C; | |
588 | |
589 | 797 |
590 /* 2. Decompose and reorder Matras: | 798 /* 2. Decompose and reorder Matras: |
591 * | 799 * |
592 * Each matra and any syllable modifier sign in the cluster are moved to the | 800 * Each matra and any syllable modifier sign in the cluster are moved to the |
593 * appropriate position relative to the consonant(s) in the cluster. The | 801 * appropriate position relative to the consonant(s) in the cluster. The |
594 * shaping engine decomposes two- or three-part matras into their constituent | 802 * shaping engine decomposes two- or three-part matras into their constituent |
595 * parts before any repositioning. Matra characters are classified by which | 803 * parts before any repositioning. Matra characters are classified by which |
596 * consonant in a conjunct they have affinity for and are reordered to the | 804 * consonant in a conjunct they have affinity for and are reordered to the |
597 * following positions: | 805 * following positions: |
598 * | 806 * |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
637 break; | 845 break; |
638 } | 846 } |
639 break; | 847 break; |
640 } | 848 } |
641 | 849 |
642 /* Handle beginning Ra */ | 850 /* Handle beginning Ra */ |
643 if (has_reph) | 851 if (has_reph) |
644 info[start].indic_position() = POS_RA_TO_BECOME_REPH; | 852 info[start].indic_position() = POS_RA_TO_BECOME_REPH; |
645 | 853 |
646 /* For old-style Indic script tags, move the first post-base Halant after | 854 /* For old-style Indic script tags, move the first post-base Halant after |
647 * last consonant. */ | 855 * last consonant. Only do this if there is *not* a Halant after last |
| 856 * consonant. Otherwise it becomes messy. */ |
648 if (indic_plan->is_old_spec) { | 857 if (indic_plan->is_old_spec) { |
649 for (unsigned int i = base + 1; i < end; i++) | 858 for (unsigned int i = base + 1; i < end; i++) |
650 if (info[i].indic_category() == OT_H) { | 859 if (info[i].indic_category() == OT_H) { |
651 unsigned int j; | 860 unsigned int j; |
652 for (j = end - 1; j > i; j--) | 861 for (j = end - 1; j > i; j--) |
653 » if (is_consonant (info[j])) | 862 » if (is_consonant (info[j]) || info[j].indic_category() == OT_H) |
654 break; | 863 break; |
655 » if (j > i) { | 864 » if (info[j].indic_category() != OT_H && j > i) { |
656 /* Move Halant to after last consonant. */ | 865 /* Move Halant to after last consonant. */ |
657 hb_glyph_info_t t = info[i]; | 866 hb_glyph_info_t t = info[i]; |
658 memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0])); | 867 memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0])); |
659 info[j] = t; | 868 info[j] = t; |
660 } | 869 } |
661 break; | 870 break; |
662 } | 871 } |
663 } | 872 } |
664 | 873 |
665 /* Attach misc marks to previous char to move with them. */ | 874 /* Attach misc marks to previous char to move with them. */ |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
738 /* Base */ | 947 /* Base */ |
739 mask = 0; | 948 mask = 0; |
740 if (base < end) | 949 if (base < end) |
741 info[base].mask |= mask; | 950 info[base].mask |= mask; |
742 /* Post-base */ | 951 /* Post-base */ |
743 mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_p
lan->mask_array[PSTF]; | 952 mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_p
lan->mask_array[PSTF]; |
744 for (unsigned int i = base + 1; i < end; i++) | 953 for (unsigned int i = base + 1; i < end; i++) |
745 info[i].mask |= mask; | 954 info[i].mask |= mask; |
746 } | 955 } |
747 | 956 |
| 957 if (indic_plan->is_old_spec && |
| 958 buffer->props.script == HB_SCRIPT_DEVANAGARI) |
| 959 { |
| 960 /* Old-spec eye-lash Ra needs special handling. From the |
| 961 * spec: |
| 962 * |
| 963 * "The feature 'below-base form' is applied to consonants |
| 964 * having below-base forms and following the base consonant. |
| 965 * The exception is vattu, which may appear below half forms |
| 966 * as well as below the base glyph. The feature 'below-base |
| 967 * form' will be applied to all such occurrences of Ra as well." |
| 968 * |
| 969 * Test case: U+0924,U+094D,U+0930,U+094d,U+0915 |
| 970 * with Sanskrit 2003 font. |
| 971 * |
| 972 * However, note that Ra,Halant,ZWJ is the correct way to |
| 973 * request eyelash form of Ra, so we wouldbn't inhibit it |
| 974 * in that sequence. |
| 975 * |
| 976 * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915 |
| 977 */ |
| 978 for (unsigned int i = start; i + 1 < base; i++) |
| 979 if (info[i ].indic_category() == OT_Ra && |
| 980 info[i+1].indic_category() == OT_H && |
| 981 (i + 2 == base || |
| 982 info[i+2].indic_category() != OT_ZWJ)) |
| 983 { |
| 984 info[i ].mask |= indic_plan->mask_array[BLWF]; |
| 985 info[i+1].mask |= indic_plan->mask_array[BLWF]; |
| 986 } |
| 987 } |
| 988 |
748 if (indic_plan->mask_array[PREF] && base + 2 < end) | 989 if (indic_plan->mask_array[PREF] && base + 2 < end) |
749 { | 990 { |
750 /* Find a Halant,Ra sequence and mark it for pre-base reordering processing.
*/ | 991 /* Find a Halant,Ra sequence and mark it for pre-base reordering processing.
*/ |
751 for (unsigned int i = base + 1; i + 1 < end; i++) { | 992 for (unsigned int i = base + 1; i + 1 < end; i++) { |
752 hb_codepoint_t glyphs[2] = {info[i].codepoint, info[i + 1].codepoint}; | 993 hb_codepoint_t glyphs[2] = {info[i].codepoint, info[i + 1].codepoint}; |
753 if (indic_plan->pref.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true
, face)) | 994 if (indic_plan->pref.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true
, face)) |
754 { | 995 { |
755 info[i++].mask |= indic_plan->mask_array[PREF]; | 996 info[i++].mask |= indic_plan->mask_array[PREF]; |
756 info[i++].mask |= indic_plan->mask_array[PREF]; | 997 info[i++].mask |= indic_plan->mask_array[PREF]; |
757 | 998 |
(...skipping 13 matching lines...) Expand all Loading... |
771 | 1012 |
772 /* Apply ZWJ/ZWNJ effects */ | 1013 /* Apply ZWJ/ZWNJ effects */ |
773 for (unsigned int i = start + 1; i < end; i++) | 1014 for (unsigned int i = start + 1; i < end; i++) |
774 if (is_joiner (info[i])) { | 1015 if (is_joiner (info[i])) { |
775 bool non_joiner = info[i].indic_category() == OT_ZWNJ; | 1016 bool non_joiner = info[i].indic_category() == OT_ZWNJ; |
776 unsigned int j = i; | 1017 unsigned int j = i; |
777 | 1018 |
778 do { | 1019 do { |
779 j--; | 1020 j--; |
780 | 1021 |
781 » /* A ZWJ disables CJCT, however, it's mere presence is enough | 1022 » /* ZWJ/ZWNJ should disable CJCT. They do that by simply |
782 » * to disable ligation. No explicit action needed. */ | 1023 » * being there, since we don't skip them for the CJCT |
| 1024 » * feature (ie. F_MANUAL_ZWJ) */ |
783 | 1025 |
784 /* A ZWNJ disables HALF. */ | 1026 /* A ZWNJ disables HALF. */ |
785 if (non_joiner) | 1027 if (non_joiner) |
786 info[j].mask &= ~indic_plan->mask_array[HALF]; | 1028 info[j].mask &= ~indic_plan->mask_array[HALF]; |
787 | 1029 |
788 } while (j > start && !is_consonant (info[j])); | 1030 } while (j > start && !is_consonant (info[j])); |
789 } | 1031 } |
790 } | 1032 } |
791 | 1033 |
792 | 1034 |
793 static void | 1035 static void |
794 initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan, | 1036 initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan, |
795 hb_face_t *face, | 1037 hb_face_t *face, |
796 hb_buffer_t *buffer, | 1038 hb_buffer_t *buffer, |
797 unsigned int start, unsigned int end) | 1039 unsigned int start, unsigned int end) |
798 { | 1040 { |
799 /* We made the vowels look like consonants. So let's call the consonant logic
! */ | 1041 /* We made the vowels look like consonants. So let's call the consonant logic
! */ |
800 initial_reordering_consonant_syllable (plan, face, buffer, start, end); | 1042 initial_reordering_consonant_syllable (plan, face, buffer, start, end); |
801 } | 1043 } |
802 | 1044 |
803 static void | 1045 static void |
804 initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, | 1046 initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, |
805 hb_face_t *face, | 1047 hb_face_t *face, |
806 hb_buffer_t *buffer, | 1048 hb_buffer_t *buffer, |
807 unsigned int start, unsigned int end) | 1049 unsigned int start, unsigned int end) |
808 { | 1050 { |
809 /* We treat NBSP/dotted-circle as if they are consonants, so we should just ch
ain. | 1051 /* We treat NBSP/dotted-circle as if they are consonants, so we should just ch
ain. |
810 * Only if not in compatibility mode that is... */ | 1052 * Only if not in compatibility mode that is... */ |
811 | 1053 |
812 if (indic_options ().uniscribe_bug_compatible) | 1054 if (hb_options ().uniscribe_bug_compatible) |
813 { | 1055 { |
814 /* For dotted-circle, this is what Uniscribe does: | 1056 /* For dotted-circle, this is what Uniscribe does: |
815 * If dotted-circle is the last glyph, it just does nothing. | 1057 * If dotted-circle is the last glyph, it just does nothing. |
816 * Ie. It doesn't form Reph. */ | 1058 * Ie. It doesn't form Reph. */ |
817 if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE) | 1059 if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE) |
818 return; | 1060 return; |
819 } | 1061 } |
820 | 1062 |
821 initial_reordering_consonant_syllable (plan, face, buffer, start, end); | 1063 initial_reordering_consonant_syllable (plan, face, buffer, start, end); |
822 } | 1064 } |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
955 */ | 1197 */ |
956 | 1198 |
957 /* Find base again */ | 1199 /* Find base again */ |
958 unsigned int base; | 1200 unsigned int base; |
959 for (base = start; base < end; base++) | 1201 for (base = start; base < end; base++) |
960 if (info[base].indic_position() >= POS_BASE_C) { | 1202 if (info[base].indic_position() >= POS_BASE_C) { |
961 if (start < base && info[base].indic_position() > POS_BASE_C) | 1203 if (start < base && info[base].indic_position() > POS_BASE_C) |
962 base--; | 1204 base--; |
963 break; | 1205 break; |
964 } | 1206 } |
| 1207 if (base == end && start < base && |
| 1208 info[base - 1].indic_category() != OT_ZWJ) |
| 1209 base--; |
| 1210 while (start < base && |
| 1211 (info[base].indic_category() == OT_H || |
| 1212 info[base].indic_category() == OT_N)) |
| 1213 base--; |
965 | 1214 |
966 | 1215 |
967 /* o Reorder matras: | 1216 /* o Reorder matras: |
968 * | 1217 * |
969 * If a pre-base matra character had been reordered before applying basic | 1218 * If a pre-base matra character had been reordered before applying basic |
970 * features, the glyph can be moved closer to the main consonant based on | 1219 * features, the glyph can be moved closer to the main consonant based on |
971 * whether half-forms had been formed. Actual position for the matra is | 1220 * whether half-forms had been formed. Actual position for the matra is |
972 * defined as “after last standalone halant glyph, after initial matra | 1221 * defined as “after last standalone halant glyph, after initial matra |
973 * position and before the main consonant”. If ZWJ or ZWNJ follow this | 1222 * position and before the main consonant”. If ZWJ or ZWNJ follow this |
974 * halant, position is moved after it. | 1223 * halant, position is moved after it. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1006 if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M) | 1255 if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M) |
1007 { | 1256 { |
1008 /* Now go see if there's actually any matras... */ | 1257 /* Now go see if there's actually any matras... */ |
1009 for (unsigned int i = new_pos; i > start; i--) | 1258 for (unsigned int i = new_pos; i > start; i--) |
1010 if (info[i - 1].indic_position () == POS_PRE_M) | 1259 if (info[i - 1].indic_position () == POS_PRE_M) |
1011 { | 1260 { |
1012 unsigned int old_pos = i - 1; | 1261 unsigned int old_pos = i - 1; |
1013 hb_glyph_info_t tmp = info[old_pos]; | 1262 hb_glyph_info_t tmp = info[old_pos]; |
1014 memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * siz
eof (info[0])); | 1263 memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * siz
eof (info[0])); |
1015 info[new_pos] = tmp; | 1264 info[new_pos] = tmp; |
| 1265 if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. *
/ |
| 1266 base--; |
1016 new_pos--; | 1267 new_pos--; |
1017 } | 1268 } |
1018 buffer->merge_clusters (new_pos, MIN (end, base + 1)); | 1269 buffer->merge_clusters (new_pos, MIN (end, base + 1)); |
1019 } else { | 1270 } else { |
1020 for (unsigned int i = start; i < base; i++) | 1271 for (unsigned int i = start; i < base; i++) |
1021 if (info[i].indic_position () == POS_PRE_M) { | 1272 if (info[i].indic_position () == POS_PRE_M) { |
1022 buffer->merge_clusters (i, MIN (end, base + 1)); | 1273 buffer->merge_clusters (i, MIN (end, base + 1)); |
1023 break; | 1274 break; |
1024 } | 1275 } |
1025 } | 1276 } |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1064 * | 1315 * |
1065 * Note: in old-implementation fonts, where classifications were | 1316 * Note: in old-implementation fonts, where classifications were |
1066 * fixed in shaping engine, there was no case where reph position | 1317 * fixed in shaping engine, there was no case where reph position |
1067 * will be found on this step. | 1318 * will be found on this step. |
1068 */ | 1319 */ |
1069 { | 1320 { |
1070 new_reph_pos = start + 1; | 1321 new_reph_pos = start + 1; |
1071 while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos])) | 1322 while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos])) |
1072 new_reph_pos++; | 1323 new_reph_pos++; |
1073 | 1324 |
1074 if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) { | 1325 if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) |
| 1326 { |
1075 /* ->If ZWJ or ZWNJ are following this halant, position is moved after i
t. */ | 1327 /* ->If ZWJ or ZWNJ are following this halant, position is moved after i
t. */ |
1076 if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) | 1328 if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) |
1077 new_reph_pos++; | 1329 new_reph_pos++; |
1078 goto reph_move; | 1330 goto reph_move; |
1079 } | 1331 } |
1080 } | 1332 } |
1081 | 1333 |
1082 /* 3. If reph should be repositioned after the main consonant: find th
e | 1334 /* 3. If reph should be repositioned after the main consonant: find th
e |
1083 * first consonant not ligated with main, or find the first | 1335 * first consonant not ligated with main, or find the first |
1084 * consonant that is not a potential pre-base reordering Ra. | 1336 * consonant that is not a potential pre-base reordering Ra. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1116 * is post-main, it will skip above-base matras that also have a | 1368 * is post-main, it will skip above-base matras that also have a |
1117 * post-main position. | 1369 * post-main position. |
1118 */ | 1370 */ |
1119 reph_step_5: | 1371 reph_step_5: |
1120 { | 1372 { |
1121 /* Copied from step 2. */ | 1373 /* Copied from step 2. */ |
1122 new_reph_pos = start + 1; | 1374 new_reph_pos = start + 1; |
1123 while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos])) | 1375 while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos])) |
1124 new_reph_pos++; | 1376 new_reph_pos++; |
1125 | 1377 |
1126 if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) { | 1378 if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) |
| 1379 { |
1127 /* ->If ZWJ or ZWNJ are following this halant, position is moved after i
t. */ | 1380 /* ->If ZWJ or ZWNJ are following this halant, position is moved after i
t. */ |
1128 if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) | 1381 if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) |
1129 new_reph_pos++; | 1382 new_reph_pos++; |
1130 goto reph_move; | 1383 goto reph_move; |
1131 } | 1384 } |
1132 } | 1385 } |
1133 | 1386 |
1134 /* 6. Otherwise, reorder reph to the end of the syllable. | 1387 /* 6. Otherwise, reorder reph to the end of the syllable. |
1135 */ | 1388 */ |
1136 { | 1389 { |
1137 new_reph_pos = end - 1; | 1390 new_reph_pos = end - 1; |
1138 while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_
SMVD) | 1391 while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_
SMVD) |
1139 new_reph_pos--; | 1392 new_reph_pos--; |
1140 | 1393 |
1141 /* | 1394 /* |
1142 * If the Reph is to be ending up after a Matra,Halant sequence, | 1395 * If the Reph is to be ending up after a Matra,Halant sequence, |
1143 * position it before that Halant so it can interact with the Matra. | 1396 * position it before that Halant so it can interact with the Matra. |
1144 * However, if it's a plain Consonant,Halant we shouldn't do that. | 1397 * However, if it's a plain Consonant,Halant we shouldn't do that. |
1145 * Uniscribe doesn't do this. | 1398 * Uniscribe doesn't do this. |
1146 * TEST: U+0930,U+094D,U+0915,U+094B,U+094D | 1399 * TEST: U+0930,U+094D,U+0915,U+094B,U+094D |
1147 */ | 1400 */ |
1148 if (!indic_options ().uniscribe_bug_compatible && | 1401 if (!hb_options ().uniscribe_bug_compatible && |
1149 unlikely (is_halant_or_coeng (info[new_reph_pos]))) { | 1402 unlikely (is_halant_or_coeng (info[new_reph_pos]))) { |
1150 for (unsigned int i = base + 1; i < new_reph_pos; i++) | 1403 for (unsigned int i = base + 1; i < new_reph_pos; i++) |
1151 if (info[i].indic_category() == OT_M) { | 1404 if (info[i].indic_category() == OT_M) { |
1152 /* Ok, got it. */ | 1405 /* Ok, got it. */ |
1153 new_reph_pos--; | 1406 new_reph_pos--; |
1154 } | 1407 } |
1155 } | 1408 } |
1156 goto reph_move; | 1409 goto reph_move; |
1157 } | 1410 } |
1158 | 1411 |
1159 reph_move: | 1412 reph_move: |
1160 { | 1413 { |
1161 /* Yay, one big cluster! Merge before moving. */ | 1414 /* Yay, one big cluster! Merge before moving. */ |
1162 buffer->merge_clusters (start, end); | 1415 buffer->merge_clusters (start, end); |
1163 | 1416 |
1164 /* Move */ | 1417 /* Move */ |
1165 hb_glyph_info_t reph = info[start]; | 1418 hb_glyph_info_t reph = info[start]; |
1166 memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (
info[0])); | 1419 memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (
info[0])); |
1167 info[new_reph_pos] = reph; | 1420 info[new_reph_pos] = reph; |
| 1421 if (start < base && base <= new_reph_pos) |
| 1422 base--; |
1168 } | 1423 } |
1169 } | 1424 } |
1170 | 1425 |
1171 | 1426 |
1172 /* o Reorder pre-base reordering consonants: | 1427 /* o Reorder pre-base reordering consonants: |
1173 * | 1428 * |
1174 * If a pre-base reordering consonant is found, reorder it according to | 1429 * If a pre-base reordering consonant is found, reorder it according to |
1175 * the following rules: | 1430 * the following rules: |
1176 */ | 1431 */ |
1177 | 1432 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1213 for (unsigned int i = base + 1; i < old_pos; i++) | 1468 for (unsigned int i = base + 1; i < old_pos; i++) |
1214 if (info[i].indic_category() == OT_M) | 1469 if (info[i].indic_category() == OT_M) |
1215 { | 1470 { |
1216 new_pos--; | 1471 new_pos--; |
1217 break; | 1472 break; |
1218 } | 1473 } |
1219 } | 1474 } |
1220 } | 1475 } |
1221 | 1476 |
1222 if (new_pos > start && is_halant_or_coeng (info[new_pos - 1])) | 1477 if (new_pos > start && is_halant_or_coeng (info[new_pos - 1])) |
| 1478 { |
1223 /* -> If ZWJ or ZWNJ follow this halant, position is moved after it.
*/ | 1479 /* -> If ZWJ or ZWNJ follow this halant, position is moved after it.
*/ |
1224 if (new_pos < end && is_joiner (info[new_pos])) | 1480 if (new_pos < end && is_joiner (info[new_pos])) |
1225 new_pos++; | 1481 new_pos++; |
| 1482 } |
1226 | 1483 |
1227 { | 1484 { |
1228 unsigned int old_pos = i; | 1485 unsigned int old_pos = i; |
1229 buffer->merge_clusters (new_pos, old_pos + 1); | 1486 buffer->merge_clusters (new_pos, old_pos + 1); |
1230 hb_glyph_info_t tmp = info[old_pos]; | 1487 hb_glyph_info_t tmp = info[old_pos]; |
1231 memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * s
izeof (info[0])); | 1488 memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * s
izeof (info[0])); |
1232 info[new_pos] = tmp; | 1489 info[new_pos] = tmp; |
| 1490 if (new_pos <= base && base < old_pos) |
| 1491 base++; |
1233 } | 1492 } |
1234 } | 1493 } |
1235 | 1494 |
1236 break; | 1495 break; |
1237 } | 1496 } |
1238 } | 1497 } |
1239 | 1498 |
1240 | 1499 |
1241 /* Apply 'init' to the Left Matra if it's a word start. */ | 1500 /* Apply 'init' to the Left Matra if it's a word start. */ |
1242 if (info[start].indic_position () == POS_PRE_M && | 1501 if (info[start].indic_position () == POS_PRE_M && |
1243 (!start || | 1502 (!start || |
1244 !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) & | 1503 !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) & |
1245 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATE
GORY_NON_SPACING_MARK)))) | 1504 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATE
GORY_NON_SPACING_MARK)))) |
1246 info[start].mask |= indic_plan->mask_array[INIT]; | 1505 info[start].mask |= indic_plan->mask_array[INIT]; |
1247 | 1506 |
1248 | 1507 |
1249 /* | 1508 /* |
1250 * Finish off the clusters and go home! | 1509 * Finish off the clusters and go home! |
1251 */ | 1510 */ |
1252 if (indic_options ().uniscribe_bug_compatible) | 1511 if (hb_options ().uniscribe_bug_compatible) |
1253 { | 1512 { |
1254 /* Uniscribe merges the entire cluster. | 1513 /* Uniscribe merges the entire cluster. |
1255 * This means, half forms are submerged into the main consonants cluster. | 1514 * This means, half forms are submerged into the main consonants cluster. |
1256 * This is unnecessary, and makes cursor positioning harder, but that's what | 1515 * This is unnecessary, and makes cursor positioning harder, but that's what |
1257 * Uniscribe does. */ | 1516 * Uniscribe does. */ |
1258 buffer->merge_clusters (start, end); | 1517 buffer->merge_clusters (start, end); |
1259 } | 1518 } |
1260 } | 1519 } |
1261 | 1520 |
1262 | 1521 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1358 * The Uniscribe behavior is now documented in the newly published Sinhala | 1617 * The Uniscribe behavior is now documented in the newly published Sinhala |
1359 * spec in 2012: | 1618 * spec in 2012: |
1360 * | 1619 * |
1361 * http://www.microsoft.com/typography/OpenTypeDev/sinhala/intro.htm#shapi
ng | 1620 * http://www.microsoft.com/typography/OpenTypeDev/sinhala/intro.htm#shapi
ng |
1362 */ | 1621 */ |
1363 | 1622 |
1364 const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan-
>data; | 1623 const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan-
>data; |
1365 | 1624 |
1366 hb_codepoint_t glyph; | 1625 hb_codepoint_t glyph; |
1367 | 1626 |
1368 if (indic_options ().uniscribe_bug_compatible || | 1627 if (hb_options ().uniscribe_bug_compatible || |
1369 (c->font->get_glyph (ab, 0, &glyph) && | 1628 (c->font->get_glyph (ab, 0, &glyph) && |
1370 indic_plan->pstf.would_substitute (&glyph, 1, true, c->font->face))) | 1629 indic_plan->pstf.would_substitute (&glyph, 1, true, c->font->face))) |
1371 { | 1630 { |
1372 /* Ok, safe to use Uniscribe-style decomposition. */ | 1631 /* Ok, safe to use Uniscribe-style decomposition. */ |
1373 *a = 0x0DD9; | 1632 *a = 0x0DD9; |
1374 *b = ab; | 1633 *b = ab; |
1375 return true; | 1634 return true; |
1376 } | 1635 } |
1377 } | 1636 } |
1378 | 1637 |
(...skipping 22 matching lines...) Expand all Loading... |
1401 "indic", | 1660 "indic", |
1402 collect_features_indic, | 1661 collect_features_indic, |
1403 override_features_indic, | 1662 override_features_indic, |
1404 data_create_indic, | 1663 data_create_indic, |
1405 data_destroy_indic, | 1664 data_destroy_indic, |
1406 NULL, /* preprocess_text */ | 1665 NULL, /* preprocess_text */ |
1407 normalization_preference_indic, | 1666 normalization_preference_indic, |
1408 decompose_indic, | 1667 decompose_indic, |
1409 compose_indic, | 1668 compose_indic, |
1410 setup_masks_indic, | 1669 setup_masks_indic, |
1411 false, /* zero_width_attached_marks */ | 1670 HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, |
1412 false, /* fallback_position */ | 1671 false, /* fallback_position */ |
1413 }; | 1672 }; |
OLD | NEW |