1use ttf_parser::opentype_layout::LookupIndex;
2use ttf_parser::GlyphId;
3
4use super::layout::{LayoutLookup, LayoutTable};
5use super::{lookup_flags, TableIndex, MAX_NESTING_LEVEL};
6use crate::buffer::{Buffer, GlyphInfo, GlyphPropsFlags};
7use crate::{Face, Mask};
8
9/// Find out whether a lookup would be applied.
10pub trait WouldApply {
11 /// Whether the lookup would be applied.
12 fn would_apply(&self, ctx: &WouldApplyContext) -> bool;
13}
14
15/// Apply a lookup.
16pub trait Apply {
17 /// Apply the lookup.
18 fn apply(&self, ctx: &mut ApplyContext) -> Option<()>;
19}
20
21pub struct WouldApplyContext<'a> {
22 pub glyphs: &'a [GlyphId],
23 pub zero_context: bool,
24}
25
26pub struct ApplyContext<'a, 'b> {
27 pub table_index: TableIndex,
28 pub face: &'a Face<'b>,
29 pub buffer: &'a mut Buffer,
30 pub lookup_mask: Mask,
31 pub lookup_index: LookupIndex,
32 pub lookup_props: u32,
33 pub nesting_level_left: usize,
34 pub auto_zwnj: bool,
35 pub auto_zwj: bool,
36 pub random: bool,
37 pub random_state: u32,
38}
39
40impl<'a, 'b> ApplyContext<'a, 'b> {
41 pub fn new(table_index: TableIndex, face: &'a Face<'b>, buffer: &'a mut Buffer) -> Self {
42 Self {
43 table_index,
44 face,
45 buffer,
46 lookup_mask: 1,
47 lookup_index: u16::MAX,
48 lookup_props: 0,
49 nesting_level_left: MAX_NESTING_LEVEL,
50 auto_zwnj: true,
51 auto_zwj: true,
52 random: false,
53 random_state: 1,
54 }
55 }
56
57 pub fn random_number(&mut self) -> u32 {
58 // http://www.cplusplus.com/reference/random/minstd_rand/
59 self.random_state = self.random_state.wrapping_mul(48271) % 2147483647;
60 self.random_state
61 }
62
63 pub fn recurse(&mut self, sub_lookup_index: LookupIndex) -> Option<()> {
64 if self.nesting_level_left == 0 {
65 return None;
66 }
67
68 self.buffer.max_ops -= 1;
69 if self.buffer.max_ops < 0 {
70 return None;
71 }
72
73 self.nesting_level_left -= 1;
74 let saved_props = self.lookup_props;
75 let saved_index = self.lookup_index;
76
77 self.lookup_index = sub_lookup_index;
78 let applied = match self.table_index {
79 TableIndex::GSUB => self
80 .face
81 .gsub
82 .as_ref()
83 .and_then(|table| table.get_lookup(sub_lookup_index))
84 .and_then(|lookup| {
85 self.lookup_props = lookup.props();
86 lookup.apply(self)
87 }),
88 TableIndex::GPOS => self
89 .face
90 .gpos
91 .as_ref()
92 .and_then(|table| table.get_lookup(sub_lookup_index))
93 .and_then(|lookup| {
94 self.lookup_props = lookup.props();
95 lookup.apply(self)
96 }),
97 };
98
99 self.lookup_props = saved_props;
100 self.lookup_index = saved_index;
101 self.nesting_level_left += 1;
102
103 applied
104 }
105
106 pub fn check_glyph_property(&self, info: &GlyphInfo, match_props: u32) -> bool {
107 let glyph_props = info.glyph_props();
108
109 // Lookup flags are lower 16-bit of match props.
110 let lookup_flags = match_props as u16;
111
112 // Not covered, if, for example, glyph class is ligature and
113 // match_props includes LookupFlags::IgnoreLigatures
114 if glyph_props & lookup_flags & lookup_flags::IGNORE_FLAGS != 0 {
115 return false;
116 }
117
118 if glyph_props & GlyphPropsFlags::MARK.bits() != 0 {
119 // If using mark filtering sets, the high short of
120 // match_props has the set index.
121 if lookup_flags & lookup_flags::USE_MARK_FILTERING_SET != 0 {
122 let set_index = (match_props >> 16) as u16;
123 if let Some(table) = self.face.tables().gdef {
124 return table.is_mark_glyph(info.as_glyph(), Some(set_index));
125 } else {
126 return false;
127 }
128 }
129
130 // The second byte of match_props has the meaning
131 // "ignore marks of attachment type different than
132 // the attachment type specified."
133 if lookup_flags & lookup_flags::MARK_ATTACHMENT_TYPE_MASK != 0 {
134 return (lookup_flags & lookup_flags::MARK_ATTACHMENT_TYPE_MASK)
135 == (glyph_props & lookup_flags::MARK_ATTACHMENT_TYPE_MASK);
136 }
137 }
138
139 true
140 }
141
142 fn set_glyph_class(
143 &mut self,
144 glyph_id: GlyphId,
145 class_guess: GlyphPropsFlags,
146 ligature: bool,
147 component: bool,
148 ) {
149 let cur = self.buffer.cur_mut(0);
150 let mut props = cur.glyph_props();
151
152 props |= GlyphPropsFlags::SUBSTITUTED.bits();
153
154 if ligature {
155 props |= GlyphPropsFlags::LIGATED.bits();
156 // In the only place that the MULTIPLIED bit is used, Uniscribe
157 // seems to only care about the "last" transformation between
158 // Ligature and Multiple substitutions. Ie. if you ligate, expand,
159 // and ligate again, it forgives the multiplication and acts as
160 // if only ligation happened. As such, clear MULTIPLIED bit.
161 props &= !GlyphPropsFlags::MULTIPLIED.bits();
162 }
163
164 if component {
165 props |= GlyphPropsFlags::MULTIPLIED.bits();
166 }
167
168 let has_glyph_classes = self
169 .face
170 .tables()
171 .gdef
172 .map_or(false, |table| table.has_glyph_classes());
173
174 if has_glyph_classes {
175 props &= GlyphPropsFlags::PRESERVE.bits();
176 props = (props & !GlyphPropsFlags::CLASS_MASK.bits()) | self.face.glyph_props(glyph_id);
177 } else if !class_guess.is_empty() {
178 props &= GlyphPropsFlags::PRESERVE.bits();
179 props = (props & !GlyphPropsFlags::CLASS_MASK.bits()) | class_guess.bits();
180 } else {
181 props = props & !GlyphPropsFlags::CLASS_MASK.bits();
182 }
183
184 cur.set_glyph_props(props);
185 }
186
187 pub fn replace_glyph(&mut self, glyph_id: GlyphId) {
188 self.set_glyph_class(glyph_id, GlyphPropsFlags::empty(), false, false);
189 self.buffer.replace_glyph(u32::from(glyph_id.0));
190 }
191
192 pub fn replace_glyph_inplace(&mut self, glyph_id: GlyphId) {
193 self.set_glyph_class(glyph_id, GlyphPropsFlags::empty(), false, false);
194 self.buffer.cur_mut(0).glyph_id = u32::from(glyph_id.0);
195 }
196
197 pub fn replace_glyph_with_ligature(&mut self, glyph_id: GlyphId, class_guess: GlyphPropsFlags) {
198 self.set_glyph_class(glyph_id, class_guess, true, false);
199 self.buffer.replace_glyph(u32::from(glyph_id.0));
200 }
201
202 pub fn output_glyph_for_component(&mut self, glyph_id: GlyphId, class_guess: GlyphPropsFlags) {
203 self.set_glyph_class(glyph_id, class_guess, false, true);
204 self.buffer.output_glyph(u32::from(glyph_id.0));
205 }
206}
207