blob: addfb87ba72fcf351c2a30952649cd6692a07e81 [file] [log] [blame]
Behdad Esfahboda9663952015-07-20 14:24:55 +01001/*
2 * Copyright © 2015 Mozilla Foundation.
3 * Copyright © 2015 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Mozilla Author(s): Jonathan Kew
26 * Google Author(s): Behdad Esfahbod
27 */
28
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070029#include "hb-ot-shape-complex-use.hh"
30#include "hb-ot-shape-complex-arabic.hh"
David Corbett205737a2018-10-12 16:54:54 -040031#include "hb-ot-shape-complex-vowel-constraints.hh"
Behdad Esfahboda9663952015-07-20 14:24:55 +010032
33/* buffer var allocations */
34#define use_category() complex_var_u8_0()
35
36
37/*
38 * Universal Shaping Engine.
Ebrahim Byagowif24b0b92018-04-12 13:40:45 +043039 * https://docs.microsoft.com/en-us/typography/script-development/use
Behdad Esfahboda9663952015-07-20 14:24:55 +010040 */
41
42static const hb_tag_t
43basic_features[] =
44{
45 /*
46 * Basic features.
Behdad Esfahbod4febed62015-07-21 10:24:32 +010047 * These features are applied all at once, before reordering.
Behdad Esfahboda9663952015-07-20 14:24:55 +010048 */
Behdad Esfahbod4febed62015-07-21 10:24:32 +010049 HB_TAG('r','k','r','f'),
Behdad Esfahboda9663952015-07-20 14:24:55 +010050 HB_TAG('a','b','v','f'),
51 HB_TAG('b','l','w','f'),
Behdad Esfahbod4febed62015-07-21 10:24:32 +010052 HB_TAG('h','a','l','f'),
Behdad Esfahboda9663952015-07-20 14:24:55 +010053 HB_TAG('p','s','t','f'),
Behdad Esfahbod4febed62015-07-21 10:24:32 +010054 HB_TAG('v','a','t','u'),
55 HB_TAG('c','j','c','t'),
Behdad Esfahboda9663952015-07-20 14:24:55 +010056};
57static const hb_tag_t
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +010058arabic_features[] =
59{
60 HB_TAG('i','s','o','l'),
61 HB_TAG('i','n','i','t'),
62 HB_TAG('m','e','d','i'),
63 HB_TAG('f','i','n','a'),
64 /* The spec doesn't specify these but we apply anyway, since our Arabic shaper
65 * does. These are only used in Syriac spec. */
66 HB_TAG('m','e','d','2'),
67 HB_TAG('f','i','n','2'),
68 HB_TAG('f','i','n','3'),
69};
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +010070/* Same order as arabic_features. Don't need Syriac stuff.*/
71enum joining_form_t {
72 ISOL,
73 INIT,
74 MEDI,
75 FINA,
76 _NONE
77};
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +010078static const hb_tag_t
Behdad Esfahboda9663952015-07-20 14:24:55 +010079other_features[] =
80{
81 /*
82 * Other features.
Behdad Esfahbod4febed62015-07-21 10:24:32 +010083 * These features are applied all at once, after reordering.
Behdad Esfahboda9663952015-07-20 14:24:55 +010084 */
Behdad Esfahboda9663952015-07-20 14:24:55 +010085 HB_TAG('a','b','v','s'),
86 HB_TAG('b','l','w','s'),
Behdad Esfahbod4febed62015-07-21 10:24:32 +010087 HB_TAG('h','a','l','n'),
88 HB_TAG('p','r','e','s'),
Behdad Esfahboda9663952015-07-20 14:24:55 +010089 HB_TAG('p','s','t','s'),
Behdad Esfahbod3583fb02018-09-23 22:33:38 -040090};
91static const hb_tag_t
92positioning_features[] =
93{
94 /*
95 * Positioning features.
96 * We don't care about the types.
97 */
Behdad Esfahboda9663952015-07-20 14:24:55 +010098 HB_TAG('d','i','s','t'),
Behdad Esfahbod4febed62015-07-21 10:24:32 +010099 HB_TAG('a','b','v','m'),
100 HB_TAG('b','l','w','m'),
Behdad Esfahboda9663952015-07-20 14:24:55 +0100101};
102
103static void
104setup_syllables (const hb_ot_shape_plan_t *plan,
105 hb_font_t *font,
106 hb_buffer_t *buffer);
107static void
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100108clear_substitution_flags (const hb_ot_shape_plan_t *plan,
109 hb_font_t *font,
110 hb_buffer_t *buffer);
111static void
112record_rphf (const hb_ot_shape_plan_t *plan,
113 hb_font_t *font,
114 hb_buffer_t *buffer);
115static void
116record_pref (const hb_ot_shape_plan_t *plan,
117 hb_font_t *font,
118 hb_buffer_t *buffer);
119static void
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100120reorder (const hb_ot_shape_plan_t *plan,
121 hb_font_t *font,
122 hb_buffer_t *buffer);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100123
124static void
125collect_features_use (hb_ot_shape_planner_t *plan)
126{
127 hb_ot_map_builder_t *map = &plan->map;
128
129 /* Do this before any lookups have been applied. */
130 map->add_gsub_pause (setup_syllables);
131
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100132 /* "Default glyph pre-processing group" */
Behdad Esfahbodf048ead2018-09-24 18:01:53 -0400133 map->enable_feature (HB_TAG('l','o','c','l'));
134 map->enable_feature (HB_TAG('c','c','m','p'));
135 map->enable_feature (HB_TAG('n','u','k','t'));
Behdad Esfahbod0a371fe2018-10-02 14:48:39 +0200136 map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100137
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100138 /* "Reordering group" */
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100139 map->add_gsub_pause (clear_substitution_flags);
Behdad Esfahbodf048ead2018-09-24 18:01:53 -0400140 map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ);
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100141 map->add_gsub_pause (record_rphf);
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100142 map->add_gsub_pause (clear_substitution_flags);
Behdad Esfahbod0a371fe2018-10-02 14:48:39 +0200143 map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ);
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100144 map->add_gsub_pause (record_pref);
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100145
146 /* "Orthographic unit shaping group" */
Behdad Esfahboda9663952015-07-20 14:24:55 +0100147 for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
Behdad Esfahbod0a371fe2018-10-02 14:48:39 +0200148 map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100149
150 map->add_gsub_pause (reorder);
151
152 /* "Topographical features" */
Behdad Esfahbodf1c20e12015-07-27 12:16:02 +0200153 for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
Behdad Esfahbodf048ead2018-09-24 18:01:53 -0400154 map->add_feature (arabic_features[i]);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200155 map->add_gsub_pause (nullptr);
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100156
Behdad Esfahbod3583fb02018-09-23 22:33:38 -0400157 /* "Standard typographic presentation" */
Behdad Esfahboda9663952015-07-20 14:24:55 +0100158 for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
Behdad Esfahbod0a371fe2018-10-02 14:48:39 +0200159 map->enable_feature (other_features[i], F_MANUAL_ZWJ);
Behdad Esfahbod3583fb02018-09-23 22:33:38 -0400160
161 /* "Positional feature application" */
162 for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++)
Behdad Esfahbodf048ead2018-09-24 18:01:53 -0400163 map->enable_feature (positioning_features[i]);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100164}
165
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100166struct use_shape_plan_t
167{
168 ASSERT_POD ();
169
170 hb_mask_t rphf_mask;
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100171
172 arabic_shape_plan_t *arabic_plan;
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100173};
174
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100175static bool
176has_arabic_joining (hb_script_t script)
177{
178 /* List of scripts that have data in arabic-table. */
179 switch ((int) script)
180 {
181 /* Unicode-1.1 additions */
182 case HB_SCRIPT_ARABIC:
183
184 /* Unicode-3.0 additions */
185 case HB_SCRIPT_MONGOLIAN:
186 case HB_SCRIPT_SYRIAC:
187
188 /* Unicode-5.0 additions */
189 case HB_SCRIPT_NKO:
190 case HB_SCRIPT_PHAGS_PA:
191
192 /* Unicode-6.0 additions */
193 case HB_SCRIPT_MANDAIC:
194
195 /* Unicode-7.0 additions */
196 case HB_SCRIPT_MANICHAEAN:
197 case HB_SCRIPT_PSALTER_PAHLAVI:
198
Behdad Esfahbod691086f2016-05-06 12:08:18 +0100199 /* Unicode-9.0 additions */
200 case HB_SCRIPT_ADLAM:
201
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100202 return true;
203
204 default:
205 return false;
206 }
207}
208
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100209static void *
210data_create_use (const hb_ot_shape_plan_t *plan)
211{
212 use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t));
213 if (unlikely (!use_plan))
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200214 return nullptr;
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100215
216 use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f'));
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100217
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100218 if (has_arabic_joining (plan->props.script))
219 {
220 use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan);
221 if (unlikely (!use_plan->arabic_plan))
222 {
223 free (use_plan);
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200224 return nullptr;
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100225 }
226 }
227
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100228 return use_plan;
229}
230
231static void
232data_destroy_use (void *data)
233{
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100234 use_shape_plan_t *use_plan = (use_shape_plan_t *) data;
235
236 if (use_plan->arabic_plan)
237 data_destroy_arabic (use_plan->arabic_plan);
238
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100239 free (data);
240}
Behdad Esfahboda9663952015-07-20 14:24:55 +0100241
242enum syllable_type_t {
243 independent_cluster,
244 virama_terminated_cluster,
Behdad Esfahbod9b6312f2016-05-06 17:41:49 +0100245 standard_cluster,
Behdad Esfahboda9663952015-07-20 14:24:55 +0100246 number_joiner_terminated_cluster,
247 numeral_cluster,
248 symbol_cluster,
Behdad Esfahbod40c4a992015-07-21 17:14:54 +0100249 broken_cluster,
Behdad Esfahbod3e4e7612016-05-06 17:28:25 +0100250 non_cluster,
Behdad Esfahboda9663952015-07-20 14:24:55 +0100251};
252
253#include "hb-ot-shape-complex-use-machine.hh"
254
255
Behdad Esfahboda9663952015-07-20 14:24:55 +0100256static void
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100257setup_masks_use (const hb_ot_shape_plan_t *plan,
Behdad Esfahboda9663952015-07-20 14:24:55 +0100258 hb_buffer_t *buffer,
259 hb_font_t *font HB_UNUSED)
260{
Behdad Esfahbod8ba9e682015-07-22 11:16:01 +0100261 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
262
263 /* Do this before allocating use_category(). */
264 if (use_plan->arabic_plan)
265 {
266 setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script);
267 }
268
Behdad Esfahboda9663952015-07-20 14:24:55 +0100269 HB_BUFFER_ALLOCATE_VAR (buffer, use_category);
270
271 /* We cannot setup masks here. We save information about characters
272 * and setup masks later on in a pause-callback. */
273
274 unsigned int count = buffer->len;
275 hb_glyph_info_t *info = buffer->info;
276 for (unsigned int i = 0; i < count; i++)
Behdad Esfahbod50780442018-02-13 21:46:28 -0800277 info[i].use_category() = hb_use_get_category (info[i].codepoint);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100278}
279
280static void
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100281setup_rphf_mask (const hb_ot_shape_plan_t *plan,
Behdad Esfahboda9663952015-07-20 14:24:55 +0100282 hb_buffer_t *buffer)
283{
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100284 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
Behdad Esfahbodac596512015-07-22 11:54:02 +0100285
286 hb_mask_t mask = use_plan->rphf_mask;
287 if (!mask) return;
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100288
289 hb_glyph_info_t *info = buffer->info;
Behdad Esfahbodac596512015-07-22 11:54:02 +0100290
291 foreach_syllable (buffer, start, end)
292 {
293 unsigned int limit = info[start].use_category() == USE_R ? 1 : MIN (3u, end - start);
294 for (unsigned int i = start; i < start + limit; i++)
295 info[i].mask |= mask;
296 }
Behdad Esfahboda9663952015-07-20 14:24:55 +0100297}
298
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100299static void
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100300setup_topographical_masks (const hb_ot_shape_plan_t *plan,
301 hb_buffer_t *buffer)
302{
Behdad Esfahbod862b1642015-12-18 13:54:06 +0000303 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
304 if (use_plan->arabic_plan)
305 return;
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100306
Behdad Esfahbod606bf572018-09-16 19:33:48 +0200307 static_assert ((INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4), "");
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100308 hb_mask_t masks[4], all_masks = 0;
309 for (unsigned int i = 0; i < 4; i++)
310 {
311 masks[i] = plan->map.get_1_mask (arabic_features[i]);
312 if (masks[i] == plan->map.get_global_mask ())
313 masks[i] = 0;
314 all_masks |= masks[i];
315 }
316 if (!all_masks)
317 return;
318 hb_mask_t other_masks = ~all_masks;
319
320 unsigned int last_start = 0;
321 joining_form_t last_form = _NONE;
322 hb_glyph_info_t *info = buffer->info;
323 foreach_syllable (buffer, start, end)
324 {
325 syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F);
326 switch (syllable_type)
327 {
328 case independent_cluster:
329 case symbol_cluster:
Behdad Esfahbod3e4e7612016-05-06 17:28:25 +0100330 case non_cluster:
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100331 /* These don't join. Nothing to do. */
332 last_form = _NONE;
333 break;
334
335 case virama_terminated_cluster:
Behdad Esfahbod9b6312f2016-05-06 17:41:49 +0100336 case standard_cluster:
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100337 case number_joiner_terminated_cluster:
338 case numeral_cluster:
339 case broken_cluster:
340
341 bool join = last_form == FINA || last_form == ISOL;
342
343 if (join)
344 {
345 /* Fixup previous syllable's form. */
346 last_form = last_form == FINA ? MEDI : INIT;
347 for (unsigned int i = last_start; i < start; i++)
348 info[i].mask = (info[i].mask & other_masks) | masks[last_form];
349 }
350
351 /* Form for this syllable. */
352 last_form = join ? FINA : ISOL;
353 for (unsigned int i = start; i < end; i++)
354 info[i].mask = (info[i].mask & other_masks) | masks[last_form];
355
356 break;
357 }
358
359 last_start = start;
360 }
361}
362
363static void
364setup_syllables (const hb_ot_shape_plan_t *plan,
365 hb_font_t *font HB_UNUSED,
366 hb_buffer_t *buffer)
367{
368 find_syllables (buffer);
Behdad Esfahbod9e005c52017-08-10 18:45:33 -0700369 foreach_syllable (buffer, start, end)
370 buffer->unsafe_to_break (start, end);
Behdad Esfahbod9cd59db2015-07-22 13:27:06 +0100371 setup_rphf_mask (plan, buffer);
372 setup_topographical_masks (plan, buffer);
373}
374
375static void
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100376clear_substitution_flags (const hb_ot_shape_plan_t *plan,
377 hb_font_t *font HB_UNUSED,
378 hb_buffer_t *buffer)
379{
380 hb_glyph_info_t *info = buffer->info;
381 unsigned int count = buffer->len;
382 for (unsigned int i = 0; i < count; i++)
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000383 _hb_glyph_info_clear_substituted (&info[i]);
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100384}
385
386static void
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100387record_rphf (const hb_ot_shape_plan_t *plan,
388 hb_font_t *font,
389 hb_buffer_t *buffer)
390{
391 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100392
Behdad Esfahbodac596512015-07-22 11:54:02 +0100393 hb_mask_t mask = use_plan->rphf_mask;
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100394 if (!mask) return;
395 hb_glyph_info_t *info = buffer->info;
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100396
Behdad Esfahbodac596512015-07-22 11:54:02 +0100397 foreach_syllable (buffer, start, end)
398 {
399 /* Mark a substituted repha as USE_R. */
400 for (unsigned int i = start; i < end && (info[i].mask & mask); i++)
401 if (_hb_glyph_info_substituted (&info[i]))
402 {
403 info[i].use_category() = USE_R;
404 break;
405 }
406 }
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100407}
408
409static void
410record_pref (const hb_ot_shape_plan_t *plan,
411 hb_font_t *font,
412 hb_buffer_t *buffer)
413{
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100414 hb_glyph_info_t *info = buffer->info;
Behdad Esfahbodac596512015-07-22 11:54:02 +0100415
416 foreach_syllable (buffer, start, end)
417 {
418 /* Mark a substituted pref as VPre, as they behave the same way. */
419 for (unsigned int i = start; i < end; i++)
420 if (_hb_glyph_info_substituted (&info[i]))
421 {
422 info[i].use_category() = USE_VPre;
423 break;
424 }
425 }
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100426}
Behdad Esfahboda9663952015-07-20 14:24:55 +0100427
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000428static inline bool
429is_halant (const hb_glyph_info_t &info)
430{
431 return info.use_category() == USE_H && !_hb_glyph_info_ligated (&info);
432}
433
Behdad Esfahboda9663952015-07-20 14:24:55 +0100434static void
Behdad Esfahbodac596512015-07-22 11:54:02 +0100435reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end)
Behdad Esfahboda9663952015-07-20 14:24:55 +0100436{
437 syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100438 /* Only a few syllable types need reordering. */
Behdad Esfahbod6058f982017-10-19 11:39:52 -0700439 if (unlikely (!(FLAG_UNSAFE (syllable_type) &
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100440 (FLAG (virama_terminated_cluster) |
Behdad Esfahbod9b6312f2016-05-06 17:41:49 +0100441 FLAG (standard_cluster) |
Behdad Esfahbod40c4a992015-07-21 17:14:54 +0100442 FLAG (broken_cluster) |
443 0))))
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100444 return;
445
446 hb_glyph_info_t *info = buffer->info;
447
Behdad Esfahbod9b6312f2016-05-06 17:41:49 +0100448#define BASE_FLAGS (FLAG (USE_B) | FLAG (USE_GB))
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100449
450 /* Move things forward. */
451 if (info[start].use_category() == USE_R && end - start > 1)
452 {
453 /* Got a repha. Reorder it to after first base, before first halant. */
454 for (unsigned int i = start + 1; i < end; i++)
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000455 if ((FLAG_UNSAFE (info[i].use_category()) & (BASE_FLAGS)) || is_halant (info[i]))
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100456 {
457 /* If we hit a halant, move before it; otherwise it's a base: move to it's
458 * place, and shift things in between backward. */
459
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000460 if (is_halant (info[i]))
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100461 i--;
462
Behdad Esfahbod5b31fe32015-09-01 16:24:34 +0100463 buffer->merge_clusters (start, i + 1);
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100464 hb_glyph_info_t t = info[start];
465 memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0]));
466 info[i] = t;
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100467
468 break;
469 }
Behdad Esfahboda9663952015-07-20 14:24:55 +0100470 }
Behdad Esfahboda85c4da2015-07-21 16:07:10 +0100471
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100472 /* Move things back. */
473 unsigned int j = end;
474 for (unsigned int i = start; i < end; i++)
475 {
476 uint32_t flag = FLAG_UNSAFE (info[i].use_category());
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000477 if ((flag & (BASE_FLAGS)) || is_halant (info[i]))
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100478 {
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000479 /* If we hit a halant, move after it; otherwise it's a base: move to it's
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100480 * place, and shift things in between backward. */
Behdad Esfahbod2ab0de92015-12-17 11:59:15 +0000481 if (is_halant (info[i]))
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100482 j = i + 1;
483 else
484 j = i;
485 }
Behdad Esfahboda51a6612015-07-21 18:24:21 +0100486 else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) &&
487 /* Only move the first component of a MultipleSubst. */
488 0 == _hb_glyph_info_get_lig_comp (&info[i]) &&
489 j < i)
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100490 {
Behdad Esfahbod5b31fe32015-09-01 16:24:34 +0100491 buffer->merge_clusters (j, i + 1);
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100492 hb_glyph_info_t t = info[i];
493 memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0]));
494 info[j] = t;
Behdad Esfahbod7ce03eb2015-07-21 16:55:26 +0100495 }
496 }
Behdad Esfahboda9663952015-07-20 14:24:55 +0100497}
498
499static inline void
500insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
501 hb_font_t *font,
502 hb_buffer_t *buffer)
503{
Behdad Esfahboda9663952015-07-20 14:24:55 +0100504 /* Note: This loop is extra overhead, but should not be measurable. */
505 bool has_broken_syllables = false;
506 unsigned int count = buffer->len;
507 hb_glyph_info_t *info = buffer->info;
508 for (unsigned int i = 0; i < count; i++)
509 if ((info[i].syllable() & 0x0F) == broken_cluster)
510 {
511 has_broken_syllables = true;
512 break;
513 }
514 if (likely (!has_broken_syllables))
515 return;
516
Behdad Esfahboda9663952015-07-20 14:24:55 +0100517 hb_glyph_info_t dottedcircle = {0};
Behdad Esfahbod8b5bc142016-02-24 19:05:23 +0900518 if (!font->get_nominal_glyph (0x25CCu, &dottedcircle.codepoint))
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100519 return;
Behdad Esfahbod50780442018-02-13 21:46:28 -0800520 dottedcircle.use_category() = hb_use_get_category (0x25CC);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100521
522 buffer->clear_output ();
523
524 buffer->idx = 0;
525 unsigned int last_syllable = 0;
Behdad Esfahbod7185b272018-05-31 20:03:00 -0700526 while (buffer->idx < buffer->len && buffer->successful)
Behdad Esfahboda9663952015-07-20 14:24:55 +0100527 {
528 unsigned int syllable = buffer->cur().syllable();
529 syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
530 if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
531 {
532 last_syllable = syllable;
533
Behdad Esfahbod6f932bc2015-10-21 11:16:49 -0200534 hb_glyph_info_t ginfo = dottedcircle;
535 ginfo.cluster = buffer->cur().cluster;
536 ginfo.mask = buffer->cur().mask;
537 ginfo.syllable() = buffer->cur().syllable();
Behdad Esfahbod40c4a992015-07-21 17:14:54 +0100538 /* TODO Set glyph_props? */
539
540 /* Insert dottedcircle after possible Repha. */
Behdad Esfahbod7185b272018-05-31 20:03:00 -0700541 while (buffer->idx < buffer->len && buffer->successful &&
Behdad Esfahbod40c4a992015-07-21 17:14:54 +0100542 last_syllable == buffer->cur().syllable() &&
543 buffer->cur().use_category() == USE_R)
544 buffer->next_glyph ();
Behdad Esfahboda9663952015-07-20 14:24:55 +0100545
Behdad Esfahbod6f932bc2015-10-21 11:16:49 -0200546 buffer->output_info (ginfo);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100547 }
548 else
549 buffer->next_glyph ();
550 }
551
552 buffer->swap_buffers ();
Behdad Esfahboda9663952015-07-20 14:24:55 +0100553}
554
555static void
Behdad Esfahbod4febed62015-07-21 10:24:32 +0100556reorder (const hb_ot_shape_plan_t *plan,
557 hb_font_t *font,
558 hb_buffer_t *buffer)
Behdad Esfahboda9663952015-07-20 14:24:55 +0100559{
560 insert_dotted_circles (plan, font, buffer);
561
562 hb_glyph_info_t *info = buffer->info;
Behdad Esfahbodac596512015-07-22 11:54:02 +0100563
564 foreach_syllable (buffer, start, end)
565 reorder_syllable (buffer, start, end);
Behdad Esfahboda9663952015-07-20 14:24:55 +0100566
567 /* Zero syllables now... */
Behdad Esfahbodac596512015-07-22 11:54:02 +0100568 unsigned int count = buffer->len;
Behdad Esfahboda9663952015-07-20 14:24:55 +0100569 for (unsigned int i = 0; i < count; i++)
570 info[i].syllable() = 0;
571
572 HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
573}
574
Behdad Esfahbod6d40eb82018-10-23 02:51:42 -0700575
576static void
577preprocess_text_use (const hb_ot_shape_plan_t *plan,
578 hb_buffer_t *buffer,
579 hb_font_t *font)
580{
581 _hb_preprocess_text_vowel_constraints (plan, buffer, font);
582}
583
Behdad Esfahboda08a2782015-07-21 18:09:40 +0100584static bool
585compose_use (const hb_ot_shape_normalize_context_t *c,
586 hb_codepoint_t a,
587 hb_codepoint_t b,
588 hb_codepoint_t *ab)
589{
590 /* Avoid recomposing split matras. */
591 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
592 return false;
593
ThePhDf798b8e2015-11-21 16:57:26 -0500594 return (bool)c->unicode->compose (a, b, ab);
Behdad Esfahboda08a2782015-07-21 18:09:40 +0100595}
596
Behdad Esfahboda9663952015-07-20 14:24:55 +0100597
598const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
599{
Behdad Esfahboda9663952015-07-20 14:24:55 +0100600 collect_features_use,
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200601 nullptr, /* override_features */
Behdad Esfahbod595936e2015-07-21 14:15:35 +0100602 data_create_use,
603 data_destroy_use,
Behdad Esfahbod6d40eb82018-10-23 02:51:42 -0700604 preprocess_text_use,
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200605 nullptr, /* postprocess_glyphs */
Behdad Esfahboda9663952015-07-20 14:24:55 +0100606 HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
David Corbett62fa7cd2018-04-13 18:45:37 -0400607 nullptr, /* decompose */
Behdad Esfahboda08a2782015-07-21 18:09:40 +0100608 compose_use,
Behdad Esfahboda9663952015-07-20 14:24:55 +0100609 setup_masks_use,
Behdad Esfahbod48c513f2018-10-02 14:17:42 +0200610 HB_TAG_NONE, /* gpos_tag */
Behdad Esfahboddbdbfe32017-10-15 12:11:08 +0200611 nullptr, /* reorder_marks */
Behdad Esfahbodda41e482016-02-16 17:16:33 +0700612 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
Behdad Esfahboda9663952015-07-20 14:24:55 +0100613 false, /* fallback_position */
614};