1use super::{aat, at};
2
3use super::buffer::*;
4use super::internal::{self, at::Gdef, raw_tag, Bytes, RawFont, RawTag};
5use crate::font::FontRef;
6use crate::text::{Language, Script};
7
8use alloc::vec::Vec;
9use core::ops::Range;
10
11pub struct Engine<'a> {
14 pub data: Bytes<'a>,
15 pub gdef: Gdef<'a>,
16 pub gsub: at::StageOffsets,
17 pub gpos: at::StageOffsets,
18 pub morx: u32,
19 pub kerx: u32,
20 pub ankr: u32,
21 pub kern: u32,
22 pub storage: at::Storage,
23 pub coords: &'a [i16],
24 pub script: Script,
25 pub tags: [RawTag; 4],
26 pub sub_mode: SubMode,
27 pub pos_mode: PosMode,
28 pub use_ot: bool,
29 pub mode: EngineMode,
30}
31
32impl<'a> Engine<'a> {
33 pub fn new(
35 metadata: &EngineMetadata,
36 font_data: &'a [u8],
37 coords: &'a [i16],
38 script: Script,
39 lang: Option<Language>,
40 ) -> Self {
41 let data = Bytes::new(font_data);
42 let gdef = Gdef::from_offset(font_data, metadata.gdef).unwrap_or_else(Gdef::empty);
43 let script_tag = script.to_opentype();
44 let lang_tag = lang.and_then(|l| l.to_opentype());
45 let (gsub, stags) = if metadata.sub_mode == SubMode::Gsub {
46 at::StageOffsets::new(&data, metadata.gsub, script_tag, lang_tag).unwrap_or_default()
47 } else {
48 (at::StageOffsets::default(), [0, 0])
49 };
50 let (gpos, ptags) = if metadata.pos_mode == PosMode::Gpos {
51 at::StageOffsets::new(&data, metadata.gpos, script_tag, lang_tag).unwrap_or_default()
52 } else {
53 (at::StageOffsets::default(), [0, 0])
54 };
55 let tags = [stags[0], stags[1], ptags[0], ptags[1]];
56 let use_ot = gsub.lang != 0 || gpos.lang != 0;
57 let mode = if gsub.lang != 0 && script.is_complex() {
58 if script == Script::Myanmar {
59 EngineMode::Myanmar
60 } else {
61 EngineMode::Complex
62 }
63 } else {
64 EngineMode::Simple
65 };
66 let mut sub_mode = metadata.sub_mode;
67 let mut pos_mode = metadata.pos_mode;
68 if sub_mode == SubMode::Gsub && gsub.lang == 0 {
69 sub_mode = SubMode::None;
70 }
71 if pos_mode == PosMode::Gpos && gpos.lang == 0 {
72 pos_mode = PosMode::None;
73 }
74 Self {
75 data,
76 gdef,
77 gsub,
78 gpos,
79 morx: metadata.morx,
80 kerx: metadata.kerx,
81 ankr: metadata.ankr,
82 kern: metadata.kern,
83 storage: at::Storage::default(),
84 coords,
85 script,
86 tags,
87 sub_mode,
88 pos_mode,
89 use_ot,
90 mode,
91 }
92 }
93}
94
95impl<'a> Engine<'a> {
97 pub fn tags(&self) -> &[RawTag; 4] {
100 &self.tags
101 }
102
103 pub fn collect_features(
105 &self,
106 builder: &mut at::FeatureStoreBuilder,
107 store: &mut at::FeatureStore,
108 ) {
109 builder.build(
110 store,
111 self.data.data(),
112 self.coords,
113 &self.gdef,
114 &self.gsub,
115 &self.gpos,
116 );
117 store.groups = store.groups(self.script);
118 }
119
120 pub fn has_feature_vars(&self) -> bool {
122 self.gsub.var != 0 || self.gpos.var != 0
123 }
124
125 pub fn set_classes(&self, buffer: &mut Buffer, range: Option<Range<usize>>) {
127 if !self.gdef.ok() {
128 return;
129 }
130 let slice = if let Some(range) = range {
131 &mut buffer.glyphs[range]
132 } else {
133 &mut buffer.glyphs[..]
134 };
135 let gdef = &self.gdef;
136 if gdef.has_mark_classes() {
137 for g in slice.iter_mut() {
138 g.class = gdef.class(g.id) as u8;
139 g.mark_type = gdef.mark_class(g.id) as u8;
140 }
141 } else {
142 for g in slice.iter_mut() {
143 g.class = gdef.class(g.id) as u8;
144 }
145 }
146 }
147
148 pub fn gsub(
150 &mut self,
151 store: &at::FeatureStore,
152 feature_mask: impl Into<at::FeatureMask>,
153 buffer: &mut Buffer,
154 buffer_range: Option<Range<usize>>,
155 ) -> bool {
156 at::apply(
157 0,
158 &self.data,
159 self.gsub.base,
160 self.coords,
161 &self.gdef,
162 &mut self.storage,
163 store,
164 feature_mask.into(),
165 buffer,
166 buffer_range,
167 ) == Some(true)
168 }
169
170 pub fn gpos(
172 &mut self,
173 store: &at::FeatureStore,
174 feature_mask: impl Into<at::FeatureMask>,
175 buffer: &mut Buffer,
176 buffer_range: Option<Range<usize>>,
177 ) -> bool {
178 at::apply(
179 1,
180 &self.data,
181 self.gpos.base,
182 self.coords,
183 &self.gdef,
184 &mut self.storage,
185 store,
186 feature_mask.into(),
187 buffer,
188 buffer_range,
189 ) == Some(true)
190 }
191}
192
193impl<'a> Engine<'a> {
195 pub fn collect_selectors(&self, features: &[(RawTag, u16)], selectors: &mut Vec<(u16, u16)>) {
197 use internal::aat::morx::feature_from_tag;
198 selectors.clear();
199 for (tag, value) in features {
200 if let Some((selector, [on, off])) = feature_from_tag(*tag) {
201 let setting = if *value == 0 { off } else { on };
202 selectors.push((selector, setting));
203 }
204 }
205 selectors.sort_unstable();
206 }
207
208 pub fn morx(&self, buffer: &mut Buffer, selectors: &[(u16, u16)]) {
210 if self.morx != 0 {
211 aat::apply_morx(self.data.data(), self.morx, buffer, selectors);
212 buffer.ensure_order(false);
213 }
214 }
215
216 pub fn kerx(&self, buffer: &mut Buffer, disable_kern: bool) {
218 if self.kerx != 0 {
219 aat::apply_kerx(self.data.data(), self.kerx, self.ankr, buffer, disable_kern);
220 buffer.ensure_order(false);
221 }
222 }
223
224 pub fn kern(&self, buffer: &mut Buffer) {
226 if self.kern != 0 {
227 aat::apply_kern(self.data.data(), self.kern, buffer);
228 }
229 }
230}
231
232#[derive(Copy, Clone, PartialEq, Eq, Debug)]
235pub enum EngineMode {
236 Simple,
237 Myanmar,
238 Complex,
239}
240
241#[derive(Copy, Clone, PartialEq, Eq, Debug)]
243pub enum SubMode {
244 None,
245 Gsub,
246 Morx,
247}
248
249#[derive(Copy, Clone, PartialEq, Eq, Debug)]
251pub enum PosMode {
252 None,
253 Gpos,
254 Kerx,
255 Kern,
256}
257
258#[derive(Copy, Clone)]
260pub struct EngineMetadata {
261 pub gdef: u32,
262 pub gsub: u32,
263 pub gpos: u32,
264 pub morx: u32,
265 pub kerx: u32,
266 pub ankr: u32,
267 pub kern: u32,
268 pub sub_mode: SubMode,
269 pub pos_mode: PosMode,
270}
271
272impl EngineMetadata {
273 pub fn from_font(font: &FontRef) -> Self {
274 let mut this = Self {
275 gdef: font.table_offset(raw_tag(b"GDEF")),
276 gsub: font.table_offset(raw_tag(b"GSUB")),
277 gpos: font.table_offset(raw_tag(b"GPOS")),
278 morx: font.table_offset(raw_tag(b"morx")),
279 kerx: font.table_offset(raw_tag(b"kerx")),
281 ankr: font.table_offset(raw_tag(b"ankr")),
282 kern: font.table_offset(raw_tag(b"kern")),
283 sub_mode: SubMode::None,
284 pos_mode: PosMode::None,
285 };
286 if this.gsub != 0 {
287 this.sub_mode = SubMode::Gsub;
288 } else if this.morx != 0 {
289 this.sub_mode = SubMode::Morx;
290 }
291 if this.gpos != 0 {
292 this.pos_mode = PosMode::Gpos;
293 } else if this.kerx != 0 {
294 this.pos_mode = PosMode::Kerx;
295 } else if this.kern != 0 {
296 this.pos_mode = PosMode::Kern;
297 }
298 this
299 }
300}