2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2011,2012 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 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
31 #ifndef HB_NO_OT_SHAPE
33 #ifdef HB_NO_OT_LAYOUT
34 #error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT."
37 #include "hb-shaper-impl.hh"
39 #include "hb-ot-shape.hh"
40 #include "hb-ot-shaper.hh"
41 #include "hb-ot-shape-fallback.hh"
42 #include "hb-ot-shape-normalize.hh"
44 #include "hb-ot-face.hh"
48 #include "hb-aat-layout.hh"
51 _hb_codepoint_is_regional_indicator (hb_codepoint_t u
)
52 { return hb_in_range
<hb_codepoint_t
> (u
, 0x1F1E6u
, 0x1F1FFu
); }
54 #ifndef HB_NO_AAT_SHAPE
56 _hb_apply_morx (hb_face_t
*face
, const hb_segment_properties_t
&props
)
58 /* https://github.com/harfbuzz/harfbuzz/issues/2124 */
59 return hb_aat_layout_has_substitution (face
) &&
60 (HB_DIRECTION_IS_HORIZONTAL (props
.direction
) || !hb_ot_layout_has_substitution (face
));
67 * @short_description: OpenType shaping support
70 * Support functions for OpenType shaping related queries.
75 hb_ot_shape_collect_features (hb_ot_shape_planner_t
*planner
,
76 const hb_feature_t
*user_features
,
77 unsigned int num_user_features
);
79 hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t
*face
,
80 const hb_segment_properties_t
&props
) :
84 #ifndef HB_NO_AAT_SHAPE
85 , apply_morx (_hb_apply_morx (face
, props
))
88 shaper
= hb_ot_shaper_categorize (this);
90 script_zero_marks
= shaper
->zero_width_marks
!= HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE
;
91 script_fallback_mark_positioning
= shaper
->fallback_position
;
93 #ifndef HB_NO_AAT_SHAPE
94 /* https://github.com/harfbuzz/harfbuzz/issues/1528 */
95 if (apply_morx
&& shaper
!= &_hb_ot_shaper_default
)
96 shaper
= &_hb_ot_shaper_dumber
;
101 hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t
&plan
,
102 const hb_ot_shape_plan_key_t
&key
)
105 plan
.shaper
= shaper
;
106 map
.compile (plan
.map
, key
);
108 #ifndef HB_NO_OT_SHAPE_FRACTIONS
109 plan
.frac_mask
= plan
.map
.get_1_mask (HB_TAG ('f','r','a','c'));
110 plan
.numr_mask
= plan
.map
.get_1_mask (HB_TAG ('n','u','m','r'));
111 plan
.dnom_mask
= plan
.map
.get_1_mask (HB_TAG ('d','n','o','m'));
112 plan
.has_frac
= plan
.frac_mask
|| (plan
.numr_mask
&& plan
.dnom_mask
);
115 plan
.rtlm_mask
= plan
.map
.get_1_mask (HB_TAG ('r','t','l','m'));
116 plan
.has_vert
= !!plan
.map
.get_1_mask (HB_TAG ('v','e','r','t'));
118 hb_tag_t kern_tag
= HB_DIRECTION_IS_HORIZONTAL (props
.direction
) ?
119 HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n');
120 #ifndef HB_NO_OT_KERN
121 plan
.kern_mask
= plan
.map
.get_mask (kern_tag
);
122 plan
.requested_kerning
= !!plan
.kern_mask
;
124 #ifndef HB_NO_AAT_SHAPE
125 plan
.trak_mask
= plan
.map
.get_mask (HB_TAG ('t','r','a','k'));
126 plan
.requested_tracking
= !!plan
.trak_mask
;
129 bool has_gpos_kern
= plan
.map
.get_feature_index (1, kern_tag
) != HB_OT_LAYOUT_NO_FEATURE_INDEX
;
130 bool disable_gpos
= plan
.shaper
->gpos_tag
&&
131 plan
.shaper
->gpos_tag
!= plan
.map
.chosen_script
[1];
134 * Decide who provides glyph classes. GDEF or Unicode.
137 if (!hb_ot_layout_has_glyph_classes (face
))
138 plan
.fallback_glyph_classes
= true;
141 * Decide who does substitutions. GSUB, morx, or fallback.
144 #ifndef HB_NO_AAT_SHAPE
145 plan
.apply_morx
= apply_morx
;
149 * Decide who does positioning. GPOS, kerx, kern, or fallback.
152 #ifndef HB_NO_AAT_SHAPE
153 bool has_kerx
= hb_aat_layout_has_positioning (face
);
154 bool has_gsub
= !apply_morx
&& hb_ot_layout_has_substitution (face
);
156 bool has_gpos
= !disable_gpos
&& hb_ot_layout_has_positioning (face
);
159 #ifndef HB_NO_AAT_SHAPE
160 /* Prefer GPOS over kerx if GSUB is present;
161 * https://github.com/harfbuzz/harfbuzz/issues/3008 */
162 else if (has_kerx
&& !(has_gsub
&& has_gpos
))
163 plan
.apply_kerx
= true;
166 plan
.apply_gpos
= true;
168 if (!plan
.apply_kerx
&& (!has_gpos_kern
|| !plan
.apply_gpos
))
170 #ifndef HB_NO_AAT_SHAPE
172 plan
.apply_kerx
= true;
175 #ifndef HB_NO_OT_KERN
176 if (hb_ot_layout_has_kerning (face
))
177 plan
.apply_kern
= true;
181 plan
.apply_fallback_kern
= !(plan
.apply_gpos
|| plan
.apply_kerx
|| plan
.apply_kern
);
183 plan
.zero_marks
= script_zero_marks
&&
186 #ifndef HB_NO_OT_KERN
187 || !hb_ot_layout_has_machine_kerning (face
)
190 plan
.has_gpos_mark
= !!plan
.map
.get_1_mask (HB_TAG ('m','a','r','k'));
192 plan
.adjust_mark_positioning_when_zeroing
= !plan
.apply_gpos
&&
195 #ifndef HB_NO_OT_KERN
196 || !hb_ot_layout_has_cross_kerning (face
)
200 plan
.fallback_mark_positioning
= plan
.adjust_mark_positioning_when_zeroing
&&
201 script_fallback_mark_positioning
;
203 #ifndef HB_NO_AAT_SHAPE
204 /* If we're using morx shaping, we cancel mark position adjustment because
205 Apple Color Emoji assumes this will NOT be done when forming emoji sequences;
206 https://github.com/harfbuzz/harfbuzz/issues/2967. */
208 plan
.adjust_mark_positioning_when_zeroing
= false;
210 /* Currently we always apply trak. */
211 plan
.apply_trak
= plan
.requested_tracking
&& hb_aat_layout_has_tracking (face
);
216 hb_ot_shape_plan_t::init0 (hb_face_t
*face
,
217 const hb_shape_plan_key_t
*key
)
221 hb_ot_shape_planner_t
planner (face
,
224 hb_ot_shape_collect_features (&planner
,
226 key
->num_user_features
);
228 planner
.compile (*this, key
->ot
);
230 if (shaper
->data_create
)
232 data
= shaper
->data_create (this);
233 if (unlikely (!data
))
244 hb_ot_shape_plan_t::fini ()
246 if (shaper
->data_destroy
)
247 shaper
->data_destroy (const_cast<void *> (data
));
253 hb_ot_shape_plan_t::substitute (hb_font_t
*font
,
254 hb_buffer_t
*buffer
) const
256 map
.substitute (this, font
, buffer
);
260 hb_ot_shape_plan_t::position (hb_font_t
*font
,
261 hb_buffer_t
*buffer
) const
263 if (this->apply_gpos
)
264 map
.position (this, font
, buffer
);
265 #ifndef HB_NO_AAT_SHAPE
266 else if (this->apply_kerx
)
267 hb_aat_layout_position (this, font
, buffer
);
270 #ifndef HB_NO_OT_KERN
271 if (this->apply_kern
)
272 hb_ot_layout_kern (this, font
, buffer
);
274 else if (this->apply_fallback_kern
)
275 _hb_ot_shape_fallback_kern (this, font
, buffer
);
277 #ifndef HB_NO_AAT_SHAPE
278 if (this->apply_trak
)
279 hb_aat_layout_track (this, font
, buffer
);
284 static const hb_ot_map_feature_t
287 {HB_TAG('a','b','v','m'), F_GLOBAL
},
288 {HB_TAG('b','l','w','m'), F_GLOBAL
},
289 {HB_TAG('c','c','m','p'), F_GLOBAL
},
290 {HB_TAG('l','o','c','l'), F_GLOBAL
},
291 {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS
},
292 {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS
},
293 {HB_TAG('r','l','i','g'), F_GLOBAL
},
297 static const hb_ot_map_feature_t
298 horizontal_features
[] =
300 {HB_TAG('c','a','l','t'), F_GLOBAL
},
301 {HB_TAG('c','l','i','g'), F_GLOBAL
},
302 {HB_TAG('c','u','r','s'), F_GLOBAL
},
303 {HB_TAG('d','i','s','t'), F_GLOBAL
},
304 {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK
},
305 {HB_TAG('l','i','g','a'), F_GLOBAL
},
306 {HB_TAG('r','c','l','t'), F_GLOBAL
},
310 hb_ot_shape_collect_features (hb_ot_shape_planner_t
*planner
,
311 const hb_feature_t
*user_features
,
312 unsigned int num_user_features
)
314 hb_ot_map_builder_t
*map
= &planner
->map
;
316 map
->is_simple
= true;
318 map
->enable_feature (HB_TAG('r','v','r','n'));
319 map
->add_gsub_pause (nullptr);
321 switch (planner
->props
.direction
)
323 case HB_DIRECTION_LTR
:
324 map
->enable_feature (HB_TAG ('l','t','r','a'));
325 map
->enable_feature (HB_TAG ('l','t','r','m'));
327 case HB_DIRECTION_RTL
:
328 map
->enable_feature (HB_TAG ('r','t','l','a'));
329 map
->add_feature (HB_TAG ('r','t','l','m'));
331 case HB_DIRECTION_TTB
:
332 case HB_DIRECTION_BTT
:
333 case HB_DIRECTION_INVALID
:
338 #ifndef HB_NO_OT_SHAPE_FRACTIONS
339 /* Automatic fractions. */
340 map
->add_feature (HB_TAG ('f','r','a','c'));
341 map
->add_feature (HB_TAG ('n','u','m','r'));
342 map
->add_feature (HB_TAG ('d','n','o','m'));
346 map
->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM
, HB_OT_MAP_MAX_VALUE
);
348 #ifndef HB_NO_AAT_SHAPE
349 /* Tracking. We enable dummy feature here just to allow disabling
350 * AAT 'trak' table using features.
351 * https://github.com/harfbuzz/harfbuzz/issues/1303 */
352 map
->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK
);
355 map
->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
356 map
->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
358 if (planner
->shaper
->collect_features
)
360 map
->is_simple
= false;
361 planner
->shaper
->collect_features (planner
);
364 map
->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */
365 map
->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */
367 for (unsigned int i
= 0; i
< ARRAY_LENGTH (common_features
); i
++)
368 map
->add_feature (common_features
[i
]);
370 if (HB_DIRECTION_IS_HORIZONTAL (planner
->props
.direction
))
371 for (unsigned int i
= 0; i
< ARRAY_LENGTH (horizontal_features
); i
++)
372 map
->add_feature (horizontal_features
[i
]);
375 /* We only apply `vert` feature. See:
376 * https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528
377 * https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html */
379 /* We really want to find a 'vert' feature if there's any in the font, no
380 * matter which script/langsys it is listed (or not) under.
381 * See various bugs referenced from:
382 * https://github.com/harfbuzz/harfbuzz/issues/63 */
383 map
->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH
);
386 if (num_user_features
)
387 map
->is_simple
= false;
388 for (unsigned int i
= 0; i
< num_user_features
; i
++)
390 const hb_feature_t
*feature
= &user_features
[i
];
391 map
->add_feature (feature
->tag
,
392 (feature
->start
== HB_FEATURE_GLOBAL_START
&&
393 feature
->end
== HB_FEATURE_GLOBAL_END
) ? F_GLOBAL
: F_NONE
,
397 if (planner
->shaper
->override_features
)
398 planner
->shaper
->override_features (planner
);
406 struct hb_ot_face_data_t
{};
409 _hb_ot_shaper_face_data_create (hb_face_t
*face
)
411 return (hb_ot_face_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
415 _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t
*data
)
424 struct hb_ot_font_data_t
{};
427 _hb_ot_shaper_font_data_create (hb_font_t
*font HB_UNUSED
)
429 return (hb_ot_font_data_t
*) HB_SHAPER_DATA_SUCCEEDED
;
433 _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t
*data HB_UNUSED
)
442 struct hb_ot_shape_context_t
444 hb_ot_shape_plan_t
*plan
;
448 const hb_feature_t
*user_features
;
449 unsigned int num_user_features
;
451 /* Transient stuff */
452 hb_direction_t target_direction
;
463 hb_set_unicode_props (hb_buffer_t
*buffer
)
465 /* Implement enough of Unicode Graphemes here that shaping
466 * in reverse-direction wouldn't break graphemes. Namely,
467 * we mark all marks and ZWJ and ZWJ,Extended_Pictographic
468 * sequences as continuations. The foreach_grapheme()
469 * macro uses this bit.
471 * https://www.unicode.org/reports/tr29/#Regex_Definitions
473 unsigned int count
= buffer
->len
;
474 hb_glyph_info_t
*info
= buffer
->info
;
475 for (unsigned int i
= 0; i
< count
; i
++)
477 _hb_glyph_info_set_unicode_props (&info
[i
], buffer
);
479 unsigned gen_cat
= _hb_glyph_info_get_general_category (&info
[i
]);
480 if (FLAG_UNSAFE (gen_cat
) &
481 (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER
) |
482 FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER
) |
483 FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER
) |
484 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER
) |
485 FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR
)))
488 /* Marks are already set as continuation by the above line.
489 * Handle Emoji_Modifier and ZWJ-continuation. */
490 if (unlikely (gen_cat
== HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL
&&
491 hb_in_range
<hb_codepoint_t
> (info
[i
].codepoint
, 0x1F3FBu
, 0x1F3FFu
)))
493 _hb_glyph_info_set_continuation (&info
[i
]);
495 /* Regional_Indicators are hairy as hell...
496 * https://github.com/harfbuzz/harfbuzz/issues/2265 */
497 else if (unlikely (i
&& _hb_codepoint_is_regional_indicator (info
[i
].codepoint
)))
499 if (_hb_codepoint_is_regional_indicator (info
[i
- 1].codepoint
) &&
500 !_hb_glyph_info_is_continuation (&info
[i
- 1]))
501 _hb_glyph_info_set_continuation (&info
[i
]);
503 #ifndef HB_NO_EMOJI_SEQUENCES
504 else if (unlikely (_hb_glyph_info_is_zwj (&info
[i
])))
506 _hb_glyph_info_set_continuation (&info
[i
]);
508 _hb_unicode_is_emoji_Extended_Pictographic (info
[i
+ 1].codepoint
))
511 _hb_glyph_info_set_unicode_props (&info
[i
], buffer
);
512 _hb_glyph_info_set_continuation (&info
[i
]);
516 /* Or part of the Other_Grapheme_Extend that is not marks.
517 * As of Unicode 15 that is just:
519 * 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
520 * FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
521 * E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
523 * ZWNJ is special, we don't want to merge it as there's no need, and keeping
524 * it separate results in more granular clusters.
525 * Tags are used for Emoji sub-region flag sequences:
526 * https://github.com/harfbuzz/harfbuzz/issues/1556
527 * Katakana ones were requested:
528 * https://github.com/harfbuzz/harfbuzz/issues/3844
530 else if (unlikely (hb_in_ranges
<hb_codepoint_t
> (info
[i
].codepoint
, 0xFF9Eu
, 0xFF9Fu
, 0xE0020u
, 0xE007Fu
)))
531 _hb_glyph_info_set_continuation (&info
[i
]);
536 hb_insert_dotted_circle (hb_buffer_t
*buffer
, hb_font_t
*font
)
538 if (unlikely (buffer
->flags
& HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE
))
541 if (!(buffer
->flags
& HB_BUFFER_FLAG_BOT
) ||
542 buffer
->context_len
[0] ||
543 !_hb_glyph_info_is_unicode_mark (&buffer
->info
[0]))
546 if (!font
->has_glyph (0x25CCu
))
549 hb_glyph_info_t dottedcircle
= {0};
550 dottedcircle
.codepoint
= 0x25CCu
;
551 _hb_glyph_info_set_unicode_props (&dottedcircle
, buffer
);
553 buffer
->clear_output ();
556 hb_glyph_info_t info
= dottedcircle
;
557 info
.cluster
= buffer
->cur().cluster
;
558 info
.mask
= buffer
->cur().mask
;
559 (void) buffer
->output_info (info
);
565 hb_form_clusters (hb_buffer_t
*buffer
)
567 if (!(buffer
->scratch_flags
& HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII
))
570 if (buffer
->cluster_level
== HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES
)
571 foreach_grapheme (buffer
, start
, end
)
572 buffer
->merge_clusters (start
, end
);
574 foreach_grapheme (buffer
, start
, end
)
575 buffer
->unsafe_to_break (start
, end
);
579 hb_ensure_native_direction (hb_buffer_t
*buffer
)
581 hb_direction_t direction
= buffer
->props
.direction
;
582 hb_direction_t horiz_dir
= hb_script_get_horizontal_direction (buffer
->props
.script
);
584 /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset
585 * the horiz_dir if the run contains at least one decimal-number char, and no
586 * letter chars (ideally we should be checking for chars with strong
587 * directionality but hb-unicode currently lacks bidi categories).
589 * This allows digit sequences in Arabic etc to be shaped in "native"
590 * direction, so that features like ligatures will work as intended.
592 * https://github.com/harfbuzz/harfbuzz/issues/501
594 * Similar thing about Regional_Indicators; They are bidi=L, but Script=Common.
595 * If they are present in a run of natively-RTL text, they get assigned a script
596 * with natively RTL direction, which would result in wrong shaping if we
597 * assign such native RTL direction to them then. Detect that as well.
599 * https://github.com/harfbuzz/harfbuzz/issues/3314
601 if (unlikely (horiz_dir
== HB_DIRECTION_RTL
&& direction
== HB_DIRECTION_LTR
))
603 bool found_number
= false, found_letter
= false, found_ri
= false;
604 const auto* info
= buffer
->info
;
605 const auto count
= buffer
->len
;
606 for (unsigned i
= 0; i
< count
; i
++)
608 auto gc
= _hb_glyph_info_get_general_category (&info
[i
]);
609 if (gc
== HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER
)
611 else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc
))
616 else if (_hb_codepoint_is_regional_indicator (info
[i
].codepoint
))
619 if ((found_number
|| found_ri
) && !found_letter
)
620 horiz_dir
= HB_DIRECTION_LTR
;
624 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
625 * Ogham fonts are supposed to be implemented BTT or not. Need to research that
627 if ((HB_DIRECTION_IS_HORIZONTAL (direction
) &&
628 direction
!= horiz_dir
&& horiz_dir
!= HB_DIRECTION_INVALID
) ||
629 (HB_DIRECTION_IS_VERTICAL (direction
) &&
630 direction
!= HB_DIRECTION_TTB
))
632 _hb_ot_layout_reverse_graphemes (buffer
);
633 buffer
->props
.direction
= HB_DIRECTION_REVERSE (buffer
->props
.direction
);
642 #ifndef HB_NO_VERTICAL
643 static hb_codepoint_t
644 hb_vert_char_for (hb_codepoint_t u
)
648 case 0x20: switch (u
) {
649 case 0x2013u
: return 0xfe32u
; // EN DASH
650 case 0x2014u
: return 0xfe31u
; // EM DASH
651 case 0x2025u
: return 0xfe30u
; // TWO DOT LEADER
652 case 0x2026u
: return 0xfe19u
; // HORIZONTAL ELLIPSIS
654 case 0x30: switch (u
) {
655 case 0x3001u
: return 0xfe11u
; // IDEOGRAPHIC COMMA
656 case 0x3002u
: return 0xfe12u
; // IDEOGRAPHIC FULL STOP
657 case 0x3008u
: return 0xfe3fu
; // LEFT ANGLE BRACKET
658 case 0x3009u
: return 0xfe40u
; // RIGHT ANGLE BRACKET
659 case 0x300au
: return 0xfe3du
; // LEFT DOUBLE ANGLE BRACKET
660 case 0x300bu
: return 0xfe3eu
; // RIGHT DOUBLE ANGLE BRACKET
661 case 0x300cu
: return 0xfe41u
; // LEFT CORNER BRACKET
662 case 0x300du
: return 0xfe42u
; // RIGHT CORNER BRACKET
663 case 0x300eu
: return 0xfe43u
; // LEFT WHITE CORNER BRACKET
664 case 0x300fu
: return 0xfe44u
; // RIGHT WHITE CORNER BRACKET
665 case 0x3010u
: return 0xfe3bu
; // LEFT BLACK LENTICULAR BRACKET
666 case 0x3011u
: return 0xfe3cu
; // RIGHT BLACK LENTICULAR BRACKET
667 case 0x3014u
: return 0xfe39u
; // LEFT TORTOISE SHELL BRACKET
668 case 0x3015u
: return 0xfe3au
; // RIGHT TORTOISE SHELL BRACKET
669 case 0x3016u
: return 0xfe17u
; // LEFT WHITE LENTICULAR BRACKET
670 case 0x3017u
: return 0xfe18u
; // RIGHT WHITE LENTICULAR BRACKET
672 case 0xfe: switch (u
) {
673 case 0xfe4fu
: return 0xfe34u
; // WAVY LOW LINE
675 case 0xff: switch (u
) {
676 case 0xff01u
: return 0xfe15u
; // FULLWIDTH EXCLAMATION MARK
677 case 0xff08u
: return 0xfe35u
; // FULLWIDTH LEFT PARENTHESIS
678 case 0xff09u
: return 0xfe36u
; // FULLWIDTH RIGHT PARENTHESIS
679 case 0xff0cu
: return 0xfe10u
; // FULLWIDTH COMMA
680 case 0xff1au
: return 0xfe13u
; // FULLWIDTH COLON
681 case 0xff1bu
: return 0xfe14u
; // FULLWIDTH SEMICOLON
682 case 0xff1fu
: return 0xfe16u
; // FULLWIDTH QUESTION MARK
683 case 0xff3bu
: return 0xfe47u
; // FULLWIDTH LEFT SQUARE BRACKET
684 case 0xff3du
: return 0xfe48u
; // FULLWIDTH RIGHT SQUARE BRACKET
685 case 0xff3fu
: return 0xfe33u
; // FULLWIDTH LOW LINE
686 case 0xff5bu
: return 0xfe37u
; // FULLWIDTH LEFT CURLY BRACKET
687 case 0xff5du
: return 0xfe38u
; // FULLWIDTH RIGHT CURLY BRACKET
696 hb_ot_rotate_chars (const hb_ot_shape_context_t
*c
)
698 hb_buffer_t
*buffer
= c
->buffer
;
699 unsigned int count
= buffer
->len
;
700 hb_glyph_info_t
*info
= buffer
->info
;
702 if (HB_DIRECTION_IS_BACKWARD (c
->target_direction
))
704 hb_unicode_funcs_t
*unicode
= buffer
->unicode
;
705 hb_mask_t rtlm_mask
= c
->plan
->rtlm_mask
;
707 for (unsigned int i
= 0; i
< count
; i
++) {
708 hb_codepoint_t codepoint
= unicode
->mirroring (info
[i
].codepoint
);
709 if (unlikely (codepoint
!= info
[i
].codepoint
&& c
->font
->has_glyph (codepoint
)))
710 info
[i
].codepoint
= codepoint
;
712 info
[i
].mask
|= rtlm_mask
;
716 #ifndef HB_NO_VERTICAL
717 if (HB_DIRECTION_IS_VERTICAL (c
->target_direction
) && !c
->plan
->has_vert
)
719 for (unsigned int i
= 0; i
< count
; i
++) {
720 hb_codepoint_t codepoint
= hb_vert_char_for (info
[i
].codepoint
);
721 if (unlikely (codepoint
!= info
[i
].codepoint
&& c
->font
->has_glyph (codepoint
)))
722 info
[i
].codepoint
= codepoint
;
729 hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t
*c
)
731 #ifdef HB_NO_OT_SHAPE_FRACTIONS
735 if (!(c
->buffer
->scratch_flags
& HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII
) ||
739 hb_buffer_t
*buffer
= c
->buffer
;
741 hb_mask_t pre_mask
, post_mask
;
742 if (HB_DIRECTION_IS_FORWARD (buffer
->props
.direction
))
744 pre_mask
= c
->plan
->numr_mask
| c
->plan
->frac_mask
;
745 post_mask
= c
->plan
->frac_mask
| c
->plan
->dnom_mask
;
749 pre_mask
= c
->plan
->frac_mask
| c
->plan
->dnom_mask
;
750 post_mask
= c
->plan
->numr_mask
| c
->plan
->frac_mask
;
753 unsigned int count
= buffer
->len
;
754 hb_glyph_info_t
*info
= buffer
->info
;
755 for (unsigned int i
= 0; i
< count
; i
++)
757 if (info
[i
].codepoint
== 0x2044u
) /* FRACTION SLASH */
759 unsigned int start
= i
, end
= i
+ 1;
761 _hb_glyph_info_get_general_category (&info
[start
- 1]) ==
762 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER
)
764 while (end
< count
&&
765 _hb_glyph_info_get_general_category (&info
[end
]) ==
766 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER
)
768 if (start
== i
|| end
== i
+ 1)
771 buffer
->unsafe_to_concat (start
, start
+ 1);
773 buffer
->unsafe_to_concat (end
- 1, end
);
777 buffer
->unsafe_to_break (start
, end
);
779 for (unsigned int j
= start
; j
< i
; j
++)
780 info
[j
].mask
|= pre_mask
;
781 info
[i
].mask
|= c
->plan
->frac_mask
;
782 for (unsigned int j
= i
+ 1; j
< end
; j
++)
783 info
[j
].mask
|= post_mask
;
791 hb_ot_shape_initialize_masks (const hb_ot_shape_context_t
*c
)
793 hb_ot_map_t
*map
= &c
->plan
->map
;
794 hb_buffer_t
*buffer
= c
->buffer
;
796 hb_mask_t global_mask
= map
->get_global_mask ();
797 buffer
->reset_masks (global_mask
);
801 hb_ot_shape_setup_masks (const hb_ot_shape_context_t
*c
)
803 hb_ot_map_t
*map
= &c
->plan
->map
;
804 hb_buffer_t
*buffer
= c
->buffer
;
806 hb_ot_shape_setup_masks_fraction (c
);
808 if (c
->plan
->shaper
->setup_masks
)
809 c
->plan
->shaper
->setup_masks (c
->plan
, buffer
, c
->font
);
811 for (unsigned int i
= 0; i
< c
->num_user_features
; i
++)
813 const hb_feature_t
*feature
= &c
->user_features
[i
];
814 if (!(feature
->start
== HB_FEATURE_GLOBAL_START
&& feature
->end
== HB_FEATURE_GLOBAL_END
)) {
816 hb_mask_t mask
= map
->get_mask (feature
->tag
, &shift
);
817 buffer
->set_masks (feature
->value
<< shift
, mask
, feature
->start
, feature
->end
);
823 hb_ot_zero_width_default_ignorables (const hb_buffer_t
*buffer
)
825 if (!(buffer
->scratch_flags
& HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES
) ||
826 (buffer
->flags
& HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES
) ||
827 (buffer
->flags
& HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES
))
830 unsigned int count
= buffer
->len
;
831 hb_glyph_info_t
*info
= buffer
->info
;
832 hb_glyph_position_t
*pos
= buffer
->pos
;
834 for (i
= 0; i
< count
; i
++)
835 if (unlikely (_hb_glyph_info_is_default_ignorable (&info
[i
])))
836 pos
[i
].x_advance
= pos
[i
].y_advance
= pos
[i
].x_offset
= pos
[i
].y_offset
= 0;
840 hb_ot_hide_default_ignorables (hb_buffer_t
*buffer
,
843 if (!(buffer
->scratch_flags
& HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES
) ||
844 (buffer
->flags
& HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES
))
847 unsigned int count
= buffer
->len
;
848 hb_glyph_info_t
*info
= buffer
->info
;
850 hb_codepoint_t invisible
= buffer
->invisible
;
851 if (!(buffer
->flags
& HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES
) &&
852 (invisible
|| font
->get_nominal_glyph (' ', &invisible
)))
854 /* Replace default-ignorables with a zero-advance invisible glyph. */
855 for (unsigned int i
= 0; i
< count
; i
++)
857 if (_hb_glyph_info_is_default_ignorable (&info
[i
]))
858 info
[i
].codepoint
= invisible
;
862 buffer
->delete_glyphs_inplace (_hb_glyph_info_is_default_ignorable
);
867 hb_ot_map_glyphs_fast (hb_buffer_t
*buffer
)
869 /* Normalization process sets up glyph_index(), we just copy it. */
870 unsigned int count
= buffer
->len
;
871 hb_glyph_info_t
*info
= buffer
->info
;
872 for (unsigned int i
= 0; i
< count
; i
++)
873 info
[i
].codepoint
= info
[i
].glyph_index();
875 buffer
->content_type
= HB_BUFFER_CONTENT_TYPE_GLYPHS
;
879 hb_synthesize_glyph_classes (hb_buffer_t
*buffer
)
881 unsigned int count
= buffer
->len
;
882 hb_glyph_info_t
*info
= buffer
->info
;
883 for (unsigned int i
= 0; i
< count
; i
++)
885 hb_ot_layout_glyph_props_flags_t klass
;
887 /* Never mark default-ignorables as marks.
888 * They won't get in the way of lookups anyway,
889 * but having them as mark will cause them to be skipped
890 * over if the lookup-flag says so, but at least for the
891 * Mongolian variation selectors, looks like Uniscribe
892 * marks them as non-mark. Some Mongolian fonts without
893 * GDEF rely on this. Another notable character that
894 * this applies to is COMBINING GRAPHEME JOINER. */
895 klass
= (_hb_glyph_info_get_general_category (&info
[i
]) !=
896 HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK
||
897 _hb_glyph_info_is_default_ignorable (&info
[i
])) ?
898 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH
:
899 HB_OT_LAYOUT_GLYPH_PROPS_MARK
;
900 _hb_glyph_info_set_glyph_props (&info
[i
], klass
);
905 hb_ot_substitute_default (const hb_ot_shape_context_t
*c
)
907 hb_buffer_t
*buffer
= c
->buffer
;
909 hb_ot_rotate_chars (c
);
911 HB_BUFFER_ALLOCATE_VAR (buffer
, glyph_index
);
913 _hb_ot_shape_normalize (c
->plan
, buffer
, c
->font
);
915 hb_ot_shape_setup_masks (c
);
917 /* This is unfortunate to go here, but necessary... */
918 if (c
->plan
->fallback_mark_positioning
)
919 _hb_ot_shape_fallback_mark_position_recategorize_marks (c
->plan
, c
->font
, buffer
);
921 hb_ot_map_glyphs_fast (buffer
);
923 HB_BUFFER_DEALLOCATE_VAR (buffer
, glyph_index
);
927 hb_ot_substitute_plan (const hb_ot_shape_context_t
*c
)
929 hb_buffer_t
*buffer
= c
->buffer
;
931 hb_ot_layout_substitute_start (c
->font
, buffer
);
933 if (c
->plan
->fallback_glyph_classes
)
934 hb_synthesize_glyph_classes (c
->buffer
);
936 #ifndef HB_NO_AAT_SHAPE
937 if (unlikely (c
->plan
->apply_morx
))
938 hb_aat_layout_substitute (c
->plan
, c
->font
, c
->buffer
,
939 c
->user_features
, c
->num_user_features
);
942 c
->plan
->substitute (c
->font
, buffer
);
946 hb_ot_substitute_pre (const hb_ot_shape_context_t
*c
)
948 hb_ot_substitute_default (c
);
950 _hb_buffer_allocate_gsubgpos_vars (c
->buffer
);
952 hb_ot_substitute_plan (c
);
954 #ifndef HB_NO_AAT_SHAPE
955 if (c
->plan
->apply_morx
&& c
->plan
->apply_gpos
)
956 hb_aat_layout_remove_deleted_glyphs (c
->buffer
);
961 hb_ot_substitute_post (const hb_ot_shape_context_t
*c
)
963 #ifndef HB_NO_AAT_SHAPE
964 if (c
->plan
->apply_morx
&& !c
->plan
->apply_gpos
)
965 hb_aat_layout_remove_deleted_glyphs (c
->buffer
);
968 hb_ot_hide_default_ignorables (c
->buffer
, c
->font
);
970 if (c
->plan
->shaper
->postprocess_glyphs
&&
971 c
->buffer
->message(c
->font
, "start postprocess-glyphs")) {
972 c
->plan
->shaper
->postprocess_glyphs (c
->plan
, c
->buffer
, c
->font
);
973 (void) c
->buffer
->message(c
->font
, "end postprocess-glyphs");
983 adjust_mark_offsets (hb_glyph_position_t
*pos
)
985 pos
->x_offset
-= pos
->x_advance
;
986 pos
->y_offset
-= pos
->y_advance
;
990 zero_mark_width (hb_glyph_position_t
*pos
)
997 zero_mark_widths_by_gdef (hb_buffer_t
*buffer
, bool adjust_offsets
)
999 unsigned int count
= buffer
->len
;
1000 hb_glyph_info_t
*info
= buffer
->info
;
1001 for (unsigned int i
= 0; i
< count
; i
++)
1002 if (_hb_glyph_info_is_mark (&info
[i
]))
1005 adjust_mark_offsets (&buffer
->pos
[i
]);
1006 zero_mark_width (&buffer
->pos
[i
]);
1011 hb_ot_position_default (const hb_ot_shape_context_t
*c
)
1013 hb_direction_t direction
= c
->buffer
->props
.direction
;
1014 unsigned int count
= c
->buffer
->len
;
1015 hb_glyph_info_t
*info
= c
->buffer
->info
;
1016 hb_glyph_position_t
*pos
= c
->buffer
->pos
;
1018 if (HB_DIRECTION_IS_HORIZONTAL (direction
))
1020 c
->font
->get_glyph_h_advances (count
, &info
[0].codepoint
, sizeof(info
[0]),
1021 &pos
[0].x_advance
, sizeof(pos
[0]));
1022 /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
1023 if (c
->font
->has_glyph_h_origin_func ())
1024 for (unsigned int i
= 0; i
< count
; i
++)
1025 c
->font
->subtract_glyph_h_origin (info
[i
].codepoint
,
1031 c
->font
->get_glyph_v_advances (count
, &info
[0].codepoint
, sizeof(info
[0]),
1032 &pos
[0].y_advance
, sizeof(pos
[0]));
1033 for (unsigned int i
= 0; i
< count
; i
++)
1035 c
->font
->subtract_glyph_v_origin (info
[i
].codepoint
,
1040 if (c
->buffer
->scratch_flags
& HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK
)
1041 _hb_ot_shape_fallback_spaces (c
->plan
, c
->font
, c
->buffer
);
1045 hb_ot_position_plan (const hb_ot_shape_context_t
*c
)
1047 unsigned int count
= c
->buffer
->len
;
1048 hb_glyph_info_t
*info
= c
->buffer
->info
;
1049 hb_glyph_position_t
*pos
= c
->buffer
->pos
;
1051 /* If the font has no GPOS and direction is forward, then when
1052 * zeroing mark widths, we shift the mark with it, such that the
1053 * mark is positioned hanging over the previous glyph. When
1054 * direction is backward we don't shift and it will end up
1055 * hanging over the next glyph after the final reordering.
1057 * Note: If fallback positioning happens, we don't care about
1058 * this as it will be overridden.
1060 bool adjust_offsets_when_zeroing
= c
->plan
->adjust_mark_positioning_when_zeroing
&&
1061 HB_DIRECTION_IS_FORWARD (c
->buffer
->props
.direction
);
1063 /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
1065 /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
1066 if (c
->font
->has_glyph_h_origin_func ())
1067 for (unsigned int i
= 0; i
< count
; i
++)
1068 c
->font
->add_glyph_h_origin (info
[i
].codepoint
,
1072 hb_ot_layout_position_start (c
->font
, c
->buffer
);
1074 if (c
->plan
->zero_marks
)
1075 switch (c
->plan
->shaper
->zero_width_marks
)
1077 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY
:
1078 zero_mark_widths_by_gdef (c
->buffer
, adjust_offsets_when_zeroing
);
1082 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE
:
1083 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
:
1087 c
->plan
->position (c
->font
, c
->buffer
);
1089 if (c
->plan
->zero_marks
)
1090 switch (c
->plan
->shaper
->zero_width_marks
)
1092 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
:
1093 zero_mark_widths_by_gdef (c
->buffer
, adjust_offsets_when_zeroing
);
1097 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE
:
1098 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY
:
1102 /* Finish off. Has to follow a certain order. */
1103 hb_ot_layout_position_finish_advances (c
->font
, c
->buffer
);
1104 hb_ot_zero_width_default_ignorables (c
->buffer
);
1105 #ifndef HB_NO_AAT_SHAPE
1106 if (c
->plan
->apply_morx
)
1107 hb_aat_layout_zero_width_deleted_glyphs (c
->buffer
);
1109 hb_ot_layout_position_finish_offsets (c
->font
, c
->buffer
);
1111 /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
1112 if (c
->font
->has_glyph_h_origin_func ())
1113 for (unsigned int i
= 0; i
< count
; i
++)
1114 c
->font
->subtract_glyph_h_origin (info
[i
].codepoint
,
1118 if (c
->plan
->fallback_mark_positioning
)
1119 _hb_ot_shape_fallback_mark_position (c
->plan
, c
->font
, c
->buffer
,
1120 adjust_offsets_when_zeroing
);
1124 hb_ot_position (const hb_ot_shape_context_t
*c
)
1126 c
->buffer
->clear_positions ();
1128 hb_ot_position_default (c
);
1130 hb_ot_position_plan (c
);
1132 if (HB_DIRECTION_IS_BACKWARD (c
->buffer
->props
.direction
))
1133 hb_buffer_reverse (c
->buffer
);
1135 _hb_buffer_deallocate_gsubgpos_vars (c
->buffer
);
1139 hb_propagate_flags (hb_buffer_t
*buffer
)
1141 /* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
1142 * Simplifies using them. */
1144 if (!(buffer
->scratch_flags
& HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS
))
1147 /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things:
1149 * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_TATWEEL,
1150 * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL,
1151 * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK.
1153 * We couldn't make this interaction earlier. It has to be done here.
1155 bool flip_tatweel
= buffer
->flags
& HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL
;
1157 bool clear_concat
= (buffer
->flags
& HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT
) == 0;
1159 hb_glyph_info_t
*info
= buffer
->info
;
1161 foreach_cluster (buffer
, start
, end
)
1163 unsigned int mask
= 0;
1164 for (unsigned int i
= start
; i
< end
; i
++)
1165 mask
|= info
[i
].mask
& HB_GLYPH_FLAG_DEFINED
;
1169 if (mask
& HB_GLYPH_FLAG_UNSAFE_TO_BREAK
)
1170 mask
&= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL
;
1171 if (mask
& HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL
)
1172 mask
|= HB_GLYPH_FLAG_UNSAFE_TO_BREAK
| HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
;
1176 mask
&= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT
;
1178 for (unsigned int i
= start
; i
< end
; i
++)
1179 info
[i
].mask
= mask
;
1183 /* Pull it all together! */
1186 hb_ot_shape_internal (hb_ot_shape_context_t
*c
)
1188 /* Save the original direction, we use it later. */
1189 c
->target_direction
= c
->buffer
->props
.direction
;
1191 _hb_buffer_allocate_unicode_vars (c
->buffer
);
1193 hb_ot_shape_initialize_masks (c
);
1194 hb_set_unicode_props (c
->buffer
);
1195 hb_insert_dotted_circle (c
->buffer
, c
->font
);
1197 hb_form_clusters (c
->buffer
);
1199 hb_ensure_native_direction (c
->buffer
);
1201 if (c
->plan
->shaper
->preprocess_text
&&
1202 c
->buffer
->message(c
->font
, "start preprocess-text"))
1204 c
->plan
->shaper
->preprocess_text (c
->plan
, c
->buffer
, c
->font
);
1205 (void) c
->buffer
->message(c
->font
, "end preprocess-text");
1208 hb_ot_substitute_pre (c
);
1210 hb_ot_substitute_post (c
);
1212 hb_propagate_flags (c
->buffer
);
1214 _hb_buffer_deallocate_unicode_vars (c
->buffer
);
1216 c
->buffer
->props
.direction
= c
->target_direction
;
1218 c
->buffer
->leave ();
1223 _hb_ot_shape (hb_shape_plan_t
*shape_plan
,
1225 hb_buffer_t
*buffer
,
1226 const hb_feature_t
*features
,
1227 unsigned int num_features
)
1229 hb_ot_shape_context_t c
= {&shape_plan
->ot
, font
, font
->face
, buffer
, features
, num_features
};
1230 hb_ot_shape_internal (&c
);
1237 * hb_ot_shape_plan_collect_lookups:
1238 * @shape_plan: #hb_shape_plan_t to query
1239 * @table_tag: GSUB or GPOS
1240 * @lookup_indexes: (out): The #hb_set_t set of lookups returned
1242 * Computes the complete set of GSUB or GPOS lookups that are applicable
1243 * under a given @shape_plan.
1248 hb_ot_shape_plan_collect_lookups (hb_shape_plan_t
*shape_plan
,
1250 hb_set_t
*lookup_indexes
/* OUT */)
1252 shape_plan
->ot
.collect_lookups (table_tag
, lookup_indexes
);
1256 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
1258 add_char (hb_font_t
*font
,
1259 hb_unicode_funcs_t
*unicode
,
1264 hb_codepoint_t glyph
;
1265 if (font
->get_nominal_glyph (u
, &glyph
))
1266 glyphs
->add (glyph
);
1269 hb_codepoint_t m
= unicode
->mirroring (u
);
1270 if (m
!= u
&& font
->get_nominal_glyph (m
, &glyph
))
1271 glyphs
->add (glyph
);
1277 * hb_ot_shape_glyphs_closure:
1278 * @font: #hb_font_t to work upon
1279 * @buffer: The input buffer to compute from
1280 * @features: (array length=num_features): The features enabled on the buffer
1281 * @num_features: The number of features enabled on the buffer
1282 * @glyphs: (out): The #hb_set_t set of glyphs comprising the transitive closure of the query
1284 * Computes the transitive closure of glyphs needed for a specified
1285 * input buffer under the given font and feature list. The closure is
1286 * computed as a set, not as a list.
1291 hb_ot_shape_glyphs_closure (hb_font_t
*font
,
1292 hb_buffer_t
*buffer
,
1293 const hb_feature_t
*features
,
1294 unsigned int num_features
,
1297 const char *shapers
[] = {"ot", nullptr};
1298 hb_shape_plan_t
*shape_plan
= hb_shape_plan_create_cached (font
->face
, &buffer
->props
,
1299 features
, num_features
, shapers
);
1301 bool mirror
= hb_script_get_horizontal_direction (buffer
->props
.script
) == HB_DIRECTION_RTL
;
1303 unsigned int count
= buffer
->len
;
1304 hb_glyph_info_t
*info
= buffer
->info
;
1305 for (unsigned int i
= 0; i
< count
; i
++)
1306 add_char (font
, buffer
->unicode
, mirror
, info
[i
].codepoint
, glyphs
);
1308 hb_set_t
*lookups
= hb_set_create ();
1309 hb_ot_shape_plan_collect_lookups (shape_plan
, HB_OT_TAG_GSUB
, lookups
);
1310 hb_ot_layout_lookups_substitute_closure (font
->face
, lookups
, glyphs
);
1312 hb_set_destroy (lookups
);
1314 hb_shape_plan_destroy (shape_plan
);