1 | use ttf_parser::opentype_layout::LookupIndex; |
2 | use ttf_parser::GlyphId; |
3 | |
4 | use super::layout::{LayoutLookup, LayoutTable}; |
5 | use super::{lookup_flags, TableIndex, MAX_NESTING_LEVEL}; |
6 | use crate::buffer::{Buffer, GlyphInfo, GlyphPropsFlags}; |
7 | use crate::{Face, Mask}; |
8 | |
9 | /// Find out whether a lookup would be applied. |
10 | pub trait WouldApply { |
11 | /// Whether the lookup would be applied. |
12 | fn would_apply(&self, ctx: &WouldApplyContext) -> bool; |
13 | } |
14 | |
15 | /// Apply a lookup. |
16 | pub trait Apply { |
17 | /// Apply the lookup. |
18 | fn apply(&self, ctx: &mut ApplyContext) -> Option<()>; |
19 | } |
20 | |
21 | pub struct WouldApplyContext<'a> { |
22 | pub glyphs: &'a [GlyphId], |
23 | pub zero_context: bool, |
24 | } |
25 | |
26 | pub 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 | |
40 | impl<'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 | |