1use 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) => kern.subtables.into_iter().any(|s| 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) => kern.subtables.into_iter().any(|s| s.has_cross_stream),
31 None => false,
32 }
33}
34
35pub fn _hb_ot_layout_set_glyph_props(face: &hb_font_t, buffer: &mut hb_buffer_t) {
40 let len = buffer.len;
41 for info 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(false, |table| table.has_glyph_classes())
51}
52
53#[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
81pub trait LayoutTable {
83 const INDEX: TableIndex;
85
86 const IN_PLACE: bool;
88
89 type Lookup: LayoutLookup;
91
92 fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup>;
94}
95
96pub trait LayoutLookup: Apply {
98 fn props(&self) -> u32;
100
101 fn is_reverse(&self) -> bool;
103
104 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 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 hb_tag_t::default_script(),
142 hb_tag_t::default_language(),
144 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 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 if let Some(index) = script.languages.index(hb_tag_t::default_language()) {
174 return Some(index);
175 }
176
177 None
178 }
179
180 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 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
225pub 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
231pub 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 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 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 = false;
298 while ctx.buffer.idx < ctx.buffer.len && ctx.buffer.successful {
299 let cur = ctx.buffer.cur(0);
300 if (cur.mask & ctx.lookup_mask()) != 0
301 && ctx.check_glyph_property(cur, 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 = false;
314 loop {
315 let cur = ctx.buffer.cur(0);
316 ret |= (cur.mask & ctx.lookup_mask()) != 0
317 && ctx.check_glyph_property(cur, 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#[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 let gen_cat = gen_cat.to_rb();
420 let n =
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 = info.unicode_props() & UnicodeProps::GENERAL_CATEGORY.bits();
430 hb_unicode_general_category_t::from_rb(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 = ((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#[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 = ((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 = _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format;
498 let b = (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, hb_unicode_general_category_t::Format);
506 info.set_unicode_props(info.unicode_props() | UnicodeProps::CF_VS.bits())
507 } else {
508 _hb_glyph_info_set_general_category(info, 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 = 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 = 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#[inline]
538pub(crate) fn _hb_glyph_info_set_continuation(info: &mut hb_glyph_info_t) {
539 let mut n = 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 = 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(b)
558}
559
560pub fn _hb_ot_layout_reverse_graphemes(buffer: &mut hb_buffer_t) {
561 buffer.reverse_groups(
562 _hb_grapheme_group_func,
563 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
584const 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, 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#[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 = 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 = 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 = buffer.len;
751 for info in &mut buffer.info[..len] {
752 _hb_glyph_info_clear_substituted(info);
753 }
754
755 false
756}