1*2d1272b8SAndroid Build Coastguard Worker /*
2*2d1272b8SAndroid Build Coastguard Worker * Copyright © 2011,2012 Google, Inc.
3*2d1272b8SAndroid Build Coastguard Worker *
4*2d1272b8SAndroid Build Coastguard Worker * This is part of HarfBuzz, a text shaping library.
5*2d1272b8SAndroid Build Coastguard Worker *
6*2d1272b8SAndroid Build Coastguard Worker * Permission is hereby granted, without written agreement and without
7*2d1272b8SAndroid Build Coastguard Worker * license or royalty fees, to use, copy, modify, and distribute this
8*2d1272b8SAndroid Build Coastguard Worker * software and its documentation for any purpose, provided that the
9*2d1272b8SAndroid Build Coastguard Worker * above copyright notice and the following two paragraphs appear in
10*2d1272b8SAndroid Build Coastguard Worker * all copies of this software.
11*2d1272b8SAndroid Build Coastguard Worker *
12*2d1272b8SAndroid Build Coastguard Worker * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13*2d1272b8SAndroid Build Coastguard Worker * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14*2d1272b8SAndroid Build Coastguard Worker * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15*2d1272b8SAndroid Build Coastguard Worker * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16*2d1272b8SAndroid Build Coastguard Worker * DAMAGE.
17*2d1272b8SAndroid Build Coastguard Worker *
18*2d1272b8SAndroid Build Coastguard Worker * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19*2d1272b8SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20*2d1272b8SAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21*2d1272b8SAndroid Build Coastguard Worker * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22*2d1272b8SAndroid Build Coastguard Worker * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23*2d1272b8SAndroid Build Coastguard Worker *
24*2d1272b8SAndroid Build Coastguard Worker * Google Author(s): Behdad Esfahbod
25*2d1272b8SAndroid Build Coastguard Worker */
26*2d1272b8SAndroid Build Coastguard Worker
27*2d1272b8SAndroid Build Coastguard Worker #include "hb.hh"
28*2d1272b8SAndroid Build Coastguard Worker
29*2d1272b8SAndroid Build Coastguard Worker #ifndef HB_NO_OT_SHAPE
30*2d1272b8SAndroid Build Coastguard Worker
31*2d1272b8SAndroid Build Coastguard Worker #include "hb-ot-shaper-indic.hh"
32*2d1272b8SAndroid Build Coastguard Worker #include "hb-ot-shaper-indic-machine.hh"
33*2d1272b8SAndroid Build Coastguard Worker #include "hb-ot-shaper-vowel-constraints.hh"
34*2d1272b8SAndroid Build Coastguard Worker #include "hb-ot-layout.hh"
35*2d1272b8SAndroid Build Coastguard Worker
36*2d1272b8SAndroid Build Coastguard Worker
37*2d1272b8SAndroid Build Coastguard Worker /*
38*2d1272b8SAndroid Build Coastguard Worker * Indic shaper.
39*2d1272b8SAndroid Build Coastguard Worker */
40*2d1272b8SAndroid Build Coastguard Worker
41*2d1272b8SAndroid Build Coastguard Worker
42*2d1272b8SAndroid Build Coastguard Worker static inline void
set_indic_properties(hb_glyph_info_t & info)43*2d1272b8SAndroid Build Coastguard Worker set_indic_properties (hb_glyph_info_t &info)
44*2d1272b8SAndroid Build Coastguard Worker {
45*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t u = info.codepoint;
46*2d1272b8SAndroid Build Coastguard Worker unsigned int type = hb_indic_get_categories (u);
47*2d1272b8SAndroid Build Coastguard Worker
48*2d1272b8SAndroid Build Coastguard Worker info.indic_category() = (indic_category_t) (type & 0xFFu);
49*2d1272b8SAndroid Build Coastguard Worker info.indic_position() = (indic_position_t) (type >> 8);
50*2d1272b8SAndroid Build Coastguard Worker }
51*2d1272b8SAndroid Build Coastguard Worker
52*2d1272b8SAndroid Build Coastguard Worker
53*2d1272b8SAndroid Build Coastguard Worker static inline bool
is_one_of(const hb_glyph_info_t & info,unsigned int flags)54*2d1272b8SAndroid Build Coastguard Worker is_one_of (const hb_glyph_info_t &info, unsigned int flags)
55*2d1272b8SAndroid Build Coastguard Worker {
56*2d1272b8SAndroid Build Coastguard Worker /* If it ligated, all bets are off. */
57*2d1272b8SAndroid Build Coastguard Worker if (_hb_glyph_info_ligated (&info)) return false;
58*2d1272b8SAndroid Build Coastguard Worker return !!(FLAG_UNSAFE (info.indic_category()) & flags);
59*2d1272b8SAndroid Build Coastguard Worker }
60*2d1272b8SAndroid Build Coastguard Worker
61*2d1272b8SAndroid Build Coastguard Worker /* Note:
62*2d1272b8SAndroid Build Coastguard Worker *
63*2d1272b8SAndroid Build Coastguard Worker * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels
64*2d1272b8SAndroid Build Coastguard Worker * cannot happen in a consonant syllable. The plus side however is, we can call the
65*2d1272b8SAndroid Build Coastguard Worker * consonant syllable logic from the vowel syllable function and get it all right!
66*2d1272b8SAndroid Build Coastguard Worker *
67*2d1272b8SAndroid Build Coastguard Worker * Keep in sync with consonant_categories in the generator. */
68*2d1272b8SAndroid Build Coastguard Worker #define CONSONANT_FLAGS_INDIC (FLAG (I_Cat(C)) | FLAG (I_Cat(CS)) | FLAG (I_Cat(Ra)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(V)) | FLAG (I_Cat(PLACEHOLDER)) | FLAG (I_Cat(DOTTEDCIRCLE)))
69*2d1272b8SAndroid Build Coastguard Worker
70*2d1272b8SAndroid Build Coastguard Worker static inline bool
is_consonant(const hb_glyph_info_t & info)71*2d1272b8SAndroid Build Coastguard Worker is_consonant (const hb_glyph_info_t &info)
72*2d1272b8SAndroid Build Coastguard Worker {
73*2d1272b8SAndroid Build Coastguard Worker return is_one_of (info, CONSONANT_FLAGS_INDIC);
74*2d1272b8SAndroid Build Coastguard Worker }
75*2d1272b8SAndroid Build Coastguard Worker
76*2d1272b8SAndroid Build Coastguard Worker #define JOINER_FLAGS (FLAG (I_Cat(ZWJ)) | FLAG (I_Cat(ZWNJ)))
77*2d1272b8SAndroid Build Coastguard Worker
78*2d1272b8SAndroid Build Coastguard Worker static inline bool
is_joiner(const hb_glyph_info_t & info)79*2d1272b8SAndroid Build Coastguard Worker is_joiner (const hb_glyph_info_t &info)
80*2d1272b8SAndroid Build Coastguard Worker {
81*2d1272b8SAndroid Build Coastguard Worker return is_one_of (info, JOINER_FLAGS);
82*2d1272b8SAndroid Build Coastguard Worker }
83*2d1272b8SAndroid Build Coastguard Worker
84*2d1272b8SAndroid Build Coastguard Worker static inline bool
is_halant(const hb_glyph_info_t & info)85*2d1272b8SAndroid Build Coastguard Worker is_halant (const hb_glyph_info_t &info)
86*2d1272b8SAndroid Build Coastguard Worker {
87*2d1272b8SAndroid Build Coastguard Worker return is_one_of (info, FLAG (I_Cat(H)));
88*2d1272b8SAndroid Build Coastguard Worker }
89*2d1272b8SAndroid Build Coastguard Worker
90*2d1272b8SAndroid Build Coastguard Worker struct hb_indic_would_substitute_feature_t
91*2d1272b8SAndroid Build Coastguard Worker {
inithb_indic_would_substitute_feature_t92*2d1272b8SAndroid Build Coastguard Worker void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
93*2d1272b8SAndroid Build Coastguard Worker {
94*2d1272b8SAndroid Build Coastguard Worker zero_context = zero_context_;
95*2d1272b8SAndroid Build Coastguard Worker lookups = map->get_stage_lookups (0/*GSUB*/,
96*2d1272b8SAndroid Build Coastguard Worker map->get_feature_stage (0/*GSUB*/, feature_tag));
97*2d1272b8SAndroid Build Coastguard Worker }
98*2d1272b8SAndroid Build Coastguard Worker
would_substitutehb_indic_would_substitute_feature_t99*2d1272b8SAndroid Build Coastguard Worker bool would_substitute (const hb_codepoint_t *glyphs,
100*2d1272b8SAndroid Build Coastguard Worker unsigned int glyphs_count,
101*2d1272b8SAndroid Build Coastguard Worker hb_face_t *face) const
102*2d1272b8SAndroid Build Coastguard Worker {
103*2d1272b8SAndroid Build Coastguard Worker for (const auto &lookup : lookups)
104*2d1272b8SAndroid Build Coastguard Worker if (hb_ot_layout_lookup_would_substitute (face, lookup.index, glyphs, glyphs_count, zero_context))
105*2d1272b8SAndroid Build Coastguard Worker return true;
106*2d1272b8SAndroid Build Coastguard Worker return false;
107*2d1272b8SAndroid Build Coastguard Worker }
108*2d1272b8SAndroid Build Coastguard Worker
109*2d1272b8SAndroid Build Coastguard Worker private:
110*2d1272b8SAndroid Build Coastguard Worker hb_array_t<const hb_ot_map_t::lookup_map_t> lookups;
111*2d1272b8SAndroid Build Coastguard Worker bool zero_context;
112*2d1272b8SAndroid Build Coastguard Worker };
113*2d1272b8SAndroid Build Coastguard Worker
114*2d1272b8SAndroid Build Coastguard Worker
115*2d1272b8SAndroid Build Coastguard Worker /*
116*2d1272b8SAndroid Build Coastguard Worker * Indic configurations. Note that we do not want to keep every single script-specific
117*2d1272b8SAndroid Build Coastguard Worker * behavior in these tables necessarily. This should mainly be used for per-script
118*2d1272b8SAndroid Build Coastguard Worker * properties that are cheaper keeping here, than in the code. Ie. if, say, one and
119*2d1272b8SAndroid Build Coastguard Worker * only one script has an exception, that one script can be if'ed directly in the code,
120*2d1272b8SAndroid Build Coastguard Worker * instead of adding a new flag in these structs.
121*2d1272b8SAndroid Build Coastguard Worker */
122*2d1272b8SAndroid Build Coastguard Worker
123*2d1272b8SAndroid Build Coastguard Worker enum reph_position_t {
124*2d1272b8SAndroid Build Coastguard Worker REPH_POS_AFTER_MAIN = POS_AFTER_MAIN,
125*2d1272b8SAndroid Build Coastguard Worker REPH_POS_BEFORE_SUB = POS_BEFORE_SUB,
126*2d1272b8SAndroid Build Coastguard Worker REPH_POS_AFTER_SUB = POS_AFTER_SUB,
127*2d1272b8SAndroid Build Coastguard Worker REPH_POS_BEFORE_POST = POS_BEFORE_POST,
128*2d1272b8SAndroid Build Coastguard Worker REPH_POS_AFTER_POST = POS_AFTER_POST
129*2d1272b8SAndroid Build Coastguard Worker };
130*2d1272b8SAndroid Build Coastguard Worker enum reph_mode_t {
131*2d1272b8SAndroid Build Coastguard Worker REPH_MODE_IMPLICIT, /* Reph formed out of initial Ra,H sequence. */
132*2d1272b8SAndroid Build Coastguard Worker REPH_MODE_EXPLICIT, /* Reph formed out of initial Ra,H,ZWJ sequence. */
133*2d1272b8SAndroid Build Coastguard Worker REPH_MODE_LOG_REPHA /* Encoded Repha character, needs reordering. */
134*2d1272b8SAndroid Build Coastguard Worker };
135*2d1272b8SAndroid Build Coastguard Worker enum blwf_mode_t {
136*2d1272b8SAndroid Build Coastguard Worker BLWF_MODE_PRE_AND_POST, /* Below-forms feature applied to pre-base and post-base. */
137*2d1272b8SAndroid Build Coastguard Worker BLWF_MODE_POST_ONLY /* Below-forms feature applied to post-base only. */
138*2d1272b8SAndroid Build Coastguard Worker };
139*2d1272b8SAndroid Build Coastguard Worker struct indic_config_t
140*2d1272b8SAndroid Build Coastguard Worker {
141*2d1272b8SAndroid Build Coastguard Worker hb_script_t script;
142*2d1272b8SAndroid Build Coastguard Worker bool has_old_spec;
143*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t virama;
144*2d1272b8SAndroid Build Coastguard Worker reph_position_t reph_pos;
145*2d1272b8SAndroid Build Coastguard Worker reph_mode_t reph_mode;
146*2d1272b8SAndroid Build Coastguard Worker blwf_mode_t blwf_mode;
147*2d1272b8SAndroid Build Coastguard Worker };
148*2d1272b8SAndroid Build Coastguard Worker
149*2d1272b8SAndroid Build Coastguard Worker static const indic_config_t indic_configs[] =
150*2d1272b8SAndroid Build Coastguard Worker {
151*2d1272b8SAndroid Build Coastguard Worker /* Default. Should be first. */
152*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_INVALID, false, 0,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
153*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_DEVANAGARI,true, 0x094Du,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
154*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_BENGALI, true, 0x09CDu,REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
155*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_GURMUKHI, true, 0x0A4Du,REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
156*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_GUJARATI, true, 0x0ACDu,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
157*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_ORIYA, true, 0x0B4Du,REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
158*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_TAMIL, true, 0x0BCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
159*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_TELUGU, true, 0x0C4Du,REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY},
160*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_KANNADA, true, 0x0CCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY},
161*2d1272b8SAndroid Build Coastguard Worker {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST},
162*2d1272b8SAndroid Build Coastguard Worker };
163*2d1272b8SAndroid Build Coastguard Worker
164*2d1272b8SAndroid Build Coastguard Worker
165*2d1272b8SAndroid Build Coastguard Worker static const hb_ot_map_feature_t
166*2d1272b8SAndroid Build Coastguard Worker indic_features[] =
167*2d1272b8SAndroid Build Coastguard Worker {
168*2d1272b8SAndroid Build Coastguard Worker /*
169*2d1272b8SAndroid Build Coastguard Worker * Basic features.
170*2d1272b8SAndroid Build Coastguard Worker * These features are applied in order, one at a time, after initial_reordering,
171*2d1272b8SAndroid Build Coastguard Worker * constrained to the syllable.
172*2d1272b8SAndroid Build Coastguard Worker */
173*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('n','u','k','t'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
174*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('a','k','h','n'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
175*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('r','p','h','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
176*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('r','k','r','f'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
177*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
178*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
179*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
180*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('h','a','l','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
181*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
182*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('v','a','t','u'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
183*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
184*2d1272b8SAndroid Build Coastguard Worker /*
185*2d1272b8SAndroid Build Coastguard Worker * Other features.
186*2d1272b8SAndroid Build Coastguard Worker * These features are applied all at once, after final_reordering, constrained
187*2d1272b8SAndroid Build Coastguard Worker * to the syllable.
188*2d1272b8SAndroid Build Coastguard Worker * Default Bengali font in Windows for example has intermixed
189*2d1272b8SAndroid Build Coastguard Worker * lookups for init,pres,abvs,blws features.
190*2d1272b8SAndroid Build Coastguard Worker */
191*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('i','n','i','t'), F_MANUAL_JOINERS | F_PER_SYLLABLE},
192*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
193*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
194*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
195*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
196*2d1272b8SAndroid Build Coastguard Worker {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE},
197*2d1272b8SAndroid Build Coastguard Worker };
198*2d1272b8SAndroid Build Coastguard Worker
199*2d1272b8SAndroid Build Coastguard Worker /*
200*2d1272b8SAndroid Build Coastguard Worker * Must be in the same order as the indic_features array.
201*2d1272b8SAndroid Build Coastguard Worker */
202*2d1272b8SAndroid Build Coastguard Worker enum {
203*2d1272b8SAndroid Build Coastguard Worker _INDIC_NUKT,
204*2d1272b8SAndroid Build Coastguard Worker _INDIC_AKHN,
205*2d1272b8SAndroid Build Coastguard Worker INDIC_RPHF,
206*2d1272b8SAndroid Build Coastguard Worker _INDIC_RKRF,
207*2d1272b8SAndroid Build Coastguard Worker INDIC_PREF,
208*2d1272b8SAndroid Build Coastguard Worker INDIC_BLWF,
209*2d1272b8SAndroid Build Coastguard Worker INDIC_ABVF,
210*2d1272b8SAndroid Build Coastguard Worker INDIC_HALF,
211*2d1272b8SAndroid Build Coastguard Worker INDIC_PSTF,
212*2d1272b8SAndroid Build Coastguard Worker _INDIC_VATU,
213*2d1272b8SAndroid Build Coastguard Worker _INDIC_CJCT,
214*2d1272b8SAndroid Build Coastguard Worker
215*2d1272b8SAndroid Build Coastguard Worker INDIC_INIT,
216*2d1272b8SAndroid Build Coastguard Worker _INDIC_PRES,
217*2d1272b8SAndroid Build Coastguard Worker _INDIC_ABVS,
218*2d1272b8SAndroid Build Coastguard Worker _INDIC_BLWS,
219*2d1272b8SAndroid Build Coastguard Worker _INDIC_PSTS,
220*2d1272b8SAndroid Build Coastguard Worker _INDIC_HALN,
221*2d1272b8SAndroid Build Coastguard Worker
222*2d1272b8SAndroid Build Coastguard Worker INDIC_NUM_FEATURES,
223*2d1272b8SAndroid Build Coastguard Worker INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */
224*2d1272b8SAndroid Build Coastguard Worker };
225*2d1272b8SAndroid Build Coastguard Worker
226*2d1272b8SAndroid Build Coastguard Worker static bool
227*2d1272b8SAndroid Build Coastguard Worker setup_syllables_indic (const hb_ot_shape_plan_t *plan,
228*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font,
229*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer);
230*2d1272b8SAndroid Build Coastguard Worker static bool
231*2d1272b8SAndroid Build Coastguard Worker initial_reordering_indic (const hb_ot_shape_plan_t *plan,
232*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font,
233*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer);
234*2d1272b8SAndroid Build Coastguard Worker static bool
235*2d1272b8SAndroid Build Coastguard Worker final_reordering_indic (const hb_ot_shape_plan_t *plan,
236*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font,
237*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer);
238*2d1272b8SAndroid Build Coastguard Worker
239*2d1272b8SAndroid Build Coastguard Worker static void
collect_features_indic(hb_ot_shape_planner_t * plan)240*2d1272b8SAndroid Build Coastguard Worker collect_features_indic (hb_ot_shape_planner_t *plan)
241*2d1272b8SAndroid Build Coastguard Worker {
242*2d1272b8SAndroid Build Coastguard Worker hb_ot_map_builder_t *map = &plan->map;
243*2d1272b8SAndroid Build Coastguard Worker
244*2d1272b8SAndroid Build Coastguard Worker /* Do this before any lookups have been applied. */
245*2d1272b8SAndroid Build Coastguard Worker map->add_gsub_pause (setup_syllables_indic);
246*2d1272b8SAndroid Build Coastguard Worker
247*2d1272b8SAndroid Build Coastguard Worker map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE);
248*2d1272b8SAndroid Build Coastguard Worker /* The Indic specs do not require ccmp, but we apply it here since if
249*2d1272b8SAndroid Build Coastguard Worker * there is a use of it, it's typically at the beginning. */
250*2d1272b8SAndroid Build Coastguard Worker map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE);
251*2d1272b8SAndroid Build Coastguard Worker
252*2d1272b8SAndroid Build Coastguard Worker
253*2d1272b8SAndroid Build Coastguard Worker unsigned int i = 0;
254*2d1272b8SAndroid Build Coastguard Worker map->add_gsub_pause (initial_reordering_indic);
255*2d1272b8SAndroid Build Coastguard Worker
256*2d1272b8SAndroid Build Coastguard Worker for (; i < INDIC_BASIC_FEATURES; i++) {
257*2d1272b8SAndroid Build Coastguard Worker map->add_feature (indic_features[i]);
258*2d1272b8SAndroid Build Coastguard Worker map->add_gsub_pause (nullptr);
259*2d1272b8SAndroid Build Coastguard Worker }
260*2d1272b8SAndroid Build Coastguard Worker
261*2d1272b8SAndroid Build Coastguard Worker map->add_gsub_pause (final_reordering_indic);
262*2d1272b8SAndroid Build Coastguard Worker
263*2d1272b8SAndroid Build Coastguard Worker for (; i < INDIC_NUM_FEATURES; i++)
264*2d1272b8SAndroid Build Coastguard Worker map->add_feature (indic_features[i]);
265*2d1272b8SAndroid Build Coastguard Worker }
266*2d1272b8SAndroid Build Coastguard Worker
267*2d1272b8SAndroid Build Coastguard Worker static void
override_features_indic(hb_ot_shape_planner_t * plan)268*2d1272b8SAndroid Build Coastguard Worker override_features_indic (hb_ot_shape_planner_t *plan)
269*2d1272b8SAndroid Build Coastguard Worker {
270*2d1272b8SAndroid Build Coastguard Worker plan->map.disable_feature (HB_TAG('l','i','g','a'));
271*2d1272b8SAndroid Build Coastguard Worker plan->map.add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var
272*2d1272b8SAndroid Build Coastguard Worker }
273*2d1272b8SAndroid Build Coastguard Worker
274*2d1272b8SAndroid Build Coastguard Worker
275*2d1272b8SAndroid Build Coastguard Worker struct indic_shape_plan_t
276*2d1272b8SAndroid Build Coastguard Worker {
load_virama_glyphindic_shape_plan_t277*2d1272b8SAndroid Build Coastguard Worker bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
278*2d1272b8SAndroid Build Coastguard Worker {
279*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t glyph = virama_glyph;
280*2d1272b8SAndroid Build Coastguard Worker if (unlikely (glyph == (hb_codepoint_t) -1))
281*2d1272b8SAndroid Build Coastguard Worker {
282*2d1272b8SAndroid Build Coastguard Worker if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph))
283*2d1272b8SAndroid Build Coastguard Worker glyph = 0;
284*2d1272b8SAndroid Build Coastguard Worker /* Technically speaking, the spec says we should apply 'locl' to virama too.
285*2d1272b8SAndroid Build Coastguard Worker * Maybe one day... */
286*2d1272b8SAndroid Build Coastguard Worker
287*2d1272b8SAndroid Build Coastguard Worker /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph
288*2d1272b8SAndroid Build Coastguard Worker * during shape planning... Instead, overwrite it here. */
289*2d1272b8SAndroid Build Coastguard Worker virama_glyph = (int) glyph;
290*2d1272b8SAndroid Build Coastguard Worker }
291*2d1272b8SAndroid Build Coastguard Worker
292*2d1272b8SAndroid Build Coastguard Worker *pglyph = glyph;
293*2d1272b8SAndroid Build Coastguard Worker return glyph != 0;
294*2d1272b8SAndroid Build Coastguard Worker }
295*2d1272b8SAndroid Build Coastguard Worker
296*2d1272b8SAndroid Build Coastguard Worker const indic_config_t *config;
297*2d1272b8SAndroid Build Coastguard Worker
298*2d1272b8SAndroid Build Coastguard Worker bool is_old_spec;
299*2d1272b8SAndroid Build Coastguard Worker #ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE
300*2d1272b8SAndroid Build Coastguard Worker bool uniscribe_bug_compatible;
301*2d1272b8SAndroid Build Coastguard Worker #else
302*2d1272b8SAndroid Build Coastguard Worker static constexpr bool uniscribe_bug_compatible = false;
303*2d1272b8SAndroid Build Coastguard Worker #endif
304*2d1272b8SAndroid Build Coastguard Worker mutable hb_atomic_int_t virama_glyph;
305*2d1272b8SAndroid Build Coastguard Worker
306*2d1272b8SAndroid Build Coastguard Worker hb_indic_would_substitute_feature_t rphf;
307*2d1272b8SAndroid Build Coastguard Worker hb_indic_would_substitute_feature_t pref;
308*2d1272b8SAndroid Build Coastguard Worker hb_indic_would_substitute_feature_t blwf;
309*2d1272b8SAndroid Build Coastguard Worker hb_indic_would_substitute_feature_t pstf;
310*2d1272b8SAndroid Build Coastguard Worker hb_indic_would_substitute_feature_t vatu;
311*2d1272b8SAndroid Build Coastguard Worker
312*2d1272b8SAndroid Build Coastguard Worker hb_mask_t mask_array[INDIC_NUM_FEATURES];
313*2d1272b8SAndroid Build Coastguard Worker };
314*2d1272b8SAndroid Build Coastguard Worker
315*2d1272b8SAndroid Build Coastguard Worker static void *
data_create_indic(const hb_ot_shape_plan_t * plan)316*2d1272b8SAndroid Build Coastguard Worker data_create_indic (const hb_ot_shape_plan_t *plan)
317*2d1272b8SAndroid Build Coastguard Worker {
318*2d1272b8SAndroid Build Coastguard Worker indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) hb_calloc (1, sizeof (indic_shape_plan_t));
319*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!indic_plan))
320*2d1272b8SAndroid Build Coastguard Worker return nullptr;
321*2d1272b8SAndroid Build Coastguard Worker
322*2d1272b8SAndroid Build Coastguard Worker indic_plan->config = &indic_configs[0];
323*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++)
324*2d1272b8SAndroid Build Coastguard Worker if (plan->props.script == indic_configs[i].script) {
325*2d1272b8SAndroid Build Coastguard Worker indic_plan->config = &indic_configs[i];
326*2d1272b8SAndroid Build Coastguard Worker break;
327*2d1272b8SAndroid Build Coastguard Worker }
328*2d1272b8SAndroid Build Coastguard Worker
329*2d1272b8SAndroid Build Coastguard Worker indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2');
330*2d1272b8SAndroid Build Coastguard Worker #ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE
331*2d1272b8SAndroid Build Coastguard Worker indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible;
332*2d1272b8SAndroid Build Coastguard Worker #endif
333*2d1272b8SAndroid Build Coastguard Worker indic_plan->virama_glyph = -1;
334*2d1272b8SAndroid Build Coastguard Worker
335*2d1272b8SAndroid Build Coastguard Worker /* Use zero-context would_substitute() matching for new-spec of the main
336*2d1272b8SAndroid Build Coastguard Worker * Indic scripts, and scripts with one spec only, but not for old-specs.
337*2d1272b8SAndroid Build Coastguard Worker * The new-spec for all dual-spec scripts says zero-context matching happens.
338*2d1272b8SAndroid Build Coastguard Worker *
339*2d1272b8SAndroid Build Coastguard Worker * However, testing with Malayalam shows that old and new spec both allow
340*2d1272b8SAndroid Build Coastguard Worker * context. Testing with Bengali new-spec however shows that it doesn't.
341*2d1272b8SAndroid Build Coastguard Worker * So, the heuristic here is the way it is. It should *only* be changed,
342*2d1272b8SAndroid Build Coastguard Worker * as we discover more cases of what Windows does. DON'T TOUCH OTHERWISE.
343*2d1272b8SAndroid Build Coastguard Worker */
344*2d1272b8SAndroid Build Coastguard Worker bool zero_context = !indic_plan->is_old_spec && plan->props.script != HB_SCRIPT_MALAYALAM;
345*2d1272b8SAndroid Build Coastguard Worker indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'), zero_context);
346*2d1272b8SAndroid Build Coastguard Worker indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context);
347*2d1272b8SAndroid Build Coastguard Worker indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context);
348*2d1272b8SAndroid Build Coastguard Worker indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context);
349*2d1272b8SAndroid Build Coastguard Worker indic_plan->vatu.init (&plan->map, HB_TAG('v','a','t','u'), zero_context);
350*2d1272b8SAndroid Build Coastguard Worker
351*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++)
352*2d1272b8SAndroid Build Coastguard Worker indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ?
353*2d1272b8SAndroid Build Coastguard Worker 0 : plan->map.get_1_mask (indic_features[i].tag);
354*2d1272b8SAndroid Build Coastguard Worker
355*2d1272b8SAndroid Build Coastguard Worker return indic_plan;
356*2d1272b8SAndroid Build Coastguard Worker }
357*2d1272b8SAndroid Build Coastguard Worker
358*2d1272b8SAndroid Build Coastguard Worker static void
data_destroy_indic(void * data)359*2d1272b8SAndroid Build Coastguard Worker data_destroy_indic (void *data)
360*2d1272b8SAndroid Build Coastguard Worker {
361*2d1272b8SAndroid Build Coastguard Worker hb_free (data);
362*2d1272b8SAndroid Build Coastguard Worker }
363*2d1272b8SAndroid Build Coastguard Worker
364*2d1272b8SAndroid Build Coastguard Worker static indic_position_t
consonant_position_from_face(const indic_shape_plan_t * indic_plan,const hb_codepoint_t consonant,const hb_codepoint_t virama,hb_face_t * face)365*2d1272b8SAndroid Build Coastguard Worker consonant_position_from_face (const indic_shape_plan_t *indic_plan,
366*2d1272b8SAndroid Build Coastguard Worker const hb_codepoint_t consonant,
367*2d1272b8SAndroid Build Coastguard Worker const hb_codepoint_t virama,
368*2d1272b8SAndroid Build Coastguard Worker hb_face_t *face)
369*2d1272b8SAndroid Build Coastguard Worker {
370*2d1272b8SAndroid Build Coastguard Worker /* For old-spec, the order of glyphs is Consonant,Virama,
371*2d1272b8SAndroid Build Coastguard Worker * whereas for new-spec, it's Virama,Consonant. However,
372*2d1272b8SAndroid Build Coastguard Worker * some broken fonts (like Free Sans) simply copied lookups
373*2d1272b8SAndroid Build Coastguard Worker * from old-spec to new-spec without modification.
374*2d1272b8SAndroid Build Coastguard Worker * And oddly enough, Uniscribe seems to respect those lookups.
375*2d1272b8SAndroid Build Coastguard Worker * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds
376*2d1272b8SAndroid Build Coastguard Worker * base at 0. The font however, only has lookups matching
377*2d1272b8SAndroid Build Coastguard Worker * 930,94D in 'blwf', not the expected 94D,930 (with new-spec
378*2d1272b8SAndroid Build Coastguard Worker * table). As such, we simply match both sequences. Seems
379*2d1272b8SAndroid Build Coastguard Worker * to work.
380*2d1272b8SAndroid Build Coastguard Worker *
381*2d1272b8SAndroid Build Coastguard Worker * Vatu is done as well, for:
382*2d1272b8SAndroid Build Coastguard Worker * https://github.com/harfbuzz/harfbuzz/issues/1587
383*2d1272b8SAndroid Build Coastguard Worker */
384*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t glyphs[3] = {virama, consonant, virama};
385*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->blwf.would_substitute (glyphs , 2, face) ||
386*2d1272b8SAndroid Build Coastguard Worker indic_plan->blwf.would_substitute (glyphs+1, 2, face) ||
387*2d1272b8SAndroid Build Coastguard Worker indic_plan->vatu.would_substitute (glyphs , 2, face) ||
388*2d1272b8SAndroid Build Coastguard Worker indic_plan->vatu.would_substitute (glyphs+1, 2, face))
389*2d1272b8SAndroid Build Coastguard Worker return POS_BELOW_C;
390*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->pstf.would_substitute (glyphs , 2, face) ||
391*2d1272b8SAndroid Build Coastguard Worker indic_plan->pstf.would_substitute (glyphs+1, 2, face))
392*2d1272b8SAndroid Build Coastguard Worker return POS_POST_C;
393*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->pref.would_substitute (glyphs , 2, face) ||
394*2d1272b8SAndroid Build Coastguard Worker indic_plan->pref.would_substitute (glyphs+1, 2, face))
395*2d1272b8SAndroid Build Coastguard Worker return POS_POST_C;
396*2d1272b8SAndroid Build Coastguard Worker return POS_BASE_C;
397*2d1272b8SAndroid Build Coastguard Worker }
398*2d1272b8SAndroid Build Coastguard Worker
399*2d1272b8SAndroid Build Coastguard Worker static void
setup_masks_indic(const hb_ot_shape_plan_t * plan HB_UNUSED,hb_buffer_t * buffer,hb_font_t * font HB_UNUSED)400*2d1272b8SAndroid Build Coastguard Worker setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
401*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
402*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font HB_UNUSED)
403*2d1272b8SAndroid Build Coastguard Worker {
404*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
405*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
406*2d1272b8SAndroid Build Coastguard Worker
407*2d1272b8SAndroid Build Coastguard Worker /* We cannot setup masks here. We save information about characters
408*2d1272b8SAndroid Build Coastguard Worker * and setup masks later on in a pause-callback. */
409*2d1272b8SAndroid Build Coastguard Worker
410*2d1272b8SAndroid Build Coastguard Worker unsigned int count = buffer->len;
411*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->info;
412*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = 0; i < count; i++)
413*2d1272b8SAndroid Build Coastguard Worker set_indic_properties (info[i]);
414*2d1272b8SAndroid Build Coastguard Worker }
415*2d1272b8SAndroid Build Coastguard Worker
416*2d1272b8SAndroid Build Coastguard Worker static bool
setup_syllables_indic(const hb_ot_shape_plan_t * plan HB_UNUSED,hb_font_t * font HB_UNUSED,hb_buffer_t * buffer)417*2d1272b8SAndroid Build Coastguard Worker setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
418*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font HB_UNUSED,
419*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer)
420*2d1272b8SAndroid Build Coastguard Worker {
421*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
422*2d1272b8SAndroid Build Coastguard Worker find_syllables_indic (buffer);
423*2d1272b8SAndroid Build Coastguard Worker foreach_syllable (buffer, start, end)
424*2d1272b8SAndroid Build Coastguard Worker buffer->unsafe_to_break (start, end);
425*2d1272b8SAndroid Build Coastguard Worker return false;
426*2d1272b8SAndroid Build Coastguard Worker }
427*2d1272b8SAndroid Build Coastguard Worker
428*2d1272b8SAndroid Build Coastguard Worker static int
compare_indic_order(const hb_glyph_info_t * pa,const hb_glyph_info_t * pb)429*2d1272b8SAndroid Build Coastguard Worker compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
430*2d1272b8SAndroid Build Coastguard Worker {
431*2d1272b8SAndroid Build Coastguard Worker int a = pa->indic_position();
432*2d1272b8SAndroid Build Coastguard Worker int b = pb->indic_position();
433*2d1272b8SAndroid Build Coastguard Worker
434*2d1272b8SAndroid Build Coastguard Worker return (int) a - (int) b;
435*2d1272b8SAndroid Build Coastguard Worker }
436*2d1272b8SAndroid Build Coastguard Worker
437*2d1272b8SAndroid Build Coastguard Worker
438*2d1272b8SAndroid Build Coastguard Worker
439*2d1272b8SAndroid Build Coastguard Worker static void
update_consonant_positions_indic(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer)440*2d1272b8SAndroid Build Coastguard Worker update_consonant_positions_indic (const hb_ot_shape_plan_t *plan,
441*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font,
442*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer)
443*2d1272b8SAndroid Build Coastguard Worker {
444*2d1272b8SAndroid Build Coastguard Worker const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
445*2d1272b8SAndroid Build Coastguard Worker
446*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t virama;
447*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->load_virama_glyph (font, &virama))
448*2d1272b8SAndroid Build Coastguard Worker {
449*2d1272b8SAndroid Build Coastguard Worker hb_face_t *face = font->face;
450*2d1272b8SAndroid Build Coastguard Worker unsigned int count = buffer->len;
451*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->info;
452*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = 0; i < count; i++)
453*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_position() == POS_BASE_C)
454*2d1272b8SAndroid Build Coastguard Worker {
455*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t consonant = info[i].codepoint;
456*2d1272b8SAndroid Build Coastguard Worker info[i].indic_position() = consonant_position_from_face (indic_plan, consonant, virama, face);
457*2d1272b8SAndroid Build Coastguard Worker }
458*2d1272b8SAndroid Build Coastguard Worker }
459*2d1272b8SAndroid Build Coastguard Worker }
460*2d1272b8SAndroid Build Coastguard Worker
461*2d1272b8SAndroid Build Coastguard Worker
462*2d1272b8SAndroid Build Coastguard Worker /* Rules from:
463*2d1272b8SAndroid Build Coastguard Worker * https://docs.microsqoft.com/en-us/typography/script-development/devanagari */
464*2d1272b8SAndroid Build Coastguard Worker
465*2d1272b8SAndroid Build Coastguard Worker static void
initial_reordering_consonant_syllable(const hb_ot_shape_plan_t * plan,hb_face_t * face,hb_buffer_t * buffer,unsigned int start,unsigned int end)466*2d1272b8SAndroid Build Coastguard Worker initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
467*2d1272b8SAndroid Build Coastguard Worker hb_face_t *face,
468*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
469*2d1272b8SAndroid Build Coastguard Worker unsigned int start, unsigned int end)
470*2d1272b8SAndroid Build Coastguard Worker {
471*2d1272b8SAndroid Build Coastguard Worker const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
472*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->info;
473*2d1272b8SAndroid Build Coastguard Worker
474*2d1272b8SAndroid Build Coastguard Worker /* https://github.com/harfbuzz/harfbuzz/issues/435#issuecomment-335560167
475*2d1272b8SAndroid Build Coastguard Worker * // For compatibility with legacy usage in Kannada,
476*2d1272b8SAndroid Build Coastguard Worker * // Ra+h+ZWJ must behave like Ra+ZWJ+h...
477*2d1272b8SAndroid Build Coastguard Worker */
478*2d1272b8SAndroid Build Coastguard Worker if (buffer->props.script == HB_SCRIPT_KANNADA &&
479*2d1272b8SAndroid Build Coastguard Worker start + 3 <= end &&
480*2d1272b8SAndroid Build Coastguard Worker is_one_of (info[start ], FLAG (I_Cat(Ra))) &&
481*2d1272b8SAndroid Build Coastguard Worker is_one_of (info[start+1], FLAG (I_Cat(H))) &&
482*2d1272b8SAndroid Build Coastguard Worker is_one_of (info[start+2], FLAG (I_Cat(ZWJ))))
483*2d1272b8SAndroid Build Coastguard Worker {
484*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (start+1, start+3);
485*2d1272b8SAndroid Build Coastguard Worker hb_swap (info[start+1], info[start+2]);
486*2d1272b8SAndroid Build Coastguard Worker }
487*2d1272b8SAndroid Build Coastguard Worker
488*2d1272b8SAndroid Build Coastguard Worker /* 1. Find base consonant:
489*2d1272b8SAndroid Build Coastguard Worker *
490*2d1272b8SAndroid Build Coastguard Worker * The shaping engine finds the base consonant of the syllable, using the
491*2d1272b8SAndroid Build Coastguard Worker * following algorithm: starting from the end of the syllable, move backwards
492*2d1272b8SAndroid Build Coastguard Worker * until a consonant is found that does not have a below-base or post-base
493*2d1272b8SAndroid Build Coastguard Worker * form (post-base forms have to follow below-base forms), or that is not a
494*2d1272b8SAndroid Build Coastguard Worker * pre-base-reordering Ra, or arrive at the first consonant. The consonant
495*2d1272b8SAndroid Build Coastguard Worker * stopped at will be the base.
496*2d1272b8SAndroid Build Coastguard Worker *
497*2d1272b8SAndroid Build Coastguard Worker * o If the syllable starts with Ra + Halant (in a script that has Reph)
498*2d1272b8SAndroid Build Coastguard Worker * and has more than one consonant, Ra is excluded from candidates for
499*2d1272b8SAndroid Build Coastguard Worker * base consonants.
500*2d1272b8SAndroid Build Coastguard Worker */
501*2d1272b8SAndroid Build Coastguard Worker
502*2d1272b8SAndroid Build Coastguard Worker unsigned int base = end;
503*2d1272b8SAndroid Build Coastguard Worker bool has_reph = false;
504*2d1272b8SAndroid Build Coastguard Worker
505*2d1272b8SAndroid Build Coastguard Worker {
506*2d1272b8SAndroid Build Coastguard Worker /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
507*2d1272b8SAndroid Build Coastguard Worker * and has more than one consonant, Ra is excluded from candidates for
508*2d1272b8SAndroid Build Coastguard Worker * base consonants. */
509*2d1272b8SAndroid Build Coastguard Worker unsigned int limit = start;
510*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->mask_array[INDIC_RPHF] &&
511*2d1272b8SAndroid Build Coastguard Worker start + 3 <= end &&
512*2d1272b8SAndroid Build Coastguard Worker (
513*2d1272b8SAndroid Build Coastguard Worker (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
514*2d1272b8SAndroid Build Coastguard Worker (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == I_Cat(ZWJ))
515*2d1272b8SAndroid Build Coastguard Worker ))
516*2d1272b8SAndroid Build Coastguard Worker {
517*2d1272b8SAndroid Build Coastguard Worker /* See if it matches the 'rphf' feature. */
518*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t glyphs[3] = {info[start].codepoint,
519*2d1272b8SAndroid Build Coastguard Worker info[start + 1].codepoint,
520*2d1272b8SAndroid Build Coastguard Worker indic_plan->config->reph_mode == REPH_MODE_EXPLICIT ?
521*2d1272b8SAndroid Build Coastguard Worker info[start + 2].codepoint : 0};
522*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->rphf.would_substitute (glyphs, 2, face) ||
523*2d1272b8SAndroid Build Coastguard Worker (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT &&
524*2d1272b8SAndroid Build Coastguard Worker indic_plan->rphf.would_substitute (glyphs, 3, face)))
525*2d1272b8SAndroid Build Coastguard Worker {
526*2d1272b8SAndroid Build Coastguard Worker limit += 2;
527*2d1272b8SAndroid Build Coastguard Worker while (limit < end && is_joiner (info[limit]))
528*2d1272b8SAndroid Build Coastguard Worker limit++;
529*2d1272b8SAndroid Build Coastguard Worker base = start;
530*2d1272b8SAndroid Build Coastguard Worker has_reph = true;
531*2d1272b8SAndroid Build Coastguard Worker }
532*2d1272b8SAndroid Build Coastguard Worker } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == I_Cat(Repha))
533*2d1272b8SAndroid Build Coastguard Worker {
534*2d1272b8SAndroid Build Coastguard Worker limit += 1;
535*2d1272b8SAndroid Build Coastguard Worker while (limit < end && is_joiner (info[limit]))
536*2d1272b8SAndroid Build Coastguard Worker limit++;
537*2d1272b8SAndroid Build Coastguard Worker base = start;
538*2d1272b8SAndroid Build Coastguard Worker has_reph = true;
539*2d1272b8SAndroid Build Coastguard Worker }
540*2d1272b8SAndroid Build Coastguard Worker
541*2d1272b8SAndroid Build Coastguard Worker {
542*2d1272b8SAndroid Build Coastguard Worker /* -> starting from the end of the syllable, move backwards */
543*2d1272b8SAndroid Build Coastguard Worker unsigned int i = end;
544*2d1272b8SAndroid Build Coastguard Worker bool seen_below = false;
545*2d1272b8SAndroid Build Coastguard Worker do {
546*2d1272b8SAndroid Build Coastguard Worker i--;
547*2d1272b8SAndroid Build Coastguard Worker /* -> until a consonant is found */
548*2d1272b8SAndroid Build Coastguard Worker if (is_consonant (info[i]))
549*2d1272b8SAndroid Build Coastguard Worker {
550*2d1272b8SAndroid Build Coastguard Worker /* -> that does not have a below-base or post-base form
551*2d1272b8SAndroid Build Coastguard Worker * (post-base forms have to follow below-base forms), */
552*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_position() != POS_BELOW_C &&
553*2d1272b8SAndroid Build Coastguard Worker (info[i].indic_position() != POS_POST_C || seen_below))
554*2d1272b8SAndroid Build Coastguard Worker {
555*2d1272b8SAndroid Build Coastguard Worker base = i;
556*2d1272b8SAndroid Build Coastguard Worker break;
557*2d1272b8SAndroid Build Coastguard Worker }
558*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_position() == POS_BELOW_C)
559*2d1272b8SAndroid Build Coastguard Worker seen_below = true;
560*2d1272b8SAndroid Build Coastguard Worker
561*2d1272b8SAndroid Build Coastguard Worker /* -> or that is not a pre-base-reordering Ra,
562*2d1272b8SAndroid Build Coastguard Worker *
563*2d1272b8SAndroid Build Coastguard Worker * IMPLEMENTATION NOTES:
564*2d1272b8SAndroid Build Coastguard Worker *
565*2d1272b8SAndroid Build Coastguard Worker * Our pre-base-reordering Ra's are marked POS_POST_C, so will be skipped
566*2d1272b8SAndroid Build Coastguard Worker * by the logic above already.
567*2d1272b8SAndroid Build Coastguard Worker */
568*2d1272b8SAndroid Build Coastguard Worker
569*2d1272b8SAndroid Build Coastguard Worker /* -> or arrive at the first consonant. The consonant stopped at will
570*2d1272b8SAndroid Build Coastguard Worker * be the base. */
571*2d1272b8SAndroid Build Coastguard Worker base = i;
572*2d1272b8SAndroid Build Coastguard Worker }
573*2d1272b8SAndroid Build Coastguard Worker else
574*2d1272b8SAndroid Build Coastguard Worker {
575*2d1272b8SAndroid Build Coastguard Worker /* A ZWJ after a Halant stops the base search, and requests an explicit
576*2d1272b8SAndroid Build Coastguard Worker * half form.
577*2d1272b8SAndroid Build Coastguard Worker * A ZWJ before a Halant, requests a subjoined form instead, and hence
578*2d1272b8SAndroid Build Coastguard Worker * search continues. This is particularly important for Bengali
579*2d1272b8SAndroid Build Coastguard Worker * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */
580*2d1272b8SAndroid Build Coastguard Worker if (start < i &&
581*2d1272b8SAndroid Build Coastguard Worker info[i].indic_category() == I_Cat(ZWJ) &&
582*2d1272b8SAndroid Build Coastguard Worker info[i - 1].indic_category() == I_Cat(H))
583*2d1272b8SAndroid Build Coastguard Worker break;
584*2d1272b8SAndroid Build Coastguard Worker }
585*2d1272b8SAndroid Build Coastguard Worker } while (i > limit);
586*2d1272b8SAndroid Build Coastguard Worker }
587*2d1272b8SAndroid Build Coastguard Worker
588*2d1272b8SAndroid Build Coastguard Worker /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
589*2d1272b8SAndroid Build Coastguard Worker * and has more than one consonant, Ra is excluded from candidates for
590*2d1272b8SAndroid Build Coastguard Worker * base consonants.
591*2d1272b8SAndroid Build Coastguard Worker *
592*2d1272b8SAndroid Build Coastguard Worker * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
593*2d1272b8SAndroid Build Coastguard Worker if (has_reph && base == start && limit - base <= 2) {
594*2d1272b8SAndroid Build Coastguard Worker /* Have no other consonant, so Reph is not formed and Ra becomes base. */
595*2d1272b8SAndroid Build Coastguard Worker has_reph = false;
596*2d1272b8SAndroid Build Coastguard Worker }
597*2d1272b8SAndroid Build Coastguard Worker }
598*2d1272b8SAndroid Build Coastguard Worker
599*2d1272b8SAndroid Build Coastguard Worker
600*2d1272b8SAndroid Build Coastguard Worker /* 2. Decompose and reorder Matras:
601*2d1272b8SAndroid Build Coastguard Worker *
602*2d1272b8SAndroid Build Coastguard Worker * Each matra and any syllable modifier sign in the syllable are moved to the
603*2d1272b8SAndroid Build Coastguard Worker * appropriate position relative to the consonant(s) in the syllable. The
604*2d1272b8SAndroid Build Coastguard Worker * shaping engine decomposes two- or three-part matras into their constituent
605*2d1272b8SAndroid Build Coastguard Worker * parts before any repositioning. Matra characters are classified by which
606*2d1272b8SAndroid Build Coastguard Worker * consonant in a conjunct they have affinity for and are reordered to the
607*2d1272b8SAndroid Build Coastguard Worker * following positions:
608*2d1272b8SAndroid Build Coastguard Worker *
609*2d1272b8SAndroid Build Coastguard Worker * o Before first half form in the syllable
610*2d1272b8SAndroid Build Coastguard Worker * o After subjoined consonants
611*2d1272b8SAndroid Build Coastguard Worker * o After post-form consonant
612*2d1272b8SAndroid Build Coastguard Worker * o After main consonant (for above marks)
613*2d1272b8SAndroid Build Coastguard Worker *
614*2d1272b8SAndroid Build Coastguard Worker * IMPLEMENTATION NOTES:
615*2d1272b8SAndroid Build Coastguard Worker *
616*2d1272b8SAndroid Build Coastguard Worker * The normalize() routine has already decomposed matras for us, so we don't
617*2d1272b8SAndroid Build Coastguard Worker * need to worry about that.
618*2d1272b8SAndroid Build Coastguard Worker */
619*2d1272b8SAndroid Build Coastguard Worker
620*2d1272b8SAndroid Build Coastguard Worker
621*2d1272b8SAndroid Build Coastguard Worker /* 3. Reorder marks to canonical order:
622*2d1272b8SAndroid Build Coastguard Worker *
623*2d1272b8SAndroid Build Coastguard Worker * Adjacent nukta and halant or nukta and vedic sign are always repositioned
624*2d1272b8SAndroid Build Coastguard Worker * if necessary, so that the nukta is first.
625*2d1272b8SAndroid Build Coastguard Worker *
626*2d1272b8SAndroid Build Coastguard Worker * IMPLEMENTATION NOTES:
627*2d1272b8SAndroid Build Coastguard Worker *
628*2d1272b8SAndroid Build Coastguard Worker * We don't need to do this: the normalize() routine already did this for us.
629*2d1272b8SAndroid Build Coastguard Worker */
630*2d1272b8SAndroid Build Coastguard Worker
631*2d1272b8SAndroid Build Coastguard Worker
632*2d1272b8SAndroid Build Coastguard Worker /* Reorder characters */
633*2d1272b8SAndroid Build Coastguard Worker
634*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < base; i++)
635*2d1272b8SAndroid Build Coastguard Worker info[i].indic_position() = hb_min (POS_PRE_C, (indic_position_t) info[i].indic_position());
636*2d1272b8SAndroid Build Coastguard Worker
637*2d1272b8SAndroid Build Coastguard Worker if (base < end)
638*2d1272b8SAndroid Build Coastguard Worker info[base].indic_position() = POS_BASE_C;
639*2d1272b8SAndroid Build Coastguard Worker
640*2d1272b8SAndroid Build Coastguard Worker /* Handle beginning Ra */
641*2d1272b8SAndroid Build Coastguard Worker if (has_reph)
642*2d1272b8SAndroid Build Coastguard Worker info[start].indic_position() = POS_RA_TO_BECOME_REPH;
643*2d1272b8SAndroid Build Coastguard Worker
644*2d1272b8SAndroid Build Coastguard Worker /* For old-style Indic script tags, move the first post-base Halant after
645*2d1272b8SAndroid Build Coastguard Worker * last consonant.
646*2d1272b8SAndroid Build Coastguard Worker *
647*2d1272b8SAndroid Build Coastguard Worker * Reports suggest that in some scripts Uniscribe does this only if there
648*2d1272b8SAndroid Build Coastguard Worker * is *not* a Halant after last consonant already. We know that is the
649*2d1272b8SAndroid Build Coastguard Worker * case for Kannada, while it reorders unconditionally in other scripts,
650*2d1272b8SAndroid Build Coastguard Worker * eg. Malayalam, Bengali, and Devanagari. We don't currently know about
651*2d1272b8SAndroid Build Coastguard Worker * other scripts, so we block Kannada.
652*2d1272b8SAndroid Build Coastguard Worker *
653*2d1272b8SAndroid Build Coastguard Worker * Kannada test case:
654*2d1272b8SAndroid Build Coastguard Worker * U+0C9A,U+0CCD,U+0C9A,U+0CCD
655*2d1272b8SAndroid Build Coastguard Worker * With some versions of Lohit Kannada.
656*2d1272b8SAndroid Build Coastguard Worker * https://bugs.freedesktop.org/show_bug.cgi?id=59118
657*2d1272b8SAndroid Build Coastguard Worker *
658*2d1272b8SAndroid Build Coastguard Worker * Malayalam test case:
659*2d1272b8SAndroid Build Coastguard Worker * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D
660*2d1272b8SAndroid Build Coastguard Worker * With lohit-ttf-20121122/Lohit-Malayalam.ttf
661*2d1272b8SAndroid Build Coastguard Worker *
662*2d1272b8SAndroid Build Coastguard Worker * Bengali test case:
663*2d1272b8SAndroid Build Coastguard Worker * U+0998,U+09CD,U+09AF,U+09CD
664*2d1272b8SAndroid Build Coastguard Worker * With Windows XP vrinda.ttf
665*2d1272b8SAndroid Build Coastguard Worker * https://github.com/harfbuzz/harfbuzz/issues/1073
666*2d1272b8SAndroid Build Coastguard Worker *
667*2d1272b8SAndroid Build Coastguard Worker * Devanagari test case:
668*2d1272b8SAndroid Build Coastguard Worker * U+091F,U+094D,U+0930,U+094D
669*2d1272b8SAndroid Build Coastguard Worker * With chandas.ttf
670*2d1272b8SAndroid Build Coastguard Worker * https://github.com/harfbuzz/harfbuzz/issues/1071
671*2d1272b8SAndroid Build Coastguard Worker */
672*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->is_old_spec)
673*2d1272b8SAndroid Build Coastguard Worker {
674*2d1272b8SAndroid Build Coastguard Worker bool disallow_double_halants = buffer->props.script == HB_SCRIPT_KANNADA;
675*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < end; i++)
676*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_category() == I_Cat(H))
677*2d1272b8SAndroid Build Coastguard Worker {
678*2d1272b8SAndroid Build Coastguard Worker unsigned int j;
679*2d1272b8SAndroid Build Coastguard Worker for (j = end - 1; j > i; j--)
680*2d1272b8SAndroid Build Coastguard Worker if (is_consonant (info[j]) ||
681*2d1272b8SAndroid Build Coastguard Worker (disallow_double_halants && info[j].indic_category() == I_Cat(H)))
682*2d1272b8SAndroid Build Coastguard Worker break;
683*2d1272b8SAndroid Build Coastguard Worker if (info[j].indic_category() != I_Cat(H) && j > i) {
684*2d1272b8SAndroid Build Coastguard Worker /* Move Halant to after last consonant. */
685*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t t = info[i];
686*2d1272b8SAndroid Build Coastguard Worker memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
687*2d1272b8SAndroid Build Coastguard Worker info[j] = t;
688*2d1272b8SAndroid Build Coastguard Worker }
689*2d1272b8SAndroid Build Coastguard Worker break;
690*2d1272b8SAndroid Build Coastguard Worker }
691*2d1272b8SAndroid Build Coastguard Worker }
692*2d1272b8SAndroid Build Coastguard Worker
693*2d1272b8SAndroid Build Coastguard Worker /* Attach misc marks to previous char to move with them. */
694*2d1272b8SAndroid Build Coastguard Worker {
695*2d1272b8SAndroid Build Coastguard Worker indic_position_t last_pos = POS_START;
696*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < end; i++)
697*2d1272b8SAndroid Build Coastguard Worker {
698*2d1272b8SAndroid Build Coastguard Worker if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (I_Cat(N)) | FLAG (I_Cat(RS)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(H)))))
699*2d1272b8SAndroid Build Coastguard Worker {
700*2d1272b8SAndroid Build Coastguard Worker info[i].indic_position() = last_pos;
701*2d1272b8SAndroid Build Coastguard Worker if (unlikely (info[i].indic_category() == I_Cat(H) &&
702*2d1272b8SAndroid Build Coastguard Worker info[i].indic_position() == POS_PRE_M))
703*2d1272b8SAndroid Build Coastguard Worker {
704*2d1272b8SAndroid Build Coastguard Worker /*
705*2d1272b8SAndroid Build Coastguard Worker * Uniscribe doesn't move the Halant with Left Matra.
706*2d1272b8SAndroid Build Coastguard Worker * TEST: U+092B,U+093F,U+094D
707*2d1272b8SAndroid Build Coastguard Worker * We follow.
708*2d1272b8SAndroid Build Coastguard Worker */
709*2d1272b8SAndroid Build Coastguard Worker for (unsigned int j = i; j > start; j--)
710*2d1272b8SAndroid Build Coastguard Worker if (info[j - 1].indic_position() != POS_PRE_M) {
711*2d1272b8SAndroid Build Coastguard Worker info[i].indic_position() = info[j - 1].indic_position();
712*2d1272b8SAndroid Build Coastguard Worker break;
713*2d1272b8SAndroid Build Coastguard Worker }
714*2d1272b8SAndroid Build Coastguard Worker }
715*2d1272b8SAndroid Build Coastguard Worker } else if (info[i].indic_position() != POS_SMVD) {
716*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_category() == I_Cat(MPst) &&
717*2d1272b8SAndroid Build Coastguard Worker i > start && info[i - 1].indic_category() == I_Cat(SM))
718*2d1272b8SAndroid Build Coastguard Worker info[i - 1].indic_position() = info[i].indic_position();
719*2d1272b8SAndroid Build Coastguard Worker last_pos = (indic_position_t) info[i].indic_position();
720*2d1272b8SAndroid Build Coastguard Worker }
721*2d1272b8SAndroid Build Coastguard Worker }
722*2d1272b8SAndroid Build Coastguard Worker }
723*2d1272b8SAndroid Build Coastguard Worker /* For post-base consonants let them own anything before them
724*2d1272b8SAndroid Build Coastguard Worker * since the last consonant or matra. */
725*2d1272b8SAndroid Build Coastguard Worker {
726*2d1272b8SAndroid Build Coastguard Worker unsigned int last = base;
727*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < end; i++)
728*2d1272b8SAndroid Build Coastguard Worker if (is_consonant (info[i]))
729*2d1272b8SAndroid Build Coastguard Worker {
730*2d1272b8SAndroid Build Coastguard Worker for (unsigned int j = last + 1; j < i; j++)
731*2d1272b8SAndroid Build Coastguard Worker if (info[j].indic_position() < POS_SMVD)
732*2d1272b8SAndroid Build Coastguard Worker info[j].indic_position() = info[i].indic_position();
733*2d1272b8SAndroid Build Coastguard Worker last = i;
734*2d1272b8SAndroid Build Coastguard Worker } else if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst))))
735*2d1272b8SAndroid Build Coastguard Worker last = i;
736*2d1272b8SAndroid Build Coastguard Worker }
737*2d1272b8SAndroid Build Coastguard Worker
738*2d1272b8SAndroid Build Coastguard Worker
739*2d1272b8SAndroid Build Coastguard Worker {
740*2d1272b8SAndroid Build Coastguard Worker /* Use syllable() for sort accounting temporarily. */
741*2d1272b8SAndroid Build Coastguard Worker unsigned int syllable = info[start].syllable();
742*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < end; i++)
743*2d1272b8SAndroid Build Coastguard Worker info[i].syllable() = i - start;
744*2d1272b8SAndroid Build Coastguard Worker
745*2d1272b8SAndroid Build Coastguard Worker /* Sit tight, rock 'n roll! */
746*2d1272b8SAndroid Build Coastguard Worker hb_stable_sort (info + start, end - start, compare_indic_order);
747*2d1272b8SAndroid Build Coastguard Worker
748*2d1272b8SAndroid Build Coastguard Worker /* Find base again; also flip left-matra sequence. */
749*2d1272b8SAndroid Build Coastguard Worker unsigned first_left_matra = end;
750*2d1272b8SAndroid Build Coastguard Worker unsigned last_left_matra = end;
751*2d1272b8SAndroid Build Coastguard Worker base = end;
752*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < end; i++)
753*2d1272b8SAndroid Build Coastguard Worker {
754*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_position() == POS_BASE_C)
755*2d1272b8SAndroid Build Coastguard Worker {
756*2d1272b8SAndroid Build Coastguard Worker base = i;
757*2d1272b8SAndroid Build Coastguard Worker break;
758*2d1272b8SAndroid Build Coastguard Worker }
759*2d1272b8SAndroid Build Coastguard Worker else if (info[i].indic_position() == POS_PRE_M)
760*2d1272b8SAndroid Build Coastguard Worker {
761*2d1272b8SAndroid Build Coastguard Worker if (first_left_matra == end)
762*2d1272b8SAndroid Build Coastguard Worker first_left_matra = i;
763*2d1272b8SAndroid Build Coastguard Worker last_left_matra = i;
764*2d1272b8SAndroid Build Coastguard Worker }
765*2d1272b8SAndroid Build Coastguard Worker }
766*2d1272b8SAndroid Build Coastguard Worker /* https://github.com/harfbuzz/harfbuzz/issues/3863 */
767*2d1272b8SAndroid Build Coastguard Worker if (first_left_matra < last_left_matra)
768*2d1272b8SAndroid Build Coastguard Worker {
769*2d1272b8SAndroid Build Coastguard Worker /* No need to merge clusters, handled later. */
770*2d1272b8SAndroid Build Coastguard Worker buffer->reverse_range (first_left_matra, last_left_matra + 1);
771*2d1272b8SAndroid Build Coastguard Worker /* Reverse back nuktas, etc. */
772*2d1272b8SAndroid Build Coastguard Worker unsigned i = first_left_matra;
773*2d1272b8SAndroid Build Coastguard Worker for (unsigned j = i; j <= last_left_matra; j++)
774*2d1272b8SAndroid Build Coastguard Worker if (FLAG_UNSAFE (info[j].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst))))
775*2d1272b8SAndroid Build Coastguard Worker {
776*2d1272b8SAndroid Build Coastguard Worker buffer->reverse_range (i, j + 1);
777*2d1272b8SAndroid Build Coastguard Worker i = j + 1;
778*2d1272b8SAndroid Build Coastguard Worker }
779*2d1272b8SAndroid Build Coastguard Worker }
780*2d1272b8SAndroid Build Coastguard Worker
781*2d1272b8SAndroid Build Coastguard Worker /* Things are out-of-control for post base positions, they may shuffle
782*2d1272b8SAndroid Build Coastguard Worker * around like crazy. In old-spec mode, we move halants around, so in
783*2d1272b8SAndroid Build Coastguard Worker * that case merge all clusters after base. Otherwise, check the sort
784*2d1272b8SAndroid Build Coastguard Worker * order and merge as needed.
785*2d1272b8SAndroid Build Coastguard Worker * For pre-base stuff, we handle cluster issues in final reordering.
786*2d1272b8SAndroid Build Coastguard Worker *
787*2d1272b8SAndroid Build Coastguard Worker * We could use buffer->sort() for this, if there was no special
788*2d1272b8SAndroid Build Coastguard Worker * reordering of pre-base stuff happening later...
789*2d1272b8SAndroid Build Coastguard Worker * We don't want to merge_clusters all of that, which buffer->sort()
790*2d1272b8SAndroid Build Coastguard Worker * would. Here's a concrete example:
791*2d1272b8SAndroid Build Coastguard Worker *
792*2d1272b8SAndroid Build Coastguard Worker * Assume there's a pre-base consonant and explicit Halant before base,
793*2d1272b8SAndroid Build Coastguard Worker * followed by a prebase-reordering (left) Matra:
794*2d1272b8SAndroid Build Coastguard Worker *
795*2d1272b8SAndroid Build Coastguard Worker * C,H,ZWNJ,B,M
796*2d1272b8SAndroid Build Coastguard Worker *
797*2d1272b8SAndroid Build Coastguard Worker * At this point in reordering we would have:
798*2d1272b8SAndroid Build Coastguard Worker *
799*2d1272b8SAndroid Build Coastguard Worker * M,C,H,ZWNJ,B
800*2d1272b8SAndroid Build Coastguard Worker *
801*2d1272b8SAndroid Build Coastguard Worker * whereas in final reordering we will bring the Matra closer to Base:
802*2d1272b8SAndroid Build Coastguard Worker *
803*2d1272b8SAndroid Build Coastguard Worker * C,H,ZWNJ,M,B
804*2d1272b8SAndroid Build Coastguard Worker *
805*2d1272b8SAndroid Build Coastguard Worker * That's why we don't want to merge-clusters anything before the Base
806*2d1272b8SAndroid Build Coastguard Worker * at this point. But if something moved from after Base to before it,
807*2d1272b8SAndroid Build Coastguard Worker * we should merge clusters from base to them. In final-reordering, we
808*2d1272b8SAndroid Build Coastguard Worker * only move things around before base, and merge-clusters up to base.
809*2d1272b8SAndroid Build Coastguard Worker * These two merge-clusters from the two sides of base will interlock
810*2d1272b8SAndroid Build Coastguard Worker * to merge things correctly. See:
811*2d1272b8SAndroid Build Coastguard Worker * https://github.com/harfbuzz/harfbuzz/issues/2272
812*2d1272b8SAndroid Build Coastguard Worker */
813*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->is_old_spec || end - start > 127)
814*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (base, end);
815*2d1272b8SAndroid Build Coastguard Worker else
816*2d1272b8SAndroid Build Coastguard Worker {
817*2d1272b8SAndroid Build Coastguard Worker /* Note! syllable() is a one-byte field. */
818*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base; i < end; i++)
819*2d1272b8SAndroid Build Coastguard Worker if (info[i].syllable() != 255)
820*2d1272b8SAndroid Build Coastguard Worker {
821*2d1272b8SAndroid Build Coastguard Worker unsigned int min = i;
822*2d1272b8SAndroid Build Coastguard Worker unsigned int max = i;
823*2d1272b8SAndroid Build Coastguard Worker unsigned int j = start + info[i].syllable();
824*2d1272b8SAndroid Build Coastguard Worker while (j != i)
825*2d1272b8SAndroid Build Coastguard Worker {
826*2d1272b8SAndroid Build Coastguard Worker min = hb_min (min, j);
827*2d1272b8SAndroid Build Coastguard Worker max = hb_max (max, j);
828*2d1272b8SAndroid Build Coastguard Worker unsigned int next = start + info[j].syllable();
829*2d1272b8SAndroid Build Coastguard Worker info[j].syllable() = 255; /* So we don't process j later again. */
830*2d1272b8SAndroid Build Coastguard Worker j = next;
831*2d1272b8SAndroid Build Coastguard Worker }
832*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (hb_max (base, min), max + 1);
833*2d1272b8SAndroid Build Coastguard Worker }
834*2d1272b8SAndroid Build Coastguard Worker }
835*2d1272b8SAndroid Build Coastguard Worker
836*2d1272b8SAndroid Build Coastguard Worker /* Put syllable back in. */
837*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < end; i++)
838*2d1272b8SAndroid Build Coastguard Worker info[i].syllable() = syllable;
839*2d1272b8SAndroid Build Coastguard Worker }
840*2d1272b8SAndroid Build Coastguard Worker
841*2d1272b8SAndroid Build Coastguard Worker /* Setup masks now */
842*2d1272b8SAndroid Build Coastguard Worker
843*2d1272b8SAndroid Build Coastguard Worker {
844*2d1272b8SAndroid Build Coastguard Worker hb_mask_t mask;
845*2d1272b8SAndroid Build Coastguard Worker
846*2d1272b8SAndroid Build Coastguard Worker /* Reph */
847*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
848*2d1272b8SAndroid Build Coastguard Worker info[i].mask |= indic_plan->mask_array[INDIC_RPHF];
849*2d1272b8SAndroid Build Coastguard Worker
850*2d1272b8SAndroid Build Coastguard Worker /* Pre-base */
851*2d1272b8SAndroid Build Coastguard Worker mask = indic_plan->mask_array[INDIC_HALF];
852*2d1272b8SAndroid Build Coastguard Worker if (!indic_plan->is_old_spec &&
853*2d1272b8SAndroid Build Coastguard Worker indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST)
854*2d1272b8SAndroid Build Coastguard Worker mask |= indic_plan->mask_array[INDIC_BLWF];
855*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < base; i++)
856*2d1272b8SAndroid Build Coastguard Worker info[i].mask |= mask;
857*2d1272b8SAndroid Build Coastguard Worker /* Base */
858*2d1272b8SAndroid Build Coastguard Worker mask = 0;
859*2d1272b8SAndroid Build Coastguard Worker if (base < end)
860*2d1272b8SAndroid Build Coastguard Worker info[base].mask |= mask;
861*2d1272b8SAndroid Build Coastguard Worker /* Post-base */
862*2d1272b8SAndroid Build Coastguard Worker mask = indic_plan->mask_array[INDIC_BLWF] |
863*2d1272b8SAndroid Build Coastguard Worker indic_plan->mask_array[INDIC_ABVF] |
864*2d1272b8SAndroid Build Coastguard Worker indic_plan->mask_array[INDIC_PSTF];
865*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < end; i++)
866*2d1272b8SAndroid Build Coastguard Worker info[i].mask |= mask;
867*2d1272b8SAndroid Build Coastguard Worker }
868*2d1272b8SAndroid Build Coastguard Worker
869*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->is_old_spec &&
870*2d1272b8SAndroid Build Coastguard Worker buffer->props.script == HB_SCRIPT_DEVANAGARI)
871*2d1272b8SAndroid Build Coastguard Worker {
872*2d1272b8SAndroid Build Coastguard Worker /* Old-spec eye-lash Ra needs special handling. From the
873*2d1272b8SAndroid Build Coastguard Worker * spec:
874*2d1272b8SAndroid Build Coastguard Worker *
875*2d1272b8SAndroid Build Coastguard Worker * "The feature 'below-base form' is applied to consonants
876*2d1272b8SAndroid Build Coastguard Worker * having below-base forms and following the base consonant.
877*2d1272b8SAndroid Build Coastguard Worker * The exception is vattu, which may appear below half forms
878*2d1272b8SAndroid Build Coastguard Worker * as well as below the base glyph. The feature 'below-base
879*2d1272b8SAndroid Build Coastguard Worker * form' will be applied to all such occurrences of Ra as well."
880*2d1272b8SAndroid Build Coastguard Worker *
881*2d1272b8SAndroid Build Coastguard Worker * Test case: U+0924,U+094D,U+0930,U+094d,U+0915
882*2d1272b8SAndroid Build Coastguard Worker * with Sanskrit 2003 font.
883*2d1272b8SAndroid Build Coastguard Worker *
884*2d1272b8SAndroid Build Coastguard Worker * However, note that Ra,Halant,ZWJ is the correct way to
885*2d1272b8SAndroid Build Coastguard Worker * request eyelash form of Ra, so we wouldbn't inhibit it
886*2d1272b8SAndroid Build Coastguard Worker * in that sequence.
887*2d1272b8SAndroid Build Coastguard Worker *
888*2d1272b8SAndroid Build Coastguard Worker * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
889*2d1272b8SAndroid Build Coastguard Worker */
890*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i + 1 < base; i++)
891*2d1272b8SAndroid Build Coastguard Worker if (info[i ].indic_category() == I_Cat(Ra) &&
892*2d1272b8SAndroid Build Coastguard Worker info[i+1].indic_category() == I_Cat(H) &&
893*2d1272b8SAndroid Build Coastguard Worker (i + 2 == base ||
894*2d1272b8SAndroid Build Coastguard Worker info[i+2].indic_category() != I_Cat(ZWJ)))
895*2d1272b8SAndroid Build Coastguard Worker {
896*2d1272b8SAndroid Build Coastguard Worker info[i ].mask |= indic_plan->mask_array[INDIC_BLWF];
897*2d1272b8SAndroid Build Coastguard Worker info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF];
898*2d1272b8SAndroid Build Coastguard Worker }
899*2d1272b8SAndroid Build Coastguard Worker }
900*2d1272b8SAndroid Build Coastguard Worker
901*2d1272b8SAndroid Build Coastguard Worker unsigned int pref_len = 2;
902*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->mask_array[INDIC_PREF] && base + pref_len < end)
903*2d1272b8SAndroid Build Coastguard Worker {
904*2d1272b8SAndroid Build Coastguard Worker /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
905*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
906*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t glyphs[2];
907*2d1272b8SAndroid Build Coastguard Worker for (unsigned int j = 0; j < pref_len; j++)
908*2d1272b8SAndroid Build Coastguard Worker glyphs[j] = info[i + j].codepoint;
909*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->pref.would_substitute (glyphs, pref_len, face))
910*2d1272b8SAndroid Build Coastguard Worker {
911*2d1272b8SAndroid Build Coastguard Worker for (unsigned int j = 0; j < pref_len; j++)
912*2d1272b8SAndroid Build Coastguard Worker info[i++].mask |= indic_plan->mask_array[INDIC_PREF];
913*2d1272b8SAndroid Build Coastguard Worker break;
914*2d1272b8SAndroid Build Coastguard Worker }
915*2d1272b8SAndroid Build Coastguard Worker }
916*2d1272b8SAndroid Build Coastguard Worker }
917*2d1272b8SAndroid Build Coastguard Worker
918*2d1272b8SAndroid Build Coastguard Worker /* Apply ZWJ/ZWNJ effects */
919*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start + 1; i < end; i++)
920*2d1272b8SAndroid Build Coastguard Worker if (is_joiner (info[i])) {
921*2d1272b8SAndroid Build Coastguard Worker bool non_joiner = info[i].indic_category() == I_Cat(ZWNJ);
922*2d1272b8SAndroid Build Coastguard Worker unsigned int j = i;
923*2d1272b8SAndroid Build Coastguard Worker
924*2d1272b8SAndroid Build Coastguard Worker do {
925*2d1272b8SAndroid Build Coastguard Worker j--;
926*2d1272b8SAndroid Build Coastguard Worker
927*2d1272b8SAndroid Build Coastguard Worker /* ZWJ/ZWNJ should disable CJCT. They do that by simply
928*2d1272b8SAndroid Build Coastguard Worker * being there, since we don't skip them for the CJCT
929*2d1272b8SAndroid Build Coastguard Worker * feature (ie. F_MANUAL_ZWJ) */
930*2d1272b8SAndroid Build Coastguard Worker
931*2d1272b8SAndroid Build Coastguard Worker /* A ZWNJ disables HALF. */
932*2d1272b8SAndroid Build Coastguard Worker if (non_joiner)
933*2d1272b8SAndroid Build Coastguard Worker info[j].mask &= ~indic_plan->mask_array[INDIC_HALF];
934*2d1272b8SAndroid Build Coastguard Worker
935*2d1272b8SAndroid Build Coastguard Worker } while (j > start && !is_consonant (info[j]));
936*2d1272b8SAndroid Build Coastguard Worker }
937*2d1272b8SAndroid Build Coastguard Worker }
938*2d1272b8SAndroid Build Coastguard Worker
939*2d1272b8SAndroid Build Coastguard Worker static void
initial_reordering_standalone_cluster(const hb_ot_shape_plan_t * plan,hb_face_t * face,hb_buffer_t * buffer,unsigned int start,unsigned int end)940*2d1272b8SAndroid Build Coastguard Worker initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
941*2d1272b8SAndroid Build Coastguard Worker hb_face_t *face,
942*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
943*2d1272b8SAndroid Build Coastguard Worker unsigned int start, unsigned int end)
944*2d1272b8SAndroid Build Coastguard Worker {
945*2d1272b8SAndroid Build Coastguard Worker /* We treat placeholder/dotted-circle as if they are consonants, so we
946*2d1272b8SAndroid Build Coastguard Worker * should just chain. Only if not in compatibility mode that is... */
947*2d1272b8SAndroid Build Coastguard Worker
948*2d1272b8SAndroid Build Coastguard Worker const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
949*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->uniscribe_bug_compatible)
950*2d1272b8SAndroid Build Coastguard Worker {
951*2d1272b8SAndroid Build Coastguard Worker /* For dotted-circle, this is what Uniscribe does:
952*2d1272b8SAndroid Build Coastguard Worker * If dotted-circle is the last glyph, it just does nothing.
953*2d1272b8SAndroid Build Coastguard Worker * Ie. It doesn't form Reph. */
954*2d1272b8SAndroid Build Coastguard Worker if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE))
955*2d1272b8SAndroid Build Coastguard Worker return;
956*2d1272b8SAndroid Build Coastguard Worker }
957*2d1272b8SAndroid Build Coastguard Worker
958*2d1272b8SAndroid Build Coastguard Worker initial_reordering_consonant_syllable (plan, face, buffer, start, end);
959*2d1272b8SAndroid Build Coastguard Worker }
960*2d1272b8SAndroid Build Coastguard Worker
961*2d1272b8SAndroid Build Coastguard Worker static void
initial_reordering_syllable_indic(const hb_ot_shape_plan_t * plan,hb_face_t * face,hb_buffer_t * buffer,unsigned int start,unsigned int end)962*2d1272b8SAndroid Build Coastguard Worker initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
963*2d1272b8SAndroid Build Coastguard Worker hb_face_t *face,
964*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
965*2d1272b8SAndroid Build Coastguard Worker unsigned int start, unsigned int end)
966*2d1272b8SAndroid Build Coastguard Worker {
967*2d1272b8SAndroid Build Coastguard Worker indic_syllable_type_t syllable_type = (indic_syllable_type_t) (buffer->info[start].syllable() & 0x0F);
968*2d1272b8SAndroid Build Coastguard Worker switch (syllable_type)
969*2d1272b8SAndroid Build Coastguard Worker {
970*2d1272b8SAndroid Build Coastguard Worker case indic_vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */
971*2d1272b8SAndroid Build Coastguard Worker case indic_consonant_syllable:
972*2d1272b8SAndroid Build Coastguard Worker initial_reordering_consonant_syllable (plan, face, buffer, start, end);
973*2d1272b8SAndroid Build Coastguard Worker break;
974*2d1272b8SAndroid Build Coastguard Worker
975*2d1272b8SAndroid Build Coastguard Worker case indic_broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */
976*2d1272b8SAndroid Build Coastguard Worker case indic_standalone_cluster:
977*2d1272b8SAndroid Build Coastguard Worker initial_reordering_standalone_cluster (plan, face, buffer, start, end);
978*2d1272b8SAndroid Build Coastguard Worker break;
979*2d1272b8SAndroid Build Coastguard Worker
980*2d1272b8SAndroid Build Coastguard Worker case indic_symbol_cluster:
981*2d1272b8SAndroid Build Coastguard Worker case indic_non_indic_cluster:
982*2d1272b8SAndroid Build Coastguard Worker break;
983*2d1272b8SAndroid Build Coastguard Worker }
984*2d1272b8SAndroid Build Coastguard Worker }
985*2d1272b8SAndroid Build Coastguard Worker
986*2d1272b8SAndroid Build Coastguard Worker static bool
initial_reordering_indic(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer)987*2d1272b8SAndroid Build Coastguard Worker initial_reordering_indic (const hb_ot_shape_plan_t *plan,
988*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font,
989*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer)
990*2d1272b8SAndroid Build Coastguard Worker {
991*2d1272b8SAndroid Build Coastguard Worker bool ret = false;
992*2d1272b8SAndroid Build Coastguard Worker if (!buffer->message (font, "start reordering indic initial"))
993*2d1272b8SAndroid Build Coastguard Worker return ret;
994*2d1272b8SAndroid Build Coastguard Worker
995*2d1272b8SAndroid Build Coastguard Worker update_consonant_positions_indic (plan, font, buffer);
996*2d1272b8SAndroid Build Coastguard Worker if (hb_syllabic_insert_dotted_circles (font, buffer,
997*2d1272b8SAndroid Build Coastguard Worker indic_broken_cluster,
998*2d1272b8SAndroid Build Coastguard Worker I_Cat(DOTTEDCIRCLE),
999*2d1272b8SAndroid Build Coastguard Worker I_Cat(Repha),
1000*2d1272b8SAndroid Build Coastguard Worker POS_END))
1001*2d1272b8SAndroid Build Coastguard Worker ret = true;
1002*2d1272b8SAndroid Build Coastguard Worker
1003*2d1272b8SAndroid Build Coastguard Worker foreach_syllable (buffer, start, end)
1004*2d1272b8SAndroid Build Coastguard Worker initial_reordering_syllable_indic (plan, font->face, buffer, start, end);
1005*2d1272b8SAndroid Build Coastguard Worker
1006*2d1272b8SAndroid Build Coastguard Worker (void) buffer->message (font, "end reordering indic initial");
1007*2d1272b8SAndroid Build Coastguard Worker
1008*2d1272b8SAndroid Build Coastguard Worker return ret;
1009*2d1272b8SAndroid Build Coastguard Worker }
1010*2d1272b8SAndroid Build Coastguard Worker
1011*2d1272b8SAndroid Build Coastguard Worker static void
final_reordering_syllable_indic(const hb_ot_shape_plan_t * plan,hb_buffer_t * buffer,unsigned int start,unsigned int end)1012*2d1272b8SAndroid Build Coastguard Worker final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
1013*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
1014*2d1272b8SAndroid Build Coastguard Worker unsigned int start, unsigned int end)
1015*2d1272b8SAndroid Build Coastguard Worker {
1016*2d1272b8SAndroid Build Coastguard Worker const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
1017*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->info;
1018*2d1272b8SAndroid Build Coastguard Worker
1019*2d1272b8SAndroid Build Coastguard Worker
1020*2d1272b8SAndroid Build Coastguard Worker /* This function relies heavily on halant glyphs. Lots of ligation
1021*2d1272b8SAndroid Build Coastguard Worker * and possibly multiple substitutions happened prior to this
1022*2d1272b8SAndroid Build Coastguard Worker * phase, and that might have messed up our properties. Recover
1023*2d1272b8SAndroid Build Coastguard Worker * from a particular case of that where we're fairly sure that a
1024*2d1272b8SAndroid Build Coastguard Worker * class of I_Cat(H) is desired but has been lost. */
1025*2d1272b8SAndroid Build Coastguard Worker /* We don't call load_virama_glyph(), since we know it's already
1026*2d1272b8SAndroid Build Coastguard Worker * loaded. */
1027*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t virama_glyph = indic_plan->virama_glyph;
1028*2d1272b8SAndroid Build Coastguard Worker if (virama_glyph)
1029*2d1272b8SAndroid Build Coastguard Worker {
1030*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < end; i++)
1031*2d1272b8SAndroid Build Coastguard Worker if (info[i].codepoint == virama_glyph &&
1032*2d1272b8SAndroid Build Coastguard Worker _hb_glyph_info_ligated (&info[i]) &&
1033*2d1272b8SAndroid Build Coastguard Worker _hb_glyph_info_multiplied (&info[i]))
1034*2d1272b8SAndroid Build Coastguard Worker {
1035*2d1272b8SAndroid Build Coastguard Worker /* This will make sure that this glyph passes is_halant() test. */
1036*2d1272b8SAndroid Build Coastguard Worker info[i].indic_category() = I_Cat(H);
1037*2d1272b8SAndroid Build Coastguard Worker _hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
1038*2d1272b8SAndroid Build Coastguard Worker }
1039*2d1272b8SAndroid Build Coastguard Worker }
1040*2d1272b8SAndroid Build Coastguard Worker
1041*2d1272b8SAndroid Build Coastguard Worker
1042*2d1272b8SAndroid Build Coastguard Worker /* 4. Final reordering:
1043*2d1272b8SAndroid Build Coastguard Worker *
1044*2d1272b8SAndroid Build Coastguard Worker * After the localized forms and basic shaping forms GSUB features have been
1045*2d1272b8SAndroid Build Coastguard Worker * applied (see below), the shaping engine performs some final glyph
1046*2d1272b8SAndroid Build Coastguard Worker * reordering before applying all the remaining font features to the entire
1047*2d1272b8SAndroid Build Coastguard Worker * syllable.
1048*2d1272b8SAndroid Build Coastguard Worker */
1049*2d1272b8SAndroid Build Coastguard Worker
1050*2d1272b8SAndroid Build Coastguard Worker bool try_pref = !!indic_plan->mask_array[INDIC_PREF];
1051*2d1272b8SAndroid Build Coastguard Worker
1052*2d1272b8SAndroid Build Coastguard Worker /* Find base again */
1053*2d1272b8SAndroid Build Coastguard Worker unsigned int base;
1054*2d1272b8SAndroid Build Coastguard Worker for (base = start; base < end; base++)
1055*2d1272b8SAndroid Build Coastguard Worker if (info[base].indic_position() >= POS_BASE_C)
1056*2d1272b8SAndroid Build Coastguard Worker {
1057*2d1272b8SAndroid Build Coastguard Worker if (try_pref && base + 1 < end)
1058*2d1272b8SAndroid Build Coastguard Worker {
1059*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < end; i++)
1060*2d1272b8SAndroid Build Coastguard Worker if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0)
1061*2d1272b8SAndroid Build Coastguard Worker {
1062*2d1272b8SAndroid Build Coastguard Worker if (!(_hb_glyph_info_substituted (&info[i]) &&
1063*2d1272b8SAndroid Build Coastguard Worker _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
1064*2d1272b8SAndroid Build Coastguard Worker {
1065*2d1272b8SAndroid Build Coastguard Worker /* Ok, this was a 'pref' candidate but didn't form any.
1066*2d1272b8SAndroid Build Coastguard Worker * Base is around here... */
1067*2d1272b8SAndroid Build Coastguard Worker base = i;
1068*2d1272b8SAndroid Build Coastguard Worker while (base < end && is_halant (info[base]))
1069*2d1272b8SAndroid Build Coastguard Worker base++;
1070*2d1272b8SAndroid Build Coastguard Worker if (base < end)
1071*2d1272b8SAndroid Build Coastguard Worker info[base].indic_position() = POS_BASE_C;
1072*2d1272b8SAndroid Build Coastguard Worker
1073*2d1272b8SAndroid Build Coastguard Worker try_pref = false;
1074*2d1272b8SAndroid Build Coastguard Worker }
1075*2d1272b8SAndroid Build Coastguard Worker break;
1076*2d1272b8SAndroid Build Coastguard Worker }
1077*2d1272b8SAndroid Build Coastguard Worker if (base == end)
1078*2d1272b8SAndroid Build Coastguard Worker break;
1079*2d1272b8SAndroid Build Coastguard Worker }
1080*2d1272b8SAndroid Build Coastguard Worker /* For Malayalam, skip over unformed below- (but NOT post-) forms. */
1081*2d1272b8SAndroid Build Coastguard Worker if (buffer->props.script == HB_SCRIPT_MALAYALAM)
1082*2d1272b8SAndroid Build Coastguard Worker {
1083*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < end; i++)
1084*2d1272b8SAndroid Build Coastguard Worker {
1085*2d1272b8SAndroid Build Coastguard Worker while (i < end && is_joiner (info[i]))
1086*2d1272b8SAndroid Build Coastguard Worker i++;
1087*2d1272b8SAndroid Build Coastguard Worker if (i == end || !is_halant (info[i]))
1088*2d1272b8SAndroid Build Coastguard Worker break;
1089*2d1272b8SAndroid Build Coastguard Worker i++; /* Skip halant. */
1090*2d1272b8SAndroid Build Coastguard Worker while (i < end && is_joiner (info[i]))
1091*2d1272b8SAndroid Build Coastguard Worker i++;
1092*2d1272b8SAndroid Build Coastguard Worker if (i < end && is_consonant (info[i]) && info[i].indic_position() == POS_BELOW_C)
1093*2d1272b8SAndroid Build Coastguard Worker {
1094*2d1272b8SAndroid Build Coastguard Worker base = i;
1095*2d1272b8SAndroid Build Coastguard Worker info[base].indic_position() = POS_BASE_C;
1096*2d1272b8SAndroid Build Coastguard Worker }
1097*2d1272b8SAndroid Build Coastguard Worker }
1098*2d1272b8SAndroid Build Coastguard Worker }
1099*2d1272b8SAndroid Build Coastguard Worker
1100*2d1272b8SAndroid Build Coastguard Worker if (start < base && info[base].indic_position() > POS_BASE_C)
1101*2d1272b8SAndroid Build Coastguard Worker base--;
1102*2d1272b8SAndroid Build Coastguard Worker break;
1103*2d1272b8SAndroid Build Coastguard Worker }
1104*2d1272b8SAndroid Build Coastguard Worker if (base == end && start < base &&
1105*2d1272b8SAndroid Build Coastguard Worker is_one_of (info[base - 1], FLAG (I_Cat(ZWJ))))
1106*2d1272b8SAndroid Build Coastguard Worker base--;
1107*2d1272b8SAndroid Build Coastguard Worker if (base < end)
1108*2d1272b8SAndroid Build Coastguard Worker while (start < base &&
1109*2d1272b8SAndroid Build Coastguard Worker is_one_of (info[base], (FLAG (I_Cat(N)) | FLAG (I_Cat(H)))))
1110*2d1272b8SAndroid Build Coastguard Worker base--;
1111*2d1272b8SAndroid Build Coastguard Worker
1112*2d1272b8SAndroid Build Coastguard Worker
1113*2d1272b8SAndroid Build Coastguard Worker /* o Reorder matras:
1114*2d1272b8SAndroid Build Coastguard Worker *
1115*2d1272b8SAndroid Build Coastguard Worker * If a pre-base matra character had been reordered before applying basic
1116*2d1272b8SAndroid Build Coastguard Worker * features, the glyph can be moved closer to the main consonant based on
1117*2d1272b8SAndroid Build Coastguard Worker * whether half-forms had been formed. Actual position for the matra is
1118*2d1272b8SAndroid Build Coastguard Worker * defined as “after last standalone halant glyph, after initial matra
1119*2d1272b8SAndroid Build Coastguard Worker * position and before the main consonant”. If ZWJ or ZWNJ follow this
1120*2d1272b8SAndroid Build Coastguard Worker * halant, position is moved after it.
1121*2d1272b8SAndroid Build Coastguard Worker *
1122*2d1272b8SAndroid Build Coastguard Worker * IMPLEMENTATION NOTES:
1123*2d1272b8SAndroid Build Coastguard Worker *
1124*2d1272b8SAndroid Build Coastguard Worker * It looks like the last sentence is wrong. Testing, with Windows 7 Uniscribe
1125*2d1272b8SAndroid Build Coastguard Worker * and Devanagari shows that the behavior is best described as:
1126*2d1272b8SAndroid Build Coastguard Worker *
1127*2d1272b8SAndroid Build Coastguard Worker * "If ZWJ follows this halant, matra is NOT repositioned after this halant.
1128*2d1272b8SAndroid Build Coastguard Worker * If ZWNJ follows this halant, position is moved after it."
1129*2d1272b8SAndroid Build Coastguard Worker *
1130*2d1272b8SAndroid Build Coastguard Worker * Test case, with Adobe Devanagari or Nirmala UI:
1131*2d1272b8SAndroid Build Coastguard Worker *
1132*2d1272b8SAndroid Build Coastguard Worker * U+091F,U+094D,U+200C,U+092F,U+093F
1133*2d1272b8SAndroid Build Coastguard Worker * (Matra moves to the middle, after ZWNJ.)
1134*2d1272b8SAndroid Build Coastguard Worker *
1135*2d1272b8SAndroid Build Coastguard Worker * U+091F,U+094D,U+200D,U+092F,U+093F
1136*2d1272b8SAndroid Build Coastguard Worker * (Matra does NOT move, stays to the left.)
1137*2d1272b8SAndroid Build Coastguard Worker *
1138*2d1272b8SAndroid Build Coastguard Worker * https://github.com/harfbuzz/harfbuzz/issues/1070
1139*2d1272b8SAndroid Build Coastguard Worker */
1140*2d1272b8SAndroid Build Coastguard Worker
1141*2d1272b8SAndroid Build Coastguard Worker if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
1142*2d1272b8SAndroid Build Coastguard Worker {
1143*2d1272b8SAndroid Build Coastguard Worker /* If we lost track of base, alas, position before last thingy. */
1144*2d1272b8SAndroid Build Coastguard Worker unsigned int new_pos = base == end ? base - 2 : base - 1;
1145*2d1272b8SAndroid Build Coastguard Worker
1146*2d1272b8SAndroid Build Coastguard Worker /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
1147*2d1272b8SAndroid Build Coastguard Worker * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
1148*2d1272b8SAndroid Build Coastguard Worker * We want to position matra after them.
1149*2d1272b8SAndroid Build Coastguard Worker */
1150*2d1272b8SAndroid Build Coastguard Worker if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
1151*2d1272b8SAndroid Build Coastguard Worker {
1152*2d1272b8SAndroid Build Coastguard Worker search:
1153*2d1272b8SAndroid Build Coastguard Worker while (new_pos > start &&
1154*2d1272b8SAndroid Build Coastguard Worker !(is_one_of (info[new_pos], (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)) | FLAG (I_Cat(H))))))
1155*2d1272b8SAndroid Build Coastguard Worker new_pos--;
1156*2d1272b8SAndroid Build Coastguard Worker
1157*2d1272b8SAndroid Build Coastguard Worker /* If we found no Halant we are done.
1158*2d1272b8SAndroid Build Coastguard Worker * Otherwise only proceed if the Halant does
1159*2d1272b8SAndroid Build Coastguard Worker * not belong to the Matra itself! */
1160*2d1272b8SAndroid Build Coastguard Worker if (is_halant (info[new_pos]) &&
1161*2d1272b8SAndroid Build Coastguard Worker info[new_pos].indic_position() != POS_PRE_M)
1162*2d1272b8SAndroid Build Coastguard Worker {
1163*2d1272b8SAndroid Build Coastguard Worker #if 0 // See comment above
1164*2d1272b8SAndroid Build Coastguard Worker /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
1165*2d1272b8SAndroid Build Coastguard Worker if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
1166*2d1272b8SAndroid Build Coastguard Worker new_pos++;
1167*2d1272b8SAndroid Build Coastguard Worker #endif
1168*2d1272b8SAndroid Build Coastguard Worker if (new_pos + 1 < end)
1169*2d1272b8SAndroid Build Coastguard Worker {
1170*2d1272b8SAndroid Build Coastguard Worker /* -> If ZWJ follows this halant, matra is NOT repositioned after this halant. */
1171*2d1272b8SAndroid Build Coastguard Worker if (info[new_pos + 1].indic_category() == I_Cat(ZWJ))
1172*2d1272b8SAndroid Build Coastguard Worker {
1173*2d1272b8SAndroid Build Coastguard Worker /* Keep searching. */
1174*2d1272b8SAndroid Build Coastguard Worker if (new_pos > start)
1175*2d1272b8SAndroid Build Coastguard Worker {
1176*2d1272b8SAndroid Build Coastguard Worker new_pos--;
1177*2d1272b8SAndroid Build Coastguard Worker goto search;
1178*2d1272b8SAndroid Build Coastguard Worker }
1179*2d1272b8SAndroid Build Coastguard Worker }
1180*2d1272b8SAndroid Build Coastguard Worker /* -> If ZWNJ follows this halant, position is moved after it.
1181*2d1272b8SAndroid Build Coastguard Worker *
1182*2d1272b8SAndroid Build Coastguard Worker * IMPLEMENTATION NOTES:
1183*2d1272b8SAndroid Build Coastguard Worker *
1184*2d1272b8SAndroid Build Coastguard Worker * This is taken care of by the state-machine. A Halant,ZWNJ is a terminating
1185*2d1272b8SAndroid Build Coastguard Worker * sequence for a consonant syllable; any pre-base matras occurring after it
1186*2d1272b8SAndroid Build Coastguard Worker * will belong to the subsequent syllable.
1187*2d1272b8SAndroid Build Coastguard Worker */
1188*2d1272b8SAndroid Build Coastguard Worker }
1189*2d1272b8SAndroid Build Coastguard Worker }
1190*2d1272b8SAndroid Build Coastguard Worker else
1191*2d1272b8SAndroid Build Coastguard Worker new_pos = start; /* No move. */
1192*2d1272b8SAndroid Build Coastguard Worker }
1193*2d1272b8SAndroid Build Coastguard Worker
1194*2d1272b8SAndroid Build Coastguard Worker if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M)
1195*2d1272b8SAndroid Build Coastguard Worker {
1196*2d1272b8SAndroid Build Coastguard Worker /* Now go see if there's actually any matras... */
1197*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = new_pos; i > start; i--)
1198*2d1272b8SAndroid Build Coastguard Worker if (info[i - 1].indic_position () == POS_PRE_M)
1199*2d1272b8SAndroid Build Coastguard Worker {
1200*2d1272b8SAndroid Build Coastguard Worker unsigned int old_pos = i - 1;
1201*2d1272b8SAndroid Build Coastguard Worker if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
1202*2d1272b8SAndroid Build Coastguard Worker base--;
1203*2d1272b8SAndroid Build Coastguard Worker
1204*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t tmp = info[old_pos];
1205*2d1272b8SAndroid Build Coastguard Worker memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
1206*2d1272b8SAndroid Build Coastguard Worker info[new_pos] = tmp;
1207*2d1272b8SAndroid Build Coastguard Worker
1208*2d1272b8SAndroid Build Coastguard Worker /* Note: this merge_clusters() is intentionally *after* the reordering.
1209*2d1272b8SAndroid Build Coastguard Worker * Indic matra reordering is special and tricky... */
1210*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (new_pos, hb_min (end, base + 1));
1211*2d1272b8SAndroid Build Coastguard Worker
1212*2d1272b8SAndroid Build Coastguard Worker new_pos--;
1213*2d1272b8SAndroid Build Coastguard Worker }
1214*2d1272b8SAndroid Build Coastguard Worker } else {
1215*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = start; i < base; i++)
1216*2d1272b8SAndroid Build Coastguard Worker if (info[i].indic_position () == POS_PRE_M) {
1217*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (i, hb_min (end, base + 1));
1218*2d1272b8SAndroid Build Coastguard Worker break;
1219*2d1272b8SAndroid Build Coastguard Worker }
1220*2d1272b8SAndroid Build Coastguard Worker }
1221*2d1272b8SAndroid Build Coastguard Worker }
1222*2d1272b8SAndroid Build Coastguard Worker
1223*2d1272b8SAndroid Build Coastguard Worker
1224*2d1272b8SAndroid Build Coastguard Worker /* o Reorder reph:
1225*2d1272b8SAndroid Build Coastguard Worker *
1226*2d1272b8SAndroid Build Coastguard Worker * Reph’s original position is always at the beginning of the syllable,
1227*2d1272b8SAndroid Build Coastguard Worker * (i.e. it is not reordered at the character reordering stage). However,
1228*2d1272b8SAndroid Build Coastguard Worker * it will be reordered according to the basic-forms shaping results.
1229*2d1272b8SAndroid Build Coastguard Worker * Possible positions for reph, depending on the script, are; after main,
1230*2d1272b8SAndroid Build Coastguard Worker * before post-base consonant forms, and after post-base consonant forms.
1231*2d1272b8SAndroid Build Coastguard Worker */
1232*2d1272b8SAndroid Build Coastguard Worker
1233*2d1272b8SAndroid Build Coastguard Worker /* Two cases:
1234*2d1272b8SAndroid Build Coastguard Worker *
1235*2d1272b8SAndroid Build Coastguard Worker * - If repha is encoded as a sequence of characters (Ra,H or Ra,H,ZWJ), then
1236*2d1272b8SAndroid Build Coastguard Worker * we should only move it if the sequence ligated to the repha form.
1237*2d1272b8SAndroid Build Coastguard Worker *
1238*2d1272b8SAndroid Build Coastguard Worker * - If repha is encoded separately and in the logical position, we should only
1239*2d1272b8SAndroid Build Coastguard Worker * move it if it did NOT ligate. If it ligated, it's probably the font trying
1240*2d1272b8SAndroid Build Coastguard Worker * to make it work without the reordering.
1241*2d1272b8SAndroid Build Coastguard Worker */
1242*2d1272b8SAndroid Build Coastguard Worker if (start + 1 < end &&
1243*2d1272b8SAndroid Build Coastguard Worker info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
1244*2d1272b8SAndroid Build Coastguard Worker ((info[start].indic_category() == I_Cat(Repha)) ^
1245*2d1272b8SAndroid Build Coastguard Worker _hb_glyph_info_ligated_and_didnt_multiply (&info[start])))
1246*2d1272b8SAndroid Build Coastguard Worker {
1247*2d1272b8SAndroid Build Coastguard Worker unsigned int new_reph_pos;
1248*2d1272b8SAndroid Build Coastguard Worker reph_position_t reph_pos = indic_plan->config->reph_pos;
1249*2d1272b8SAndroid Build Coastguard Worker
1250*2d1272b8SAndroid Build Coastguard Worker /* 1. If reph should be positioned after post-base consonant forms,
1251*2d1272b8SAndroid Build Coastguard Worker * proceed to step 5.
1252*2d1272b8SAndroid Build Coastguard Worker */
1253*2d1272b8SAndroid Build Coastguard Worker if (reph_pos == REPH_POS_AFTER_POST)
1254*2d1272b8SAndroid Build Coastguard Worker {
1255*2d1272b8SAndroid Build Coastguard Worker goto reph_step_5;
1256*2d1272b8SAndroid Build Coastguard Worker }
1257*2d1272b8SAndroid Build Coastguard Worker
1258*2d1272b8SAndroid Build Coastguard Worker /* 2. If the reph repositioning class is not after post-base: target
1259*2d1272b8SAndroid Build Coastguard Worker * position is after the first explicit halant glyph between the
1260*2d1272b8SAndroid Build Coastguard Worker * first post-reph consonant and last main consonant. If ZWJ or ZWNJ
1261*2d1272b8SAndroid Build Coastguard Worker * are following this halant, position is moved after it. If such
1262*2d1272b8SAndroid Build Coastguard Worker * position is found, this is the target position. Otherwise,
1263*2d1272b8SAndroid Build Coastguard Worker * proceed to the next step.
1264*2d1272b8SAndroid Build Coastguard Worker *
1265*2d1272b8SAndroid Build Coastguard Worker * Note: in old-implementation fonts, where classifications were
1266*2d1272b8SAndroid Build Coastguard Worker * fixed in shaping engine, there was no case where reph position
1267*2d1272b8SAndroid Build Coastguard Worker * will be found on this step.
1268*2d1272b8SAndroid Build Coastguard Worker */
1269*2d1272b8SAndroid Build Coastguard Worker {
1270*2d1272b8SAndroid Build Coastguard Worker new_reph_pos = start + 1;
1271*2d1272b8SAndroid Build Coastguard Worker while (new_reph_pos < base && !is_halant (info[new_reph_pos]))
1272*2d1272b8SAndroid Build Coastguard Worker new_reph_pos++;
1273*2d1272b8SAndroid Build Coastguard Worker
1274*2d1272b8SAndroid Build Coastguard Worker if (new_reph_pos < base && is_halant (info[new_reph_pos]))
1275*2d1272b8SAndroid Build Coastguard Worker {
1276*2d1272b8SAndroid Build Coastguard Worker /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
1277*2d1272b8SAndroid Build Coastguard Worker if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
1278*2d1272b8SAndroid Build Coastguard Worker new_reph_pos++;
1279*2d1272b8SAndroid Build Coastguard Worker goto reph_move;
1280*2d1272b8SAndroid Build Coastguard Worker }
1281*2d1272b8SAndroid Build Coastguard Worker }
1282*2d1272b8SAndroid Build Coastguard Worker
1283*2d1272b8SAndroid Build Coastguard Worker /* 3. If reph should be repositioned after the main consonant: find the
1284*2d1272b8SAndroid Build Coastguard Worker * first consonant not ligated with main, or find the first
1285*2d1272b8SAndroid Build Coastguard Worker * consonant that is not a potential pre-base-reordering Ra.
1286*2d1272b8SAndroid Build Coastguard Worker */
1287*2d1272b8SAndroid Build Coastguard Worker if (reph_pos == REPH_POS_AFTER_MAIN)
1288*2d1272b8SAndroid Build Coastguard Worker {
1289*2d1272b8SAndroid Build Coastguard Worker new_reph_pos = base;
1290*2d1272b8SAndroid Build Coastguard Worker while (new_reph_pos + 1 < end && info[new_reph_pos + 1].indic_position() <= POS_AFTER_MAIN)
1291*2d1272b8SAndroid Build Coastguard Worker new_reph_pos++;
1292*2d1272b8SAndroid Build Coastguard Worker if (new_reph_pos < end)
1293*2d1272b8SAndroid Build Coastguard Worker goto reph_move;
1294*2d1272b8SAndroid Build Coastguard Worker }
1295*2d1272b8SAndroid Build Coastguard Worker
1296*2d1272b8SAndroid Build Coastguard Worker /* 4. If reph should be positioned before post-base consonant, find
1297*2d1272b8SAndroid Build Coastguard Worker * first post-base classified consonant not ligated with main. If no
1298*2d1272b8SAndroid Build Coastguard Worker * consonant is found, the target position should be before the
1299*2d1272b8SAndroid Build Coastguard Worker * first matra, syllable modifier sign or vedic sign.
1300*2d1272b8SAndroid Build Coastguard Worker */
1301*2d1272b8SAndroid Build Coastguard Worker /* This is our take on what step 4 is trying to say (and failing, BADLY). */
1302*2d1272b8SAndroid Build Coastguard Worker if (reph_pos == REPH_POS_AFTER_SUB)
1303*2d1272b8SAndroid Build Coastguard Worker {
1304*2d1272b8SAndroid Build Coastguard Worker new_reph_pos = base;
1305*2d1272b8SAndroid Build Coastguard Worker while (new_reph_pos + 1 < end &&
1306*2d1272b8SAndroid Build Coastguard Worker !( FLAG_UNSAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
1307*2d1272b8SAndroid Build Coastguard Worker new_reph_pos++;
1308*2d1272b8SAndroid Build Coastguard Worker if (new_reph_pos < end)
1309*2d1272b8SAndroid Build Coastguard Worker goto reph_move;
1310*2d1272b8SAndroid Build Coastguard Worker }
1311*2d1272b8SAndroid Build Coastguard Worker
1312*2d1272b8SAndroid Build Coastguard Worker /* 5. If no consonant is found in steps 3 or 4, move reph to a position
1313*2d1272b8SAndroid Build Coastguard Worker * immediately before the first post-base matra, syllable modifier
1314*2d1272b8SAndroid Build Coastguard Worker * sign or vedic sign that has a reordering class after the intended
1315*2d1272b8SAndroid Build Coastguard Worker * reph position. For example, if the reordering position for reph
1316*2d1272b8SAndroid Build Coastguard Worker * is post-main, it will skip above-base matras that also have a
1317*2d1272b8SAndroid Build Coastguard Worker * post-main position.
1318*2d1272b8SAndroid Build Coastguard Worker */
1319*2d1272b8SAndroid Build Coastguard Worker reph_step_5:
1320*2d1272b8SAndroid Build Coastguard Worker {
1321*2d1272b8SAndroid Build Coastguard Worker /* Copied from step 2. */
1322*2d1272b8SAndroid Build Coastguard Worker new_reph_pos = start + 1;
1323*2d1272b8SAndroid Build Coastguard Worker while (new_reph_pos < base && !is_halant (info[new_reph_pos]))
1324*2d1272b8SAndroid Build Coastguard Worker new_reph_pos++;
1325*2d1272b8SAndroid Build Coastguard Worker
1326*2d1272b8SAndroid Build Coastguard Worker if (new_reph_pos < base && is_halant (info[new_reph_pos]))
1327*2d1272b8SAndroid Build Coastguard Worker {
1328*2d1272b8SAndroid Build Coastguard Worker /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
1329*2d1272b8SAndroid Build Coastguard Worker if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
1330*2d1272b8SAndroid Build Coastguard Worker new_reph_pos++;
1331*2d1272b8SAndroid Build Coastguard Worker goto reph_move;
1332*2d1272b8SAndroid Build Coastguard Worker }
1333*2d1272b8SAndroid Build Coastguard Worker }
1334*2d1272b8SAndroid Build Coastguard Worker /* See https://github.com/harfbuzz/harfbuzz/issues/2298#issuecomment-615318654 */
1335*2d1272b8SAndroid Build Coastguard Worker
1336*2d1272b8SAndroid Build Coastguard Worker /* 6. Otherwise, reorder reph to the end of the syllable.
1337*2d1272b8SAndroid Build Coastguard Worker */
1338*2d1272b8SAndroid Build Coastguard Worker {
1339*2d1272b8SAndroid Build Coastguard Worker new_reph_pos = end - 1;
1340*2d1272b8SAndroid Build Coastguard Worker while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
1341*2d1272b8SAndroid Build Coastguard Worker new_reph_pos--;
1342*2d1272b8SAndroid Build Coastguard Worker
1343*2d1272b8SAndroid Build Coastguard Worker /*
1344*2d1272b8SAndroid Build Coastguard Worker * If the Reph is to be ending up after a Matra,Halant sequence,
1345*2d1272b8SAndroid Build Coastguard Worker * position it before that Halant so it can interact with the Matra.
1346*2d1272b8SAndroid Build Coastguard Worker * However, if it's a plain Consonant,Halant we shouldn't do that.
1347*2d1272b8SAndroid Build Coastguard Worker * Uniscribe doesn't do this.
1348*2d1272b8SAndroid Build Coastguard Worker * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
1349*2d1272b8SAndroid Build Coastguard Worker */
1350*2d1272b8SAndroid Build Coastguard Worker if (!indic_plan->uniscribe_bug_compatible &&
1351*2d1272b8SAndroid Build Coastguard Worker unlikely (is_halant (info[new_reph_pos])))
1352*2d1272b8SAndroid Build Coastguard Worker {
1353*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < new_reph_pos; i++)
1354*2d1272b8SAndroid Build Coastguard Worker if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst))))
1355*2d1272b8SAndroid Build Coastguard Worker {
1356*2d1272b8SAndroid Build Coastguard Worker /* Ok, got it. */
1357*2d1272b8SAndroid Build Coastguard Worker new_reph_pos--;
1358*2d1272b8SAndroid Build Coastguard Worker }
1359*2d1272b8SAndroid Build Coastguard Worker }
1360*2d1272b8SAndroid Build Coastguard Worker
1361*2d1272b8SAndroid Build Coastguard Worker goto reph_move;
1362*2d1272b8SAndroid Build Coastguard Worker }
1363*2d1272b8SAndroid Build Coastguard Worker
1364*2d1272b8SAndroid Build Coastguard Worker reph_move:
1365*2d1272b8SAndroid Build Coastguard Worker {
1366*2d1272b8SAndroid Build Coastguard Worker /* Move */
1367*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (start, new_reph_pos + 1);
1368*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t reph = info[start];
1369*2d1272b8SAndroid Build Coastguard Worker memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
1370*2d1272b8SAndroid Build Coastguard Worker info[new_reph_pos] = reph;
1371*2d1272b8SAndroid Build Coastguard Worker
1372*2d1272b8SAndroid Build Coastguard Worker if (start < base && base <= new_reph_pos)
1373*2d1272b8SAndroid Build Coastguard Worker base--;
1374*2d1272b8SAndroid Build Coastguard Worker }
1375*2d1272b8SAndroid Build Coastguard Worker }
1376*2d1272b8SAndroid Build Coastguard Worker
1377*2d1272b8SAndroid Build Coastguard Worker
1378*2d1272b8SAndroid Build Coastguard Worker /* o Reorder pre-base-reordering consonants:
1379*2d1272b8SAndroid Build Coastguard Worker *
1380*2d1272b8SAndroid Build Coastguard Worker * If a pre-base-reordering consonant is found, reorder it according to
1381*2d1272b8SAndroid Build Coastguard Worker * the following rules:
1382*2d1272b8SAndroid Build Coastguard Worker */
1383*2d1272b8SAndroid Build Coastguard Worker
1384*2d1272b8SAndroid Build Coastguard Worker if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
1385*2d1272b8SAndroid Build Coastguard Worker {
1386*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = base + 1; i < end; i++)
1387*2d1272b8SAndroid Build Coastguard Worker if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0)
1388*2d1272b8SAndroid Build Coastguard Worker {
1389*2d1272b8SAndroid Build Coastguard Worker /* 1. Only reorder a glyph produced by substitution during application
1390*2d1272b8SAndroid Build Coastguard Worker * of the <pref> feature. (Note that a font may shape a Ra consonant with
1391*2d1272b8SAndroid Build Coastguard Worker * the feature generally but block it in certain contexts.)
1392*2d1272b8SAndroid Build Coastguard Worker */
1393*2d1272b8SAndroid Build Coastguard Worker /* Note: We just check that something got substituted. We don't check that
1394*2d1272b8SAndroid Build Coastguard Worker * the <pref> feature actually did it...
1395*2d1272b8SAndroid Build Coastguard Worker *
1396*2d1272b8SAndroid Build Coastguard Worker * Reorder pref only if it ligated. */
1397*2d1272b8SAndroid Build Coastguard Worker if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i]))
1398*2d1272b8SAndroid Build Coastguard Worker {
1399*2d1272b8SAndroid Build Coastguard Worker /*
1400*2d1272b8SAndroid Build Coastguard Worker * 2. Try to find a target position the same way as for pre-base matra.
1401*2d1272b8SAndroid Build Coastguard Worker * If it is found, reorder pre-base consonant glyph.
1402*2d1272b8SAndroid Build Coastguard Worker *
1403*2d1272b8SAndroid Build Coastguard Worker * 3. If position is not found, reorder immediately before main
1404*2d1272b8SAndroid Build Coastguard Worker * consonant.
1405*2d1272b8SAndroid Build Coastguard Worker */
1406*2d1272b8SAndroid Build Coastguard Worker
1407*2d1272b8SAndroid Build Coastguard Worker unsigned int new_pos = base;
1408*2d1272b8SAndroid Build Coastguard Worker /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
1409*2d1272b8SAndroid Build Coastguard Worker * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
1410*2d1272b8SAndroid Build Coastguard Worker * We want to position matra after them.
1411*2d1272b8SAndroid Build Coastguard Worker */
1412*2d1272b8SAndroid Build Coastguard Worker if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
1413*2d1272b8SAndroid Build Coastguard Worker {
1414*2d1272b8SAndroid Build Coastguard Worker while (new_pos > start &&
1415*2d1272b8SAndroid Build Coastguard Worker !(is_one_of (info[new_pos - 1], FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)) | FLAG (I_Cat(H)))))
1416*2d1272b8SAndroid Build Coastguard Worker new_pos--;
1417*2d1272b8SAndroid Build Coastguard Worker }
1418*2d1272b8SAndroid Build Coastguard Worker
1419*2d1272b8SAndroid Build Coastguard Worker if (new_pos > start && is_halant (info[new_pos - 1]))
1420*2d1272b8SAndroid Build Coastguard Worker {
1421*2d1272b8SAndroid Build Coastguard Worker /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
1422*2d1272b8SAndroid Build Coastguard Worker if (new_pos < end && is_joiner (info[new_pos]))
1423*2d1272b8SAndroid Build Coastguard Worker new_pos++;
1424*2d1272b8SAndroid Build Coastguard Worker }
1425*2d1272b8SAndroid Build Coastguard Worker
1426*2d1272b8SAndroid Build Coastguard Worker {
1427*2d1272b8SAndroid Build Coastguard Worker unsigned int old_pos = i;
1428*2d1272b8SAndroid Build Coastguard Worker
1429*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (new_pos, old_pos + 1);
1430*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t tmp = info[old_pos];
1431*2d1272b8SAndroid Build Coastguard Worker memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
1432*2d1272b8SAndroid Build Coastguard Worker info[new_pos] = tmp;
1433*2d1272b8SAndroid Build Coastguard Worker
1434*2d1272b8SAndroid Build Coastguard Worker if (new_pos <= base && base < old_pos)
1435*2d1272b8SAndroid Build Coastguard Worker base++;
1436*2d1272b8SAndroid Build Coastguard Worker }
1437*2d1272b8SAndroid Build Coastguard Worker }
1438*2d1272b8SAndroid Build Coastguard Worker
1439*2d1272b8SAndroid Build Coastguard Worker break;
1440*2d1272b8SAndroid Build Coastguard Worker }
1441*2d1272b8SAndroid Build Coastguard Worker }
1442*2d1272b8SAndroid Build Coastguard Worker
1443*2d1272b8SAndroid Build Coastguard Worker
1444*2d1272b8SAndroid Build Coastguard Worker /* Apply 'init' to the Left Matra if it's a word start. */
1445*2d1272b8SAndroid Build Coastguard Worker if (info[start].indic_position () == POS_PRE_M)
1446*2d1272b8SAndroid Build Coastguard Worker {
1447*2d1272b8SAndroid Build Coastguard Worker if (!start ||
1448*2d1272b8SAndroid Build Coastguard Worker !(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
1449*2d1272b8SAndroid Build Coastguard Worker FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
1450*2d1272b8SAndroid Build Coastguard Worker info[start].mask |= indic_plan->mask_array[INDIC_INIT];
1451*2d1272b8SAndroid Build Coastguard Worker else
1452*2d1272b8SAndroid Build Coastguard Worker buffer->unsafe_to_break (start - 1, start + 1);
1453*2d1272b8SAndroid Build Coastguard Worker }
1454*2d1272b8SAndroid Build Coastguard Worker
1455*2d1272b8SAndroid Build Coastguard Worker
1456*2d1272b8SAndroid Build Coastguard Worker /*
1457*2d1272b8SAndroid Build Coastguard Worker * Finish off the clusters and go home!
1458*2d1272b8SAndroid Build Coastguard Worker */
1459*2d1272b8SAndroid Build Coastguard Worker if (indic_plan->uniscribe_bug_compatible)
1460*2d1272b8SAndroid Build Coastguard Worker {
1461*2d1272b8SAndroid Build Coastguard Worker switch ((hb_tag_t) plan->props.script)
1462*2d1272b8SAndroid Build Coastguard Worker {
1463*2d1272b8SAndroid Build Coastguard Worker case HB_SCRIPT_TAMIL:
1464*2d1272b8SAndroid Build Coastguard Worker break;
1465*2d1272b8SAndroid Build Coastguard Worker
1466*2d1272b8SAndroid Build Coastguard Worker default:
1467*2d1272b8SAndroid Build Coastguard Worker /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil.
1468*2d1272b8SAndroid Build Coastguard Worker * This means, half forms are submerged into the main consonant's cluster.
1469*2d1272b8SAndroid Build Coastguard Worker * This is unnecessary, and makes cursor positioning harder, but that's what
1470*2d1272b8SAndroid Build Coastguard Worker * Uniscribe does. */
1471*2d1272b8SAndroid Build Coastguard Worker buffer->merge_clusters (start, end);
1472*2d1272b8SAndroid Build Coastguard Worker break;
1473*2d1272b8SAndroid Build Coastguard Worker }
1474*2d1272b8SAndroid Build Coastguard Worker }
1475*2d1272b8SAndroid Build Coastguard Worker }
1476*2d1272b8SAndroid Build Coastguard Worker
1477*2d1272b8SAndroid Build Coastguard Worker
1478*2d1272b8SAndroid Build Coastguard Worker static bool
final_reordering_indic(const hb_ot_shape_plan_t * plan,hb_font_t * font HB_UNUSED,hb_buffer_t * buffer)1479*2d1272b8SAndroid Build Coastguard Worker final_reordering_indic (const hb_ot_shape_plan_t *plan,
1480*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font HB_UNUSED,
1481*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer)
1482*2d1272b8SAndroid Build Coastguard Worker {
1483*2d1272b8SAndroid Build Coastguard Worker unsigned int count = buffer->len;
1484*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!count)) return false;
1485*2d1272b8SAndroid Build Coastguard Worker
1486*2d1272b8SAndroid Build Coastguard Worker if (buffer->message (font, "start reordering indic final")) {
1487*2d1272b8SAndroid Build Coastguard Worker foreach_syllable (buffer, start, end)
1488*2d1272b8SAndroid Build Coastguard Worker final_reordering_syllable_indic (plan, buffer, start, end);
1489*2d1272b8SAndroid Build Coastguard Worker (void) buffer->message (font, "end reordering indic final");
1490*2d1272b8SAndroid Build Coastguard Worker }
1491*2d1272b8SAndroid Build Coastguard Worker
1492*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
1493*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
1494*2d1272b8SAndroid Build Coastguard Worker
1495*2d1272b8SAndroid Build Coastguard Worker return false;
1496*2d1272b8SAndroid Build Coastguard Worker }
1497*2d1272b8SAndroid Build Coastguard Worker
1498*2d1272b8SAndroid Build Coastguard Worker
1499*2d1272b8SAndroid Build Coastguard Worker static void
preprocess_text_indic(const hb_ot_shape_plan_t * plan,hb_buffer_t * buffer,hb_font_t * font)1500*2d1272b8SAndroid Build Coastguard Worker preprocess_text_indic (const hb_ot_shape_plan_t *plan,
1501*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
1502*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font)
1503*2d1272b8SAndroid Build Coastguard Worker {
1504*2d1272b8SAndroid Build Coastguard Worker const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
1505*2d1272b8SAndroid Build Coastguard Worker if (!indic_plan->uniscribe_bug_compatible)
1506*2d1272b8SAndroid Build Coastguard Worker _hb_preprocess_text_vowel_constraints (plan, buffer, font);
1507*2d1272b8SAndroid Build Coastguard Worker }
1508*2d1272b8SAndroid Build Coastguard Worker
1509*2d1272b8SAndroid Build Coastguard Worker static bool
decompose_indic(const hb_ot_shape_normalize_context_t * c,hb_codepoint_t ab,hb_codepoint_t * a,hb_codepoint_t * b)1510*2d1272b8SAndroid Build Coastguard Worker decompose_indic (const hb_ot_shape_normalize_context_t *c,
1511*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t ab,
1512*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t *a,
1513*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t *b)
1514*2d1272b8SAndroid Build Coastguard Worker {
1515*2d1272b8SAndroid Build Coastguard Worker switch (ab)
1516*2d1272b8SAndroid Build Coastguard Worker {
1517*2d1272b8SAndroid Build Coastguard Worker /* Don't decompose these. */
1518*2d1272b8SAndroid Build Coastguard Worker case 0x0931u : return false; /* DEVANAGARI LETTER RRA */
1519*2d1272b8SAndroid Build Coastguard Worker // https://github.com/harfbuzz/harfbuzz/issues/779
1520*2d1272b8SAndroid Build Coastguard Worker case 0x09DCu : return false; /* BENGALI LETTER RRA */
1521*2d1272b8SAndroid Build Coastguard Worker case 0x09DDu : return false; /* BENGALI LETTER RHA */
1522*2d1272b8SAndroid Build Coastguard Worker case 0x0B94u : return false; /* TAMIL LETTER AU */
1523*2d1272b8SAndroid Build Coastguard Worker
1524*2d1272b8SAndroid Build Coastguard Worker
1525*2d1272b8SAndroid Build Coastguard Worker /*
1526*2d1272b8SAndroid Build Coastguard Worker * Decompose split matras that don't have Unicode decompositions.
1527*2d1272b8SAndroid Build Coastguard Worker */
1528*2d1272b8SAndroid Build Coastguard Worker
1529*2d1272b8SAndroid Build Coastguard Worker #if 0
1530*2d1272b8SAndroid Build Coastguard Worker /* Gujarati */
1531*2d1272b8SAndroid Build Coastguard Worker /* This one has no decomposition in Unicode, but needs no decomposition either. */
1532*2d1272b8SAndroid Build Coastguard Worker /* case 0x0AC9u : return false; */
1533*2d1272b8SAndroid Build Coastguard Worker
1534*2d1272b8SAndroid Build Coastguard Worker /* Oriya */
1535*2d1272b8SAndroid Build Coastguard Worker case 0x0B57u : *a = no decomp, -> RIGHT; return true;
1536*2d1272b8SAndroid Build Coastguard Worker #endif
1537*2d1272b8SAndroid Build Coastguard Worker }
1538*2d1272b8SAndroid Build Coastguard Worker
1539*2d1272b8SAndroid Build Coastguard Worker return (bool) c->unicode->decompose (ab, a, b);
1540*2d1272b8SAndroid Build Coastguard Worker }
1541*2d1272b8SAndroid Build Coastguard Worker
1542*2d1272b8SAndroid Build Coastguard Worker static bool
compose_indic(const hb_ot_shape_normalize_context_t * c,hb_codepoint_t a,hb_codepoint_t b,hb_codepoint_t * ab)1543*2d1272b8SAndroid Build Coastguard Worker compose_indic (const hb_ot_shape_normalize_context_t *c,
1544*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t a,
1545*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t b,
1546*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t *ab)
1547*2d1272b8SAndroid Build Coastguard Worker {
1548*2d1272b8SAndroid Build Coastguard Worker /* Avoid recomposing split matras. */
1549*2d1272b8SAndroid Build Coastguard Worker if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
1550*2d1272b8SAndroid Build Coastguard Worker return false;
1551*2d1272b8SAndroid Build Coastguard Worker
1552*2d1272b8SAndroid Build Coastguard Worker /* Composition-exclusion exceptions that we want to recompose. */
1553*2d1272b8SAndroid Build Coastguard Worker if (a == 0x09AFu && b == 0x09BCu) { *ab = 0x09DFu; return true; }
1554*2d1272b8SAndroid Build Coastguard Worker
1555*2d1272b8SAndroid Build Coastguard Worker return (bool) c->unicode->compose (a, b, ab);
1556*2d1272b8SAndroid Build Coastguard Worker }
1557*2d1272b8SAndroid Build Coastguard Worker
1558*2d1272b8SAndroid Build Coastguard Worker
1559*2d1272b8SAndroid Build Coastguard Worker const hb_ot_shaper_t _hb_ot_shaper_indic =
1560*2d1272b8SAndroid Build Coastguard Worker {
1561*2d1272b8SAndroid Build Coastguard Worker collect_features_indic,
1562*2d1272b8SAndroid Build Coastguard Worker override_features_indic,
1563*2d1272b8SAndroid Build Coastguard Worker data_create_indic,
1564*2d1272b8SAndroid Build Coastguard Worker data_destroy_indic,
1565*2d1272b8SAndroid Build Coastguard Worker preprocess_text_indic,
1566*2d1272b8SAndroid Build Coastguard Worker nullptr, /* postprocess_glyphs */
1567*2d1272b8SAndroid Build Coastguard Worker decompose_indic,
1568*2d1272b8SAndroid Build Coastguard Worker compose_indic,
1569*2d1272b8SAndroid Build Coastguard Worker setup_masks_indic,
1570*2d1272b8SAndroid Build Coastguard Worker nullptr, /* reorder_marks */
1571*2d1272b8SAndroid Build Coastguard Worker HB_TAG_NONE, /* gpos_tag */
1572*2d1272b8SAndroid Build Coastguard Worker HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
1573*2d1272b8SAndroid Build Coastguard Worker HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
1574*2d1272b8SAndroid Build Coastguard Worker false, /* fallback_position */
1575*2d1272b8SAndroid Build Coastguard Worker };
1576*2d1272b8SAndroid Build Coastguard Worker
1577*2d1272b8SAndroid Build Coastguard Worker
1578*2d1272b8SAndroid Build Coastguard Worker #endif
1579