1//! OpenType layout.
2
3use core::ops::{Index, IndexMut};
4
5use super::buffer::*;
6use super::common::TagExt;
7use super::ot_layout_gsubgpos::{Apply, OT};
8use super::ot_shape_plan::hb_ot_shape_plan_t;
9use super::unicode::{hb_unicode_funcs_t, hb_unicode_general_category_t, GeneralCategoryExt};
10use super::{hb_font_t, hb_glyph_info_t, hb_tag_t};
11use crate::hb::set_digest::{hb_set_digest_ext, hb_set_digest_t};
12use ttf_parser::opentype_layout::{FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex};
13
14pub const MAX_NESTING_LEVEL: usize = 64;
15pub const MAX_CONTEXT_LENGTH: usize = 64;
16
17pub fn hb_ot_layout_has_kerning(face: &hb_font_t) -> bool {
18 face.tables().kern.is_some()
19}
20
21pub fn hb_ot_layout_has_machine_kerning(face: &hb_font_t) -> bool {
22 match face.tables().kern {
23 Some(ref kern: &Table<'_>) => kern.subtables.into_iter().any(|s: Subtable<'_>| s.has_state_machine),
24 None => false,
25 }
26}
27
28pub fn hb_ot_layout_has_cross_kerning(face: &hb_font_t) -> bool {
29 match face.tables().kern {
30 Some(ref kern: &Table<'_>) => kern.subtables.into_iter().any(|s: Subtable<'_>| s.has_cross_stream),
31 None => false,
32 }
33}
34
35// hb_ot_layout_kern
36
37// OT::GDEF::is_blocklisted unsupported
38
39pub fn _hb_ot_layout_set_glyph_props(face: &hb_font_t, buffer: &mut hb_buffer_t) {
40 let len: usize = buffer.len;
41 for info: &mut hb_glyph_info_t in &mut buffer.info[..len] {
42 info.set_glyph_props(face.glyph_props(info.as_glyph()));
43 info.set_lig_props(0);
44 }
45}
46
47pub fn hb_ot_layout_has_glyph_classes(face: &hb_font_t) -> bool {
48 face.tables()
49 .gdef
50 .map_or(default:false, |table: Table<'_>| table.has_glyph_classes())
51}
52
53// get_gsubgpos_table
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum TableIndex {
57 GSUB = 0,
58 GPOS = 1,
59}
60
61impl TableIndex {
62 pub fn iter() -> impl Iterator<Item = TableIndex> {
63 [Self::GSUB, Self::GPOS].iter().copied()
64 }
65}
66
67impl<T> Index<TableIndex> for [T] {
68 type Output = T;
69
70 fn index(&self, table_index: TableIndex) -> &Self::Output {
71 &self[table_index as usize]
72 }
73}
74
75impl<T> IndexMut<TableIndex> for [T] {
76 fn index_mut(&mut self, table_index: TableIndex) -> &mut Self::Output {
77 &mut self[table_index as usize]
78 }
79}
80
81/// A lookup-based layout table (GSUB or GPOS).
82pub trait LayoutTable {
83 /// The index of this table.
84 const INDEX: TableIndex;
85
86 /// Whether lookups in this table can be applied to the buffer in-place.
87 const IN_PLACE: bool;
88
89 /// The kind of lookup stored in this table.
90 type Lookup: LayoutLookup;
91
92 /// Get the lookup at the specified index.
93 fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup>;
94}
95
96/// A lookup in a layout table.
97pub trait LayoutLookup: Apply {
98 /// The lookup's lookup_props.
99 fn props(&self) -> u32;
100
101 /// Whether the lookup has to be applied backwards.
102 fn is_reverse(&self) -> bool;
103
104 /// The digest of the lookup.
105 fn digest(&self) -> &hb_set_digest_t;
106}
107
108pub trait LayoutTableExt {
109 fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)>;
110 fn select_script_language(
111 &self,
112 script_index: ScriptIndex,
113 lang_tags: &[hb_tag_t],
114 ) -> Option<LanguageIndex>;
115 fn get_required_language_feature(
116 &self,
117 script_index: ScriptIndex,
118 lang_index: Option<LanguageIndex>,
119 ) -> Option<(FeatureIndex, hb_tag_t)>;
120 fn find_language_feature(
121 &self,
122 script_index: ScriptIndex,
123 lang_index: Option<LanguageIndex>,
124 feature_tag: hb_tag_t,
125 ) -> Option<FeatureIndex>;
126}
127
128impl LayoutTableExt for ttf_parser::opentype_layout::LayoutTable<'_> {
129 // hb_ot_layout_table_select_script
130 /// Returns true + index and tag of the first found script tag in the given GSUB or GPOS table
131 /// or false + index and tag if falling back to a default script.
132 fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)> {
133 for &tag in script_tags {
134 if let Some(index) = self.scripts.index(tag) {
135 return Some((true, index, tag));
136 }
137 }
138
139 for &tag in &[
140 // try finding 'DFLT'
141 hb_tag_t::default_script(),
142 // try with 'dflt'; MS site has had typos and many fonts use it now :(
143 hb_tag_t::default_language(),
144 // try with 'latn'; some old fonts put their features there even though
145 // they're really trying to support Thai, for example :(
146 hb_tag_t::from_bytes(b"latn"),
147 ] {
148 if let Some(index) = self.scripts.index(tag) {
149 return Some((false, index, tag));
150 }
151 }
152
153 None
154 }
155
156 // hb_ot_layout_script_select_language
157 /// Returns the index of the first found language tag in the given GSUB or GPOS table,
158 /// underneath the specified script index.
159 fn select_script_language(
160 &self,
161 script_index: ScriptIndex,
162 lang_tags: &[hb_tag_t],
163 ) -> Option<LanguageIndex> {
164 let script = self.scripts.get(script_index)?;
165
166 for &tag in lang_tags {
167 if let Some(index) = script.languages.index(tag) {
168 return Some(index);
169 }
170 }
171
172 // try finding 'dflt'
173 if let Some(index) = script.languages.index(hb_tag_t::default_language()) {
174 return Some(index);
175 }
176
177 None
178 }
179
180 // hb_ot_layout_language_get_required_feature
181 /// Returns the index and tag of a required feature in the given GSUB or GPOS table,
182 /// underneath the specified script and language.
183 fn get_required_language_feature(
184 &self,
185 script_index: ScriptIndex,
186 lang_index: Option<LanguageIndex>,
187 ) -> Option<(FeatureIndex, hb_tag_t)> {
188 let script = self.scripts.get(script_index)?;
189 let sys = match lang_index {
190 Some(index) => script.languages.get(index)?,
191 None => script.default_language?,
192 };
193 let idx = sys.required_feature?;
194 let tag = self.features.get(idx)?.tag;
195 Some((idx, tag))
196 }
197
198 // hb_ot_layout_language_find_feature
199 /// Returns the index of a given feature tag in the given GSUB or GPOS table,
200 /// underneath the specified script and language.
201 fn find_language_feature(
202 &self,
203 script_index: ScriptIndex,
204 lang_index: Option<LanguageIndex>,
205 feature_tag: hb_tag_t,
206 ) -> Option<FeatureIndex> {
207 let script = self.scripts.get(script_index)?;
208 let sys = match lang_index {
209 Some(index) => script.languages.get(index)?,
210 None => script.default_language?,
211 };
212
213 for i in 0..sys.feature_indices.len() {
214 if let Some(index) = sys.feature_indices.get(i) {
215 if self.features.get(index).map(|v| v.tag) == Some(feature_tag) {
216 return Some(index);
217 }
218 }
219 }
220
221 None
222 }
223}
224
225/// Called before substitution lookups are performed, to ensure that glyph
226/// class and other properties are set on the glyphs in the buffer.
227pub fn hb_ot_layout_substitute_start(face: &hb_font_t, buffer: &mut hb_buffer_t) {
228 _hb_ot_layout_set_glyph_props(face, buffer)
229}
230
231/// Applies the lookups in the given GSUB or GPOS table.
232pub fn apply_layout_table<T: LayoutTable>(
233 plan: &hb_ot_shape_plan_t,
234 face: &hb_font_t,
235 buffer: &mut hb_buffer_t,
236 table: Option<&T>,
237) {
238 let mut ctx = OT::hb_ot_apply_context_t::new(T::INDEX, face, buffer);
239
240 for (stage_index, stage) in plan.ot_map.stages(T::INDEX).iter().enumerate() {
241 if let Some(table) = table {
242 for lookup_map in plan.ot_map.stage_lookups(T::INDEX, stage_index) {
243 let Some(lookup) = table.get_lookup(lookup_map.index) else {
244 continue;
245 };
246
247 if lookup.digest().may_have(&ctx.digest) {
248 ctx.lookup_index = lookup_map.index;
249 ctx.set_lookup_mask(lookup_map.mask);
250 ctx.auto_zwj = lookup_map.auto_zwj;
251 ctx.auto_zwnj = lookup_map.auto_zwnj;
252
253 ctx.random = lookup_map.random;
254 ctx.per_syllable = lookup_map.per_syllable;
255
256 apply_string::<T>(&mut ctx, lookup);
257 }
258 }
259 }
260
261 if let Some(func) = stage.pause_func {
262 if func(plan, face, ctx.buffer) {
263 ctx.digest = ctx.buffer.digest();
264 }
265 }
266 }
267}
268
269fn apply_string<T: LayoutTable>(ctx: &mut OT::hb_ot_apply_context_t, lookup: &T::Lookup) {
270 if ctx.buffer.is_empty() || ctx.lookup_mask() == 0 {
271 return;
272 }
273
274 ctx.lookup_props = lookup.props();
275
276 if !lookup.is_reverse() {
277 // in/out forward substitution/positioning
278 if !T::IN_PLACE {
279 ctx.buffer.clear_output();
280 }
281 ctx.buffer.idx = 0;
282 apply_forward(ctx, lookup);
283
284 if !T::IN_PLACE {
285 ctx.buffer.sync();
286 }
287 } else {
288 // in-place backward substitution/positioning
289 assert!(!ctx.buffer.have_output);
290
291 ctx.buffer.idx = ctx.buffer.len - 1;
292 apply_backward(ctx, lookup);
293 }
294}
295
296fn apply_forward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool {
297 let mut ret: bool = false;
298 while ctx.buffer.idx < ctx.buffer.len && ctx.buffer.successful {
299 let cur: &hb_glyph_info_t = ctx.buffer.cur(0);
300 if (cur.mask & ctx.lookup_mask()) != 0
301 && ctx.check_glyph_property(info:cur, match_props:ctx.lookup_props)
302 && lookup.apply(ctx).is_some()
303 {
304 ret = true;
305 } else {
306 ctx.buffer.next_glyph();
307 }
308 }
309 ret
310}
311
312fn apply_backward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool {
313 let mut ret: bool = false;
314 loop {
315 let cur: &hb_glyph_info_t = ctx.buffer.cur(0);
316 ret |= (cur.mask & ctx.lookup_mask()) != 0
317 && ctx.check_glyph_property(info:cur, match_props:ctx.lookup_props)
318 && lookup.apply(ctx).is_some();
319
320 if ctx.buffer.idx == 0 {
321 break;
322 }
323
324 ctx.buffer.idx -= 1;
325 }
326 ret
327}
328
329/* unicode_props */
330
331/* Design:
332 * unicode_props() is a two-byte number. The low byte includes:
333 * - Modified General_Category: 5 bits.
334 * - A bit each for:
335 * * Is it Default_Ignorable(); we have a modified Default_Ignorable().
336 * * Whether it's one of the four Mongolian Free Variation Selectors,
337 * CGJ, or other characters that are hidden but should not be ignored
338 * like most other Default_Ignorable()s do during GSUB matching.
339 * * Whether it's a grapheme continuation.
340 *
341 * The high-byte has different meanings, switched by the Gen-Cat:
342 * - For Mn,Mc,Me: the modified Combining_Class.
343 * - For Cf: whether it's ZWJ, ZWNJ, or something else.
344 * - For Ws: index of which space character this is, if space fallback
345 * is needed, ie. we don't set this by default, only if asked to.
346 *
347 * Above I said "modified" General_Category. This is because we need to
348 * remember Variation Selectors, and we don't have bits left. So we
349 * change their Gen_Cat from Mn to Cf, and use a bit of the high byte to
350 * remember them.
351 */
352
353// enum hb_unicode_props_flags_t {
354// UPROPS_MASK_GEN_CAT = 0x001Fu,
355// UPROPS_MASK_IGNORABLE = 0x0020u,
356// UPROPS_MASK_HIDDEN = 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters */
357// UPROPS_MASK_CONTINUATION=0x0080u,
358
359// /* If GEN_CAT=FORMAT, top byte masks: */
360// UPROPS_MASK_Cf_ZWJ = 0x0100u,
361// UPROPS_MASK_Cf_ZWNJ = 0x0200u
362// };
363// HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t);
364
365// static inline void
366// _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer)
367// {
368// hb_unicode_funcs_t *unicode = buffer->unicode;
369// unsigned int u = info->codepoint;
370// unsigned int gen_cat = (unsigned int) unicode->general_category (u);
371// unsigned int props = gen_cat;
372
373// if (u >= 0x80u)
374// {
375// buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII;
376
377// if (unlikely (unicode->is_default_ignorable (u)))
378// {
379// buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES;
380// props |= UPROPS_MASK_IGNORABLE;
381// if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ;
382// else if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ;
383// /* Mongolian Free Variation Selectors need to be remembered
384// * because although we need to hide them like default-ignorables,
385// * they need to non-ignorable during shaping. This is similar to
386// * what we do for joiners in Indic-like shapers, but since the
387// * FVSes are GC=Mn, we have use a separate bit to remember them.
388// * Fixes:
389// * https://github.com/harfbuzz/harfbuzz/issues/234 */
390// else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) props |= UPROPS_MASK_HIDDEN;
391// /* TAG characters need similar treatment. Fixes:
392// * https://github.com/harfbuzz/harfbuzz/issues/463 */
393// else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN;
394// /* COMBINING GRAPHEME JOINER should not be skipped; at least some times.
395// * https://github.com/harfbuzz/harfbuzz/issues/554 */
396// else if (unlikely (u == 0x034Fu))
397// {
398// buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CGJ;
399// props |= UPROPS_MASK_HIDDEN;
400// }
401// }
402
403// if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat)))
404// {
405// props |= UPROPS_MASK_CONTINUATION;
406// props |= unicode->modified_combining_class (u)<<8;
407// }
408// }
409
410// info->unicode_props() = props;
411// }
412
413#[inline]
414pub fn _hb_glyph_info_set_general_category(
415 info: &mut hb_glyph_info_t,
416 gen_cat: hb_unicode_general_category_t,
417) {
418 /* Clears top-byte. */
419 let gen_cat: u32 = gen_cat.to_rb();
420 let n: u16 =
421 (gen_cat as u16) | (info.unicode_props() & (0xFF & !UnicodeProps::GENERAL_CATEGORY.bits()));
422 info.set_unicode_props(n);
423}
424
425#[inline]
426pub fn _hb_glyph_info_get_general_category(
427 info: &hb_glyph_info_t,
428) -> hb_unicode_general_category_t {
429 let n: u16 = info.unicode_props() & UnicodeProps::GENERAL_CATEGORY.bits();
430 hb_unicode_general_category_t::from_rb(gc:n as u32)
431}
432
433#[inline]
434pub fn _hb_glyph_info_is_unicode_mark(info: &hb_glyph_info_t) -> bool {
435 _hb_glyph_info_get_general_category(info).is_mark()
436}
437
438#[inline]
439pub(crate) fn _hb_glyph_info_set_modified_combining_class(
440 info: &mut hb_glyph_info_t,
441 modified_class: u8,
442) {
443 if !_hb_glyph_info_is_unicode_mark(info) {
444 return;
445 }
446
447 let n: u16 = ((modified_class as u16) << 8) | (info.unicode_props() & 0xFF);
448 info.set_unicode_props(n);
449}
450
451#[inline]
452pub fn _hb_glyph_info_get_modified_combining_class(info: &hb_glyph_info_t) -> u8 {
453 if _hb_glyph_info_is_unicode_mark(info) {
454 (info.unicode_props() >> 8) as u8
455 } else {
456 0
457 }
458}
459
460// TODO: use
461// #[inline]
462// pub fn info_cc(info: &hb_glyph_info_t) -> u8 {
463// _hb_glyph_info_get_modified_combining_class(info)
464// }
465
466#[inline]
467pub(crate) fn _hb_glyph_info_is_unicode_space(info: &hb_glyph_info_t) -> bool {
468 _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::SpaceSeparator
469}
470
471#[inline]
472pub(crate) fn _hb_glyph_info_set_unicode_space_fallback_type(
473 info: &mut hb_glyph_info_t,
474 s: hb_unicode_funcs_t::space_t,
475) {
476 if !_hb_glyph_info_is_unicode_space(info) {
477 return;
478 }
479
480 let n: u16 = ((s as u16) << 8) | (info.unicode_props() & 0xFF);
481 info.set_unicode_props(n);
482}
483
484#[inline]
485pub(crate) fn _hb_glyph_info_get_unicode_space_fallback_type(
486 info: &hb_glyph_info_t,
487) -> hb_unicode_funcs_t::space_t {
488 if _hb_glyph_info_is_unicode_space(info) {
489 (info.unicode_props() >> 8) as u8
490 } else {
491 hb_unicode_funcs_t::NOT_SPACE
492 }
493}
494
495#[inline]
496pub(crate) fn _hb_glyph_info_is_variation_selector(info: &hb_glyph_info_t) -> bool {
497 let a: bool = _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format;
498 let b: bool = (info.unicode_props() & UnicodeProps::CF_VS.bits()) != 0;
499 a && b
500}
501
502#[inline]
503pub(crate) fn _hb_glyph_info_set_variation_selector(info: &mut hb_glyph_info_t, customize: bool) {
504 if customize {
505 _hb_glyph_info_set_general_category(info, gen_cat:hb_unicode_general_category_t::Format);
506 info.set_unicode_props(info.unicode_props() | UnicodeProps::CF_VS.bits())
507 } else {
508 // Reset to their original condition
509 _hb_glyph_info_set_general_category(info, gen_cat:hb_unicode_general_category_t::NonspacingMark);
510 }
511}
512
513#[inline]
514pub(crate) fn _hb_glyph_info_is_default_ignorable(info: &hb_glyph_info_t) -> bool {
515 let n: u16 = info.unicode_props() & UnicodeProps::IGNORABLE.bits();
516 n != 0 && !_hb_glyph_info_substituted(info)
517}
518
519#[inline]
520pub(crate) fn _hb_glyph_info_clear_default_ignorable(info: &mut hb_glyph_info_t) {
521 let mut n: u16 = info.unicode_props();
522 n &= !UnicodeProps::IGNORABLE.bits();
523 info.set_unicode_props(n);
524}
525
526#[inline]
527pub(crate) fn _hb_glyph_info_is_hidden(info: &hb_glyph_info_t) -> bool {
528 (info.unicode_props() & UnicodeProps::HIDDEN.bits()) != 0
529}
530
531// static inline void
532// _hb_glyph_info_unhide (hb_glyph_info_t *info)
533// {
534// info->unicode_props() &= ~ UPROPS_MASK_HIDDEN;
535// }
536
537#[inline]
538pub(crate) fn _hb_glyph_info_set_continuation(info: &mut hb_glyph_info_t) {
539 let mut n: u16 = info.unicode_props();
540 n |= UnicodeProps::CONTINUATION.bits();
541 info.set_unicode_props(n);
542}
543
544#[inline]
545pub(crate) fn _hb_glyph_info_reset_continuation(info: &mut hb_glyph_info_t) {
546 let mut n: u16 = info.unicode_props();
547 n &= !UnicodeProps::CONTINUATION.bits();
548 info.set_unicode_props(n);
549}
550
551#[inline]
552pub(crate) fn _hb_glyph_info_is_continuation(info: &hb_glyph_info_t) -> bool {
553 info.unicode_props() & UnicodeProps::CONTINUATION.bits() != 0
554}
555
556pub(crate) fn _hb_grapheme_group_func(_: &hb_glyph_info_t, b: &hb_glyph_info_t) -> bool {
557 _hb_glyph_info_is_continuation(info:b)
558}
559
560pub fn _hb_ot_layout_reverse_graphemes(buffer: &mut hb_buffer_t) {
561 buffer.reverse_groups(
562 group:_hb_grapheme_group_func,
563 merge_clusters:buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
564 )
565}
566
567#[inline]
568pub(crate) fn _hb_glyph_info_is_unicode_format(info: &hb_glyph_info_t) -> bool {
569 _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format
570}
571
572#[inline]
573pub(crate) fn _hb_glyph_info_is_zwnj(info: &hb_glyph_info_t) -> bool {
574 _hb_glyph_info_is_unicode_format(info)
575 && (info.unicode_props() & UnicodeProps::CF_ZWNJ.bits() != 0)
576}
577
578#[inline]
579pub(crate) fn _hb_glyph_info_is_zwj(info: &hb_glyph_info_t) -> bool {
580 _hb_glyph_info_is_unicode_format(info)
581 && (info.unicode_props() & UnicodeProps::CF_ZWJ.bits() != 0)
582}
583
584// static inline bool
585// _hb_glyph_info_is_joiner (const hb_glyph_info_t *info)
586// {
587// return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ));
588// }
589
590// static inline void
591// _hb_glyph_info_flip_joiners (hb_glyph_info_t *info)
592// {
593// if (!_hb_glyph_info_is_unicode_format (info))
594// return;
595// info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ;
596// }
597
598// /* lig_props: aka lig_id / lig_comp
599// *
600// * When a ligature is formed:
601// *
602// * - The ligature glyph and any marks in between all the same newly allocated
603// * lig_id,
604// * - The ligature glyph will get lig_num_comps set to the number of components
605// * - The marks get lig_comp > 0, reflecting which component of the ligature
606// * they were applied to.
607// * - This is used in GPOS to attach marks to the right component of a ligature
608// * in MarkLigPos,
609// * - Note that when marks are ligated together, much of the above is skipped
610// * and the current lig_id reused.
611// *
612// * When a multiple-substitution is done:
613// *
614// * - All resulting glyphs will have lig_id = 0,
615// * - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively.
616// * - This is used in GPOS to attach marks to the first component of a
617// * multiple substitution in MarkBasePos.
618// *
619// * The numbers are also used in GPOS to do mark-to-mark positioning only
620// * to marks that belong to the same component of the same ligature.
621// */
622// static inline void
623// _hb_glyph_info_clear_lig_props (hb_glyph_info_t *info)
624// {
625// info->lig_props() = 0;
626// }
627
628const IS_LIG_BASE: u8 = 0x10;
629
630#[inline]
631pub(crate) fn _hb_glyph_info_set_lig_props_for_ligature(
632 info: &mut hb_glyph_info_t,
633 lig_id: u8,
634 lig_num_comps: u8,
635) {
636 info.set_lig_props((lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F));
637}
638
639#[inline]
640pub(crate) fn _hb_glyph_info_set_lig_props_for_mark(
641 info: &mut hb_glyph_info_t,
642 lig_id: u8,
643 lig_comp: u8,
644) {
645 info.set_lig_props((lig_id << 5) | (lig_comp & 0x0F));
646}
647
648#[inline]
649pub(crate) fn _hb_glyph_info_set_lig_props_for_component(info: &mut hb_glyph_info_t, comp: u8) {
650 _hb_glyph_info_set_lig_props_for_mark(info, lig_id:0, comp);
651}
652
653#[inline]
654pub(crate) fn _hb_glyph_info_get_lig_id(info: &hb_glyph_info_t) -> u8 {
655 info.lig_props() >> 5
656}
657
658#[inline]
659pub(crate) fn _hb_glyph_info_ligated_internal(info: &hb_glyph_info_t) -> bool {
660 info.lig_props() & IS_LIG_BASE != 0
661}
662
663#[inline]
664pub(crate) fn _hb_glyph_info_get_lig_comp(info: &hb_glyph_info_t) -> u8 {
665 if _hb_glyph_info_ligated_internal(info) {
666 0
667 } else {
668 info.lig_props() & 0x0F
669 }
670}
671
672#[inline]
673pub(crate) fn _hb_glyph_info_get_lig_num_comps(info: &hb_glyph_info_t) -> u8 {
674 if info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
675 && _hb_glyph_info_ligated_internal(info)
676 {
677 info.lig_props() & 0x0F
678 } else {
679 1
680 }
681}
682
683// /* glyph_props: */
684// static inline void
685// _hb_glyph_info_set_glyph_props (hb_glyph_info_t *info, unsigned int props)
686// {
687// info->glyph_props() = props;
688// }
689
690// static inline unsigned int
691// _hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info)
692// {
693// return info->glyph_props();
694// }
695
696#[inline]
697pub(crate) fn _hb_glyph_info_is_base_glyph(info: &hb_glyph_info_t) -> bool {
698 info.glyph_props() & GlyphPropsFlags::BASE_GLYPH.bits() != 0
699}
700
701#[inline]
702pub(crate) fn _hb_glyph_info_is_ligature(info: &hb_glyph_info_t) -> bool {
703 info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
704}
705
706#[inline]
707pub(crate) fn _hb_glyph_info_is_mark(info: &hb_glyph_info_t) -> bool {
708 info.glyph_props() & GlyphPropsFlags::MARK.bits() != 0
709}
710
711#[inline]
712pub(crate) fn _hb_glyph_info_substituted(info: &hb_glyph_info_t) -> bool {
713 info.glyph_props() & GlyphPropsFlags::SUBSTITUTED.bits() != 0
714}
715
716#[inline]
717pub(crate) fn _hb_glyph_info_ligated(info: &hb_glyph_info_t) -> bool {
718 info.glyph_props() & GlyphPropsFlags::LIGATED.bits() != 0
719}
720
721#[inline]
722pub(crate) fn _hb_glyph_info_multiplied(info: &hb_glyph_info_t) -> bool {
723 info.glyph_props() & GlyphPropsFlags::MULTIPLIED.bits() != 0
724}
725
726#[inline]
727pub(crate) fn _hb_glyph_info_ligated_and_didnt_multiply(info: &hb_glyph_info_t) -> bool {
728 _hb_glyph_info_ligated(info) && !_hb_glyph_info_multiplied(info)
729}
730
731#[inline]
732pub(crate) fn _hb_glyph_info_clear_ligated_and_multiplied(info: &mut hb_glyph_info_t) {
733 let mut n: u16 = info.glyph_props();
734 n &= !(GlyphPropsFlags::LIGATED | GlyphPropsFlags::MULTIPLIED).bits();
735 info.set_glyph_props(n);
736}
737
738#[inline]
739pub(crate) fn _hb_glyph_info_clear_substituted(info: &mut hb_glyph_info_t) {
740 let mut n: u16 = info.glyph_props();
741 n &= !GlyphPropsFlags::SUBSTITUTED.bits();
742 info.set_glyph_props(n);
743}
744
745pub fn _hb_clear_substitution_flags(
746 _: &hb_ot_shape_plan_t,
747 _: &hb_font_t,
748 buffer: &mut hb_buffer_t,
749) -> bool {
750 let len: usize = buffer.len;
751 for info: &mut hb_glyph_info_t in &mut buffer.info[..len] {
752 _hb_glyph_info_clear_substituted(info);
753 }
754
755 false
756}
757