| 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 |