2 * Copyright © 2015 Mozilla Foundation.
3 * Copyright © 2015 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Mozilla Author(s): Jonathan Kew
26 * Google Author(s): Behdad Esfahbod
31 #ifndef HB_NO_OT_SHAPE
33 #include "hb-ot-shape-complex-use-machine.hh"
34 #include "hb-ot-shape-complex-use-table.hh"
35 #include "hb-ot-shape-complex-arabic.hh"
36 #include "hb-ot-shape-complex-arabic-joining-list.hh"
37 #include "hb-ot-shape-complex-vowel-constraints.hh"
41 * Universal Shaping Engine.
42 * https://docs.microsoft.com/en-us/typography/script-development/use
46 use_basic_features
[] =
50 * These features are applied all at once, before reordering, constrained
53 HB_TAG('r','k','r','f'),
54 HB_TAG('a','b','v','f'),
55 HB_TAG('b','l','w','f'),
56 HB_TAG('h','a','l','f'),
57 HB_TAG('p','s','t','f'),
58 HB_TAG('v','a','t','u'),
59 HB_TAG('c','j','c','t'),
62 use_topographical_features
[] =
64 HB_TAG('i','s','o','l'),
65 HB_TAG('i','n','i','t'),
66 HB_TAG('m','e','d','i'),
67 HB_TAG('f','i','n','a'),
69 /* Same order as use_topographical_features. */
78 use_other_features
[] =
82 * These features are applied all at once, after reordering and
85 HB_TAG('a','b','v','s'),
86 HB_TAG('b','l','w','s'),
87 HB_TAG('h','a','l','n'),
88 HB_TAG('p','r','e','s'),
89 HB_TAG('p','s','t','s'),
93 setup_syllables_use (const hb_ot_shape_plan_t
*plan
,
97 record_rphf_use (const hb_ot_shape_plan_t
*plan
,
101 record_pref_use (const hb_ot_shape_plan_t
*plan
,
103 hb_buffer_t
*buffer
);
105 reorder_use (const hb_ot_shape_plan_t
*plan
,
107 hb_buffer_t
*buffer
);
110 collect_features_use (hb_ot_shape_planner_t
*plan
)
112 hb_ot_map_builder_t
*map
= &plan
->map
;
114 /* Do this before any lookups have been applied. */
115 map
->add_gsub_pause (setup_syllables_use
);
117 /* "Default glyph pre-processing group" */
118 map
->enable_feature (HB_TAG('l','o','c','l'));
119 map
->enable_feature (HB_TAG('c','c','m','p'));
120 map
->enable_feature (HB_TAG('n','u','k','t'));
121 map
->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ
);
123 /* "Reordering group" */
124 map
->add_gsub_pause (_hb_clear_substitution_flags
);
125 map
->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ
);
126 map
->add_gsub_pause (record_rphf_use
);
127 map
->add_gsub_pause (_hb_clear_substitution_flags
);
128 map
->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ
);
129 map
->add_gsub_pause (record_pref_use
);
131 /* "Orthographic unit shaping group" */
132 for (unsigned int i
= 0; i
< ARRAY_LENGTH (use_basic_features
); i
++)
133 map
->enable_feature (use_basic_features
[i
], F_MANUAL_ZWJ
);
135 map
->add_gsub_pause (reorder_use
);
136 map
->add_gsub_pause (_hb_clear_syllables
);
138 /* "Topographical features" */
139 for (unsigned int i
= 0; i
< ARRAY_LENGTH (use_topographical_features
); i
++)
140 map
->add_feature (use_topographical_features
[i
]);
141 map
->add_gsub_pause (nullptr);
143 /* "Standard typographic presentation" */
144 for (unsigned int i
= 0; i
< ARRAY_LENGTH (use_other_features
); i
++)
145 map
->enable_feature (use_other_features
[i
], F_MANUAL_ZWJ
);
148 struct use_shape_plan_t
152 arabic_shape_plan_t
*arabic_plan
;
156 data_create_use (const hb_ot_shape_plan_t
*plan
)
158 use_shape_plan_t
*use_plan
= (use_shape_plan_t
*) hb_calloc (1, sizeof (use_shape_plan_t
));
159 if (unlikely (!use_plan
))
162 use_plan
->rphf_mask
= plan
->map
.get_1_mask (HB_TAG('r','p','h','f'));
164 if (has_arabic_joining (plan
->props
.script
))
166 use_plan
->arabic_plan
= (arabic_shape_plan_t
*) data_create_arabic (plan
);
167 if (unlikely (!use_plan
->arabic_plan
))
178 data_destroy_use (void *data
)
180 use_shape_plan_t
*use_plan
= (use_shape_plan_t
*) data
;
182 if (use_plan
->arabic_plan
)
183 data_destroy_arabic (use_plan
->arabic_plan
);
189 setup_masks_use (const hb_ot_shape_plan_t
*plan
,
191 hb_font_t
*font HB_UNUSED
)
193 const use_shape_plan_t
*use_plan
= (const use_shape_plan_t
*) plan
->data
;
195 /* Do this before allocating use_category(). */
196 if (use_plan
->arabic_plan
)
198 setup_masks_arabic_plan (use_plan
->arabic_plan
, buffer
, plan
->props
.script
);
201 HB_BUFFER_ALLOCATE_VAR (buffer
, use_category
);
203 /* We cannot setup masks here. We save information about characters
204 * and setup masks later on in a pause-callback. */
206 unsigned int count
= buffer
->len
;
207 hb_glyph_info_t
*info
= buffer
->info
;
208 for (unsigned int i
= 0; i
< count
; i
++)
209 info
[i
].use_category() = hb_use_get_category (info
[i
].codepoint
);
213 setup_rphf_mask (const hb_ot_shape_plan_t
*plan
,
216 const use_shape_plan_t
*use_plan
= (const use_shape_plan_t
*) plan
->data
;
218 hb_mask_t mask
= use_plan
->rphf_mask
;
221 hb_glyph_info_t
*info
= buffer
->info
;
223 foreach_syllable (buffer
, start
, end
)
225 unsigned int limit
= info
[start
].use_category() == USE(R
) ? 1 : hb_min (3u, end
- start
);
226 for (unsigned int i
= start
; i
< start
+ limit
; i
++)
227 info
[i
].mask
|= mask
;
232 setup_topographical_masks (const hb_ot_shape_plan_t
*plan
,
235 const use_shape_plan_t
*use_plan
= (const use_shape_plan_t
*) plan
->data
;
236 if (use_plan
->arabic_plan
)
239 static_assert ((JOINING_FORM_INIT
< 4 && JOINING_FORM_ISOL
< 4 && JOINING_FORM_MEDI
< 4 && JOINING_FORM_FINA
< 4), "");
240 hb_mask_t masks
[4], all_masks
= 0;
241 for (unsigned int i
= 0; i
< 4; i
++)
243 masks
[i
] = plan
->map
.get_1_mask (use_topographical_features
[i
]);
244 if (masks
[i
] == plan
->map
.get_global_mask ())
246 all_masks
|= masks
[i
];
250 hb_mask_t other_masks
= ~all_masks
;
252 unsigned int last_start
= 0;
253 joining_form_t last_form
= _JOINING_FORM_NONE
;
254 hb_glyph_info_t
*info
= buffer
->info
;
255 foreach_syllable (buffer
, start
, end
)
257 use_syllable_type_t syllable_type
= (use_syllable_type_t
) (info
[start
].syllable() & 0x0F);
258 switch (syllable_type
)
260 case use_symbol_cluster
:
261 case use_hieroglyph_cluster
:
262 case use_non_cluster
:
263 /* These don't join. Nothing to do. */
264 last_form
= _JOINING_FORM_NONE
;
267 case use_virama_terminated_cluster
:
268 case use_sakot_terminated_cluster
:
269 case use_standard_cluster
:
270 case use_number_joiner_terminated_cluster
:
271 case use_numeral_cluster
:
272 case use_broken_cluster
:
274 bool join
= last_form
== JOINING_FORM_FINA
|| last_form
== JOINING_FORM_ISOL
;
278 /* Fixup previous syllable's form. */
279 last_form
= last_form
== JOINING_FORM_FINA
? JOINING_FORM_MEDI
: JOINING_FORM_INIT
;
280 for (unsigned int i
= last_start
; i
< start
; i
++)
281 info
[i
].mask
= (info
[i
].mask
& other_masks
) | masks
[last_form
];
284 /* Form for this syllable. */
285 last_form
= join
? JOINING_FORM_FINA
: JOINING_FORM_ISOL
;
286 for (unsigned int i
= start
; i
< end
; i
++)
287 info
[i
].mask
= (info
[i
].mask
& other_masks
) | masks
[last_form
];
297 setup_syllables_use (const hb_ot_shape_plan_t
*plan
,
298 hb_font_t
*font HB_UNUSED
,
301 find_syllables_use (buffer
);
302 foreach_syllable (buffer
, start
, end
)
303 buffer
->unsafe_to_break (start
, end
);
304 setup_rphf_mask (plan
, buffer
);
305 setup_topographical_masks (plan
, buffer
);
309 record_rphf_use (const hb_ot_shape_plan_t
*plan
,
310 hb_font_t
*font HB_UNUSED
,
313 const use_shape_plan_t
*use_plan
= (const use_shape_plan_t
*) plan
->data
;
315 hb_mask_t mask
= use_plan
->rphf_mask
;
317 hb_glyph_info_t
*info
= buffer
->info
;
319 foreach_syllable (buffer
, start
, end
)
321 /* Mark a substituted repha as USE(R). */
322 for (unsigned int i
= start
; i
< end
&& (info
[i
].mask
& mask
); i
++)
323 if (_hb_glyph_info_substituted (&info
[i
]))
325 info
[i
].use_category() = USE(R
);
332 record_pref_use (const hb_ot_shape_plan_t
*plan HB_UNUSED
,
333 hb_font_t
*font HB_UNUSED
,
336 hb_glyph_info_t
*info
= buffer
->info
;
338 foreach_syllable (buffer
, start
, end
)
340 /* Mark a substituted pref as VPre, as they behave the same way. */
341 for (unsigned int i
= start
; i
< end
; i
++)
342 if (_hb_glyph_info_substituted (&info
[i
]))
344 info
[i
].use_category() = USE(VPre
);
351 is_halant_use (const hb_glyph_info_t
&info
)
353 return (info
.use_category() == USE(H
) || info
.use_category() == USE(HVM
)) &&
354 !_hb_glyph_info_ligated (&info
);
358 reorder_syllable_use (hb_buffer_t
*buffer
, unsigned int start
, unsigned int end
)
360 use_syllable_type_t syllable_type
= (use_syllable_type_t
) (buffer
->info
[start
].syllable() & 0x0F);
361 /* Only a few syllable types need reordering. */
362 if (unlikely (!(FLAG_UNSAFE (syllable_type
) &
363 (FLAG (use_virama_terminated_cluster
) |
364 FLAG (use_sakot_terminated_cluster
) |
365 FLAG (use_standard_cluster
) |
366 FLAG (use_broken_cluster
) |
370 hb_glyph_info_t
*info
= buffer
->info
;
372 #define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \
373 FLAG64 (USE(FBlw)) | \
374 FLAG64 (USE(FPst)) | \
375 FLAG64 (USE(MAbv)) | \
376 FLAG64 (USE(MBlw)) | \
377 FLAG64 (USE(MPst)) | \
378 FLAG64 (USE(MPre)) | \
379 FLAG64 (USE(VAbv)) | \
380 FLAG64 (USE(VBlw)) | \
381 FLAG64 (USE(VPst)) | \
382 FLAG64 (USE(VPre)) | \
383 FLAG64 (USE(VMAbv)) | \
384 FLAG64 (USE(VMBlw)) | \
385 FLAG64 (USE(VMPst)) | \
388 /* Move things forward. */
389 if (info
[start
].use_category() == USE(R
) && end
- start
> 1)
391 /* Got a repha. Reorder it towards the end, but before the first post-base
393 for (unsigned int i
= start
+ 1; i
< end
; i
++)
395 bool is_post_base_glyph
= (FLAG64_UNSAFE (info
[i
].use_category()) & POST_BASE_FLAGS64
) ||
396 is_halant_use (info
[i
]);
397 if (is_post_base_glyph
|| i
== end
- 1)
399 /* If we hit a post-base glyph, move before it; otherwise move to the
400 * end. Shift things in between backward. */
402 if (is_post_base_glyph
)
405 buffer
->merge_clusters (start
, i
+ 1);
406 hb_glyph_info_t t
= info
[start
];
407 memmove (&info
[start
], &info
[start
+ 1], (i
- start
) * sizeof (info
[0]));
415 /* Move things back. */
416 unsigned int j
= start
;
417 for (unsigned int i
= start
; i
< end
; i
++)
419 uint32_t flag
= FLAG_UNSAFE (info
[i
].use_category());
420 if (is_halant_use (info
[i
]))
422 /* If we hit a halant, move after it; otherwise move to the beginning, and
423 * shift things in between forward. */
426 else if (((flag
) & (FLAG (USE(VPre
)) | FLAG (USE(VMPre
)))) &&
427 /* Only move the first component of a MultipleSubst. */
428 0 == _hb_glyph_info_get_lig_comp (&info
[i
]) &&
431 buffer
->merge_clusters (j
, i
+ 1);
432 hb_glyph_info_t t
= info
[i
];
433 memmove (&info
[j
+ 1], &info
[j
], (i
- j
) * sizeof (info
[0]));
440 reorder_use (const hb_ot_shape_plan_t
*plan
,
444 if (buffer
->message (font
, "start reordering USE"))
446 hb_syllabic_insert_dotted_circles (font
, buffer
,
451 foreach_syllable (buffer
, start
, end
)
452 reorder_syllable_use (buffer
, start
, end
);
454 (void) buffer
->message (font
, "end reordering USE");
457 HB_BUFFER_DEALLOCATE_VAR (buffer
, use_category
);
462 preprocess_text_use (const hb_ot_shape_plan_t
*plan
,
466 _hb_preprocess_text_vowel_constraints (plan
, buffer
, font
);
470 compose_use (const hb_ot_shape_normalize_context_t
*c
,
475 /* Avoid recomposing split matras. */
476 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c
->unicode
->general_category (a
)))
479 return (bool)c
->unicode
->compose (a
, b
, ab
);
483 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use
=
485 collect_features_use
,
486 nullptr, /* override_features */
490 nullptr, /* postprocess_glyphs */
491 HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT
,
492 nullptr, /* decompose */
495 HB_TAG_NONE
, /* gpos_tag */
496 nullptr, /* reorder_marks */
497 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY
,
498 false, /* fallback_position */