1use core::ops::{Index, IndexMut};
4
5use ttf_parser::opentype_layout::{FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex};
6use ttf_parser::GlyphId;
7
8use super::buffer::*;
9use super::common::TagExt;
10use super::ot_layout_gsubgpos::{Apply, OT};
11use super::ot_shape_plan::hb_ot_shape_plan_t;
12use super::unicode::{hb_unicode_funcs_t, hb_unicode_general_category_t, GeneralCategoryExt};
13use super::{hb_font_t, hb_glyph_info_t, hb_tag_t};
14
15pub const MAX_NESTING_LEVEL: usize = 64;
16pub const MAX_CONTEXT_LENGTH: usize = 64;
17
18pub fn hb_ot_layout_has_kerning(face: &hb_font_t) -> bool {
19 face.tables().kern.is_some()
20}
21
22pub fn hb_ot_layout_has_machine_kerning(face: &hb_font_t) -> bool {
23 match face.tables().kern {
24 Some(ref kern) => kern.subtables.into_iter().any(|s| s.has_state_machine),
25 None => false,
26 }
27}
28
29pub fn hb_ot_layout_has_cross_kerning(face: &hb_font_t) -> bool {
30 match face.tables().kern {
31 Some(ref kern) => kern.subtables.into_iter().any(|s| s.has_cross_stream),
32 None => false,
33 }
34}
35
36pub fn _hb_ot_layout_set_glyph_props(face: &hb_font_t, buffer: &mut hb_buffer_t) {
41 let len = buffer.len;
42 for info in &mut buffer.info[..len] {
43 info.set_glyph_props(face.glyph_props(info.as_glyph()));
44 info.set_lig_props(0);
45 info.set_syllable(0);
46 }
47}
48
49pub fn hb_ot_layout_has_glyph_classes(face: &hb_font_t) -> bool {
50 face.tables()
51 .gdef
52 .map_or(false, |table| table.has_glyph_classes())
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum TableIndex {
59 GSUB = 0,
60 GPOS = 1,
61}
62
63impl TableIndex {
64 pub fn iter() -> impl Iterator<Item = TableIndex> {
65 [Self::GSUB, Self::GPOS].iter().copied()
66 }
67}
68
69impl<T> Index<TableIndex> for [T] {
70 type Output = T;
71
72 fn index(&self, table_index: TableIndex) -> &Self::Output {
73 &self[table_index as usize]
74 }
75}
76
77impl<T> IndexMut<TableIndex> for [T] {
78 fn index_mut(&mut self, table_index: TableIndex) -> &mut Self::Output {
79 &mut self[table_index as usize]
80 }
81}
82
83pub trait LayoutTable {
85 const INDEX: TableIndex;
87
88 const IN_PLACE: bool;
90
91 type Lookup: LayoutLookup;
93
94 fn get_lookup(&self, index: LookupIndex) -> Option<&Self::Lookup>;
96}
97
98pub trait LayoutLookup: Apply {
100 fn props(&self) -> u32;
102
103 fn is_reverse(&self) -> bool;
105
106 fn covers(&self, glyph: GlyphId) -> bool;
108}
109
110pub trait LayoutTableExt {
111 fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)>;
112 fn select_script_language(
113 &self,
114 script_index: ScriptIndex,
115 lang_tags: &[hb_tag_t],
116 ) -> Option<LanguageIndex>;
117 fn get_required_language_feature(
118 &self,
119 script_index: ScriptIndex,
120 lang_index: Option<LanguageIndex>,
121 ) -> Option<(FeatureIndex, hb_tag_t)>;
122 fn find_language_feature(
123 &self,
124 script_index: ScriptIndex,
125 lang_index: Option<LanguageIndex>,
126 feature_tag: hb_tag_t,
127 ) -> Option<FeatureIndex>;
128}
129
130impl LayoutTableExt for ttf_parser::opentype_layout::LayoutTable<'_> {
131 fn select_script(&self, script_tags: &[hb_tag_t]) -> Option<(bool, ScriptIndex, hb_tag_t)> {
135 for &tag in script_tags {
136 if let Some(index) = self.scripts.index(tag) {
137 return Some((true, index, tag));
138 }
139 }
140
141 for &tag in &[
142 hb_tag_t::default_script(),
144 hb_tag_t::default_language(),
146 hb_tag_t::from_bytes(b"latn"),
149 ] {
150 if let Some(index) = self.scripts.index(tag) {
151 return Some((false, index, tag));
152 }
153 }
154
155 None
156 }
157
158 fn select_script_language(
162 &self,
163 script_index: ScriptIndex,
164 lang_tags: &[hb_tag_t],
165 ) -> Option<LanguageIndex> {
166 let script = self.scripts.get(script_index)?;
167
168 for &tag in lang_tags {
169 if let Some(index) = script.languages.index(tag) {
170 return Some(index);
171 }
172 }
173
174 if let Some(index) = script.languages.index(hb_tag_t::default_language()) {
176 return Some(index);
177 }
178
179 None
180 }
181
182 fn get_required_language_feature(
186 &self,
187 script_index: ScriptIndex,
188 lang_index: Option<LanguageIndex>,
189 ) -> Option<(FeatureIndex, hb_tag_t)> {
190 let script = self.scripts.get(script_index)?;
191 let sys = match lang_index {
192 Some(index) => script.languages.get(index)?,
193 None => script.default_language?,
194 };
195 let idx = sys.required_feature?;
196 let tag = self.features.get(idx)?.tag;
197 Some((idx, tag))
198 }
199
200 fn find_language_feature(
204 &self,
205 script_index: ScriptIndex,
206 lang_index: Option<LanguageIndex>,
207 feature_tag: hb_tag_t,
208 ) -> Option<FeatureIndex> {
209 let script = self.scripts.get(script_index)?;
210 let sys = match lang_index {
211 Some(index) => script.languages.get(index)?,
212 None => script.default_language?,
213 };
214
215 for i in 0..sys.feature_indices.len() {
216 if let Some(index) = sys.feature_indices.get(i) {
217 if self.features.get(index).map(|v| v.tag) == Some(feature_tag) {
218 return Some(index);
219 }
220 }
221 }
222
223 None
224 }
225}
226
227pub fn hb_ot_layout_substitute_start(face: &hb_font_t, buffer: &mut hb_buffer_t) {
230 _hb_ot_layout_set_glyph_props(face, buffer)
231}
232
233pub fn apply_layout_table<T: LayoutTable>(
235 plan: &hb_ot_shape_plan_t,
236 face: &hb_font_t,
237 buffer: &mut hb_buffer_t,
238 table: Option<&T>,
239) {
240 let mut ctx = OT::hb_ot_apply_context_t::new(T::INDEX, face, buffer);
241
242 for (stage_index, stage) in plan.ot_map.stages(T::INDEX).iter().enumerate() {
243 for lookup in plan.ot_map.stage_lookups(T::INDEX, stage_index) {
244 ctx.lookup_index = lookup.index;
245 ctx.lookup_mask = lookup.mask;
246 ctx.auto_zwj = lookup.auto_zwj;
247 ctx.auto_zwnj = lookup.auto_zwnj;
248
249 ctx.random = lookup.random;
250 ctx.per_syllable = lookup.per_syllable;
251
252 if let Some(table) = &table {
253 if let Some(lookup) = table.get_lookup(lookup.index) {
254 apply_string::<T>(&mut ctx, lookup);
255 }
256 }
257 }
258
259 if let Some(func) = stage.pause_func {
260 func(plan, face, ctx.buffer);
261 }
262 }
263}
264
265fn apply_string<T: LayoutTable>(ctx: &mut OT::hb_ot_apply_context_t, lookup: &T::Lookup) {
266 if ctx.buffer.is_empty() || ctx.lookup_mask == 0 {
267 return;
268 }
269
270 ctx.lookup_props = lookup.props();
271
272 if !lookup.is_reverse() {
273 if !T::IN_PLACE {
275 ctx.buffer.clear_output();
276 }
277 ctx.buffer.idx = 0;
278 apply_forward(ctx, lookup);
279
280 if !T::IN_PLACE {
281 ctx.buffer.sync();
282 }
283 } else {
284 assert!(!ctx.buffer.have_output);
286
287 ctx.buffer.idx = ctx.buffer.len - 1;
288 apply_backward(ctx, lookup);
289 }
290}
291
292fn apply_forward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool {
293 let mut ret = false;
294 while ctx.buffer.idx < ctx.buffer.len && ctx.buffer.successful {
295 let cur = ctx.buffer.cur(0);
296 if (cur.mask & ctx.lookup_mask) != 0
297 && ctx.check_glyph_property(cur, ctx.lookup_props)
298 && lookup.apply(ctx).is_some()
299 {
300 ret = true;
301 } else {
302 ctx.buffer.next_glyph();
303 }
304 }
305 ret
306}
307
308fn apply_backward(ctx: &mut OT::hb_ot_apply_context_t, lookup: &impl Apply) -> bool {
309 let mut ret = false;
310 loop {
311 let cur = ctx.buffer.cur(0);
312 ret |= (cur.mask & ctx.lookup_mask) != 0
313 && ctx.check_glyph_property(cur, ctx.lookup_props)
314 && lookup.apply(ctx).is_some();
315
316 if ctx.buffer.idx == 0 {
317 break;
318 }
319
320 ctx.buffer.idx -= 1;
321 }
322 ret
323}
324
325#[inline]
405pub fn _hb_glyph_info_set_general_category(
406 info: &mut hb_glyph_info_t,
407 gen_cat: hb_unicode_general_category_t,
408) {
409 let gen_cat = gen_cat.to_rb();
411 let n =
412 (gen_cat as u16) | (info.unicode_props() & (0xFF & !UnicodeProps::GENERAL_CATEGORY.bits()));
413 info.set_unicode_props(n);
414}
415
416#[inline]
417pub fn _hb_glyph_info_get_general_category(
418 info: &hb_glyph_info_t,
419) -> hb_unicode_general_category_t {
420 let n = info.unicode_props() & UnicodeProps::GENERAL_CATEGORY.bits();
421 hb_unicode_general_category_t::from_rb(n as u32)
422}
423
424#[inline]
425pub fn _hb_glyph_info_is_unicode_mark(info: &hb_glyph_info_t) -> bool {
426 _hb_glyph_info_get_general_category(info).is_mark()
427}
428
429#[inline]
430pub(crate) fn _hb_glyph_info_set_modified_combining_class(
431 info: &mut hb_glyph_info_t,
432 modified_class: u8,
433) {
434 if !_hb_glyph_info_is_unicode_mark(info) {
435 return;
436 }
437
438 let n = ((modified_class as u16) << 8) | (info.unicode_props() & 0xFF);
439 info.set_unicode_props(n);
440}
441
442#[inline]
443pub fn _hb_glyph_info_get_modified_combining_class(info: &hb_glyph_info_t) -> u8 {
444 if _hb_glyph_info_is_unicode_mark(info) {
445 (info.unicode_props() >> 8) as u8
446 } else {
447 0
448 }
449}
450
451#[inline]
458pub(crate) fn _hb_glyph_info_is_unicode_space(info: &hb_glyph_info_t) -> bool {
459 _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::SpaceSeparator
460}
461
462#[inline]
463pub(crate) fn _hb_glyph_info_set_unicode_space_fallback_type(
464 info: &mut hb_glyph_info_t,
465 s: hb_unicode_funcs_t::space_t,
466) {
467 if !_hb_glyph_info_is_unicode_space(info) {
468 return;
469 }
470
471 let n = ((s as u16) << 8) | (info.unicode_props() & 0xFF);
472 info.set_unicode_props(n);
473}
474
475#[inline]
476pub(crate) fn _hb_glyph_info_get_unicode_space_fallback_type(
477 info: &hb_glyph_info_t,
478) -> hb_unicode_funcs_t::space_t {
479 if _hb_glyph_info_is_unicode_space(info) {
480 (info.unicode_props() >> 8) as u8
481 } else {
482 hb_unicode_funcs_t::NOT_SPACE
483 }
484}
485
486#[inline]
487pub(crate) fn _hb_glyph_info_is_default_ignorable(info: &hb_glyph_info_t) -> bool {
488 let n = info.unicode_props() & UnicodeProps::IGNORABLE.bits();
489 n != 0 && !_hb_glyph_info_substituted(info)
490}
491
492#[inline]
507pub(crate) fn _hb_glyph_info_set_continuation(info: &mut hb_glyph_info_t) {
508 let mut n = info.unicode_props();
509 n |= UnicodeProps::CONTINUATION.bits();
510 info.set_unicode_props(n);
511}
512
513#[inline]
514pub(crate) fn _hb_glyph_info_reset_continuation(info: &mut hb_glyph_info_t) {
515 let mut n = info.unicode_props();
516 n &= !UnicodeProps::CONTINUATION.bits();
517 info.set_unicode_props(n);
518}
519
520#[inline]
521pub(crate) fn _hb_glyph_info_is_continuation(info: &hb_glyph_info_t) -> bool {
522 info.unicode_props() & UnicodeProps::CONTINUATION.bits() != 0
523}
524
525pub(crate) fn _hb_grapheme_group_func(_: &hb_glyph_info_t, b: &hb_glyph_info_t) -> bool {
526 _hb_glyph_info_is_continuation(b)
527}
528
529pub fn _hb_ot_layout_reverse_graphemes(buffer: &mut hb_buffer_t) {
530 buffer.reverse_groups(
531 _hb_grapheme_group_func,
532 buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
533 )
534}
535
536#[inline]
537pub(crate) fn _hb_glyph_info_is_unicode_format(info: &hb_glyph_info_t) -> bool {
538 _hb_glyph_info_get_general_category(info) == hb_unicode_general_category_t::Format
539}
540
541#[inline]
542pub(crate) fn _hb_glyph_info_is_zwnj(info: &hb_glyph_info_t) -> bool {
543 _hb_glyph_info_is_unicode_format(info)
544 && (info.unicode_props() & UnicodeProps::CF_ZWNJ.bits() != 0)
545}
546
547#[inline]
548pub(crate) fn _hb_glyph_info_is_zwj(info: &hb_glyph_info_t) -> bool {
549 _hb_glyph_info_is_unicode_format(info)
550 && (info.unicode_props() & UnicodeProps::CF_ZWJ.bits() != 0)
551}
552
553const IS_LIG_BASE: u8 = 0x10;
598
599#[inline]
600pub(crate) fn _hb_glyph_info_set_lig_props_for_ligature(
601 info: &mut hb_glyph_info_t,
602 lig_id: u8,
603 lig_num_comps: u8,
604) {
605 info.set_lig_props((lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F));
606}
607
608#[inline]
609pub(crate) fn _hb_glyph_info_set_lig_props_for_mark(
610 info: &mut hb_glyph_info_t,
611 lig_id: u8,
612 lig_comp: u8,
613) {
614 info.set_lig_props((lig_id << 5) | (lig_comp & 0x0F));
615}
616
617#[inline]
618pub(crate) fn _hb_glyph_info_set_lig_props_for_component(info: &mut hb_glyph_info_t, comp: u8) {
619 _hb_glyph_info_set_lig_props_for_mark(info, 0, comp);
620}
621
622#[inline]
623pub(crate) fn _hb_glyph_info_get_lig_id(info: &hb_glyph_info_t) -> u8 {
624 info.lig_props() >> 5
625}
626
627#[inline]
628pub(crate) fn _hb_glyph_info_ligated_internal(info: &hb_glyph_info_t) -> bool {
629 info.lig_props() & IS_LIG_BASE != 0
630}
631
632#[inline]
633pub(crate) fn _hb_glyph_info_get_lig_comp(info: &hb_glyph_info_t) -> u8 {
634 if _hb_glyph_info_ligated_internal(info) {
635 0
636 } else {
637 info.lig_props() & 0x0F
638 }
639}
640
641#[inline]
642pub(crate) fn _hb_glyph_info_get_lig_num_comps(info: &hb_glyph_info_t) -> u8 {
643 if info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
644 && _hb_glyph_info_ligated_internal(info)
645 {
646 info.lig_props() & 0x0F
647 } else {
648 1
649 }
650}
651
652#[inline]
666pub(crate) fn _hb_glyph_info_is_base_glyph(info: &hb_glyph_info_t) -> bool {
667 info.glyph_props() & GlyphPropsFlags::BASE_GLYPH.bits() != 0
668}
669
670#[inline]
671pub(crate) fn _hb_glyph_info_is_ligature(info: &hb_glyph_info_t) -> bool {
672 info.glyph_props() & GlyphPropsFlags::LIGATURE.bits() != 0
673}
674
675#[inline]
676pub(crate) fn _hb_glyph_info_is_mark(info: &hb_glyph_info_t) -> bool {
677 info.glyph_props() & GlyphPropsFlags::MARK.bits() != 0
678}
679
680#[inline]
681pub(crate) fn _hb_glyph_info_substituted(info: &hb_glyph_info_t) -> bool {
682 info.glyph_props() & GlyphPropsFlags::SUBSTITUTED.bits() != 0
683}
684
685#[inline]
686pub(crate) fn _hb_glyph_info_ligated(info: &hb_glyph_info_t) -> bool {
687 info.glyph_props() & GlyphPropsFlags::LIGATED.bits() != 0
688}
689
690#[inline]
691pub(crate) fn _hb_glyph_info_multiplied(info: &hb_glyph_info_t) -> bool {
692 info.glyph_props() & GlyphPropsFlags::MULTIPLIED.bits() != 0
693}
694
695#[inline]
696pub(crate) fn _hb_glyph_info_ligated_and_didnt_multiply(info: &hb_glyph_info_t) -> bool {
697 _hb_glyph_info_ligated(info) && !_hb_glyph_info_multiplied(info)
698}
699
700#[inline]
701pub(crate) fn _hb_glyph_info_clear_ligated_and_multiplied(info: &mut hb_glyph_info_t) {
702 let mut n = info.glyph_props();
703 n &= !(GlyphPropsFlags::LIGATED | GlyphPropsFlags::MULTIPLIED).bits();
704 info.set_glyph_props(n);
705}
706
707#[inline]
708pub(crate) fn _hb_glyph_info_clear_substituted(info: &mut hb_glyph_info_t) {
709 let mut n = info.glyph_props();
710 n &= !GlyphPropsFlags::SUBSTITUTED.bits();
711 info.set_glyph_props(n);
712}
713
714pub fn _hb_clear_substitution_flags(
715 _: &hb_ot_shape_plan_t,
716 _: &hb_font_t,
717 buffer: &mut hb_buffer_t,
718) {
719 let len = buffer.len;
720 for info in &mut buffer.info[..len] {
721 _hb_glyph_info_clear_substituted(info);
722 }
723}