1use alloc::vec::Vec;
2use core::ops::Range;
3
4use ttf_parser::opentype_layout::{
5 FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex, VariationIndex,
6};
7
8use super::{LayoutTableExt, TableIndex};
9use crate::buffer::{glyph_flag, Buffer};
10use crate::plan::ShapePlan;
11use crate::{tag, Face, Language, Mask, Script, Tag};
12
13pub struct Map {
14 found_script: [bool; 2],
15 chosen_script: [Option<Tag>; 2],
16 global_mask: Mask,
17 features: Vec<FeatureMap>,
18 lookups: [Vec<LookupMap>; 2],
19 stages: [Vec<StageMap>; 2],
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
23pub struct FeatureMap {
24 tag: Tag,
25 // GSUB/GPOS
26 index: [Option<FeatureIndex>; 2],
27 stage: [usize; 2],
28 shift: u32,
29 mask: Mask,
30 // mask for value=1, for quick access
31 one_mask: Mask,
32 auto_zwnj: bool,
33 auto_zwj: bool,
34 random: bool,
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
38pub struct LookupMap {
39 pub index: LookupIndex,
40 // TODO: to bitflags
41 pub auto_zwnj: bool,
42 pub auto_zwj: bool,
43 pub random: bool,
44 pub mask: Mask,
45}
46
47#[derive(Clone, Copy)]
48pub struct StageMap {
49 // Cumulative
50 pub last_lookup: usize,
51 pub pause_func: Option<PauseFunc>,
52}
53
54pub type PauseFunc = fn(&ShapePlan, &Face, &mut Buffer);
55
56impl Map {
57 pub const MAX_BITS: u32 = 8;
58 pub const MAX_VALUE: u32 = (1 << Self::MAX_BITS) - 1;
59
60 #[inline]
61 pub fn found_script(&self, table_index: TableIndex) -> bool {
62 self.found_script[table_index]
63 }
64
65 #[inline]
66 pub fn chosen_script(&self, table_index: TableIndex) -> Option<Tag> {
67 self.chosen_script[table_index]
68 }
69
70 #[inline]
71 pub fn global_mask(&self) -> Mask {
72 self.global_mask
73 }
74
75 #[inline]
76 pub fn mask(&self, feature_tag: Tag) -> (Mask, u32) {
77 self.features
78 .binary_search_by_key(&feature_tag, |f| f.tag)
79 .map_or((0, 0), |idx| {
80 (self.features[idx].mask, self.features[idx].shift)
81 })
82 }
83
84 #[inline]
85 pub fn one_mask(&self, feature_tag: Tag) -> Mask {
86 self.features
87 .binary_search_by_key(&feature_tag, |f| f.tag)
88 .map_or(0, |idx| self.features[idx].one_mask)
89 }
90
91 #[inline]
92 pub fn feature_index(&self, table_index: TableIndex, feature_tag: Tag) -> Option<FeatureIndex> {
93 self.features
94 .binary_search_by_key(&feature_tag, |f| f.tag)
95 .ok()
96 .and_then(|idx| self.features[idx].index[table_index])
97 }
98
99 #[inline]
100 pub fn feature_stage(&self, table_index: TableIndex, feature_tag: Tag) -> Option<usize> {
101 self.features
102 .binary_search_by_key(&feature_tag, |f| f.tag)
103 .map(|idx| self.features[idx].stage[table_index])
104 .ok()
105 }
106
107 #[inline]
108 pub fn stages(&self, table_index: TableIndex) -> &[StageMap] {
109 &self.stages[table_index]
110 }
111
112 #[inline]
113 pub fn lookup(&self, table_index: TableIndex, index: usize) -> &LookupMap {
114 &self.lookups[table_index][index]
115 }
116
117 #[inline]
118 pub fn stage_lookups(&self, table_index: TableIndex, stage: usize) -> &[LookupMap] {
119 &self.lookups[table_index][self.stage_lookup_range(table_index, stage)]
120 }
121
122 #[inline]
123 pub fn stage_lookup_range(&self, table_index: TableIndex, stage: usize) -> Range<usize> {
124 let stages = &self.stages[table_index];
125 let lookups = &self.lookups[table_index];
126 let start = stage
127 .checked_sub(1)
128 .map_or(0, |prev| stages[prev].last_lookup);
129 let end = stages
130 .get(stage)
131 .map_or(lookups.len(), |curr| curr.last_lookup);
132 start..end
133 }
134}
135
136bitflags::bitflags! {
137 /// Flags used for serialization with a `BufferSerializer`.
138 #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
139 pub struct FeatureFlags: u32 {
140 /// Feature applies to all characters; results in no mask allocated for it.
141 const GLOBAL = 0x01;
142 /// Has fallback implementation, so include mask bit even if feature not found.
143 const HAS_FALLBACK = 0x02;
144 /// Don't skip over ZWNJ when matching **context**.
145 const MANUAL_ZWNJ = 0x04;
146 /// Don't skip over ZWJ when matching **input**.
147 const MANUAL_ZWJ = 0x08;
148 /// If feature not found in LangSys, look for it in global feature list and pick one.
149 const GLOBAL_SEARCH = 0x10;
150 /// Randomly select a glyph from an AlternateSubstFormat1 subtable.
151 const RANDOM = 0x20;
152
153 const MANUAL_JOINERS = Self::MANUAL_ZWNJ.bits() | Self::MANUAL_ZWJ.bits();
154 const GLOBAL_MANUAL_JOINERS = Self::GLOBAL.bits() | Self::MANUAL_JOINERS.bits();
155 const GLOBAL_HAS_FALLBACK = Self::GLOBAL.bits() | Self::HAS_FALLBACK.bits();
156 }
157}
158
159pub struct MapBuilder<'a> {
160 face: &'a Face<'a>,
161 found_script: [bool; 2],
162 script_index: [Option<ScriptIndex>; 2],
163 chosen_script: [Option<Tag>; 2],
164 lang_index: [Option<LanguageIndex>; 2],
165 current_stage: [usize; 2],
166 feature_infos: Vec<FeatureInfo>,
167 stages: [Vec<StageInfo>; 2],
168}
169
170#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
171struct FeatureInfo {
172 tag: Tag,
173 // sequence number, used for stable sorting only
174 seq: usize,
175 max_value: u32,
176 flags: FeatureFlags,
177 // for non-global features, what should the unset glyphs take
178 default_value: u32,
179 // GSUB/GPOS
180 stage: [usize; 2],
181}
182
183#[derive(Clone, Copy)]
184struct StageInfo {
185 index: usize,
186 pause_func: Option<PauseFunc>,
187}
188
189impl<'a> MapBuilder<'a> {
190 pub fn new(face: &'a Face<'a>, script: Option<Script>, language: Option<&Language>) -> Self {
191 // Fetch script/language indices for GSUB/GPOS. We need these later to skip
192 // features not available in either table and not waste precious bits for them.
193 let (script_tags, lang_tags) = tag::tags_from_script_and_language(script, language);
194
195 let mut found_script = [false; 2];
196 let mut script_index = [None; 2];
197 let mut chosen_script = [None; 2];
198 let mut lang_index = [None; 2];
199
200 for (table_index, table) in face.layout_tables() {
201 if let Some((found, idx, tag)) = table.select_script(&script_tags) {
202 chosen_script[table_index] = Some(tag);
203 found_script[table_index] = found;
204 script_index[table_index] = Some(idx);
205
206 if let Some(idx) = table.select_script_language(idx, &lang_tags) {
207 lang_index[table_index] = Some(idx);
208 }
209 }
210 }
211
212 Self {
213 face,
214 found_script,
215 script_index,
216 chosen_script,
217 lang_index,
218 current_stage: [0, 0],
219 feature_infos: Vec::new(),
220 stages: [Vec::new(), Vec::new()],
221 }
222 }
223
224 #[inline]
225 pub fn chosen_script(&self, table_index: TableIndex) -> Option<Tag> {
226 self.chosen_script[table_index]
227 }
228
229 #[inline]
230 pub fn add_feature(&mut self, tag: Tag, flags: FeatureFlags, value: u32) {
231 if !tag.is_null() {
232 let seq = self.feature_infos.len();
233 self.feature_infos.push(FeatureInfo {
234 tag,
235 seq,
236 max_value: value,
237 flags,
238 default_value: if flags.contains(FeatureFlags::GLOBAL) {
239 value
240 } else {
241 0
242 },
243 stage: self.current_stage,
244 });
245 }
246 }
247
248 #[inline]
249 pub fn enable_feature(&mut self, tag: Tag, flags: FeatureFlags, value: u32) {
250 self.add_feature(tag, flags | FeatureFlags::GLOBAL, value);
251 }
252
253 #[inline]
254 pub fn disable_feature(&mut self, tag: Tag) {
255 self.add_feature(tag, FeatureFlags::GLOBAL, 0);
256 }
257
258 #[inline]
259 pub fn add_gsub_pause(&mut self, pause: Option<PauseFunc>) {
260 self.add_pause(TableIndex::GSUB, pause);
261 }
262
263 #[inline]
264 pub fn add_gpos_pause(&mut self, pause: Option<PauseFunc>) {
265 self.add_pause(TableIndex::GPOS, pause);
266 }
267
268 fn add_pause(&mut self, table_index: TableIndex, pause: Option<PauseFunc>) {
269 self.stages[table_index].push(StageInfo {
270 index: self.current_stage[table_index],
271 pause_func: pause,
272 });
273
274 self.current_stage[table_index] += 1;
275 }
276
277 const GLOBAL_BIT_MASK: Mask = glyph_flag::DEFINED + 1;
278 const GLOBAL_BIT_SHIFT: u32 = glyph_flag::DEFINED.count_ones();
279
280 pub fn compile(&mut self) -> Map {
281 // We default to applying required feature in stage 0. If the required
282 // feature has a tag that is known to the shaper, we apply required feature
283 // in the stage for that tag.
284 let mut required_index = [None; 2];
285 let mut required_tag = [None; 2];
286
287 for (table_index, table) in self.face.layout_tables() {
288 if let Some(script) = self.script_index[table_index] {
289 let lang = self.lang_index[table_index];
290 if let Some((idx, tag)) = table.get_required_language_feature(script, lang) {
291 required_index[table_index] = Some(idx);
292 required_tag[table_index] = Some(tag);
293 }
294 }
295 }
296
297 let (features, required_stage, global_mask) = self.collect_feature_maps(required_tag);
298
299 self.add_gsub_pause(None);
300 self.add_gpos_pause(None);
301
302 let (lookups, stages) =
303 self.collect_lookup_stages(&features, required_index, required_stage);
304
305 Map {
306 found_script: self.found_script,
307 chosen_script: self.chosen_script,
308 global_mask,
309 features,
310 lookups,
311 stages,
312 }
313 }
314
315 fn collect_feature_maps(
316 &mut self,
317 required_tag: [Option<Tag>; 2],
318 ) -> (Vec<FeatureMap>, [usize; 2], Mask) {
319 let mut map_features = Vec::new();
320 let mut required_stage = [0; 2];
321 let mut global_mask = Self::GLOBAL_BIT_MASK;
322 let mut next_bit = Self::GLOBAL_BIT_SHIFT + 1;
323
324 // Sort features and merge duplicates.
325 self.dedup_feature_infos();
326
327 for info in &self.feature_infos {
328 let bits_needed = if info.flags.contains(FeatureFlags::GLOBAL) && info.max_value == 1 {
329 // Uses the global bit.
330 0
331 } else {
332 // Limit bits per feature.
333 let v = info.max_value;
334 let num_bits = 8 * core::mem::size_of_val(&v) as u32 - v.leading_zeros();
335 Map::MAX_BITS.min(num_bits)
336 };
337
338 let bits_available = 8 * core::mem::size_of::<Mask>() as u32;
339 if info.max_value == 0 || next_bit + bits_needed > bits_available {
340 // Feature disabled, or not enough bits.
341 continue;
342 }
343
344 let mut found = false;
345 let mut feature_index = [None; 2];
346
347 for (table_index, table) in self.face.layout_tables() {
348 if required_tag[table_index] == Some(info.tag) {
349 required_stage[table_index] = info.stage[table_index];
350 }
351
352 if let Some(script) = self.script_index[table_index] {
353 let lang = self.lang_index[table_index];
354 if let Some(idx) = table.find_language_feature(script, lang, info.tag) {
355 feature_index[table_index] = Some(idx);
356 found = true;
357 }
358 }
359 }
360
361 if !found && info.flags.contains(FeatureFlags::GLOBAL_SEARCH) {
362 for (table_index, table) in self.face.layout_tables() {
363 if let Some(idx) = table.features.index(info.tag) {
364 feature_index[table_index] = Some(idx);
365 found = true;
366 }
367 }
368 }
369
370 if !found && !info.flags.contains(FeatureFlags::HAS_FALLBACK) {
371 continue;
372 }
373
374 let (shift, mask) = if info.flags.contains(FeatureFlags::GLOBAL) && info.max_value == 1
375 {
376 // Uses the global bit
377 (Self::GLOBAL_BIT_SHIFT, Self::GLOBAL_BIT_MASK)
378 } else {
379 let shift = next_bit;
380 let mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
381 next_bit += bits_needed;
382 global_mask |= (info.default_value << shift) & mask;
383 (shift, mask)
384 };
385
386 map_features.push(FeatureMap {
387 tag: info.tag,
388 index: feature_index,
389 stage: info.stage,
390 shift,
391 mask,
392 one_mask: (1 << shift) & mask,
393 auto_zwnj: !info.flags.contains(FeatureFlags::MANUAL_ZWNJ),
394 auto_zwj: !info.flags.contains(FeatureFlags::MANUAL_ZWJ),
395 random: info.flags.contains(FeatureFlags::RANDOM),
396 });
397 }
398
399 (map_features, required_stage, global_mask)
400 }
401
402 fn dedup_feature_infos(&mut self) {
403 let feature_infos = &mut self.feature_infos;
404 if feature_infos.is_empty() {
405 return;
406 }
407
408 feature_infos.sort();
409
410 let mut j = 0;
411 for i in 1..feature_infos.len() {
412 if feature_infos[i].tag != feature_infos[j].tag {
413 j += 1;
414 feature_infos[j] = feature_infos[i];
415 } else {
416 if feature_infos[i].flags.contains(FeatureFlags::GLOBAL) {
417 feature_infos[j].flags |= FeatureFlags::GLOBAL;
418 feature_infos[j].max_value = feature_infos[i].max_value;
419 feature_infos[j].default_value = feature_infos[i].default_value;
420 } else {
421 if feature_infos[j].flags.contains(FeatureFlags::GLOBAL) {
422 feature_infos[j].flags ^= FeatureFlags::GLOBAL;
423 }
424 feature_infos[j].max_value =
425 feature_infos[j].max_value.max(feature_infos[i].max_value);
426 // Inherit default_value from j
427 }
428 let flags = feature_infos[i].flags & FeatureFlags::HAS_FALLBACK;
429 feature_infos[j].flags |= flags;
430 feature_infos[j].stage[0] =
431 feature_infos[j].stage[0].min(feature_infos[i].stage[0]);
432 feature_infos[j].stage[1] =
433 feature_infos[j].stage[1].min(feature_infos[i].stage[1]);
434 }
435 }
436
437 feature_infos.truncate(j + 1);
438 }
439
440 fn collect_lookup_stages(
441 &self,
442 map_features: &[FeatureMap],
443 required_feature_index: [Option<FeatureIndex>; 2],
444 required_feature_stage: [usize; 2],
445 ) -> ([Vec<LookupMap>; 2], [Vec<StageMap>; 2]) {
446 let mut map_lookups = [Vec::new(), Vec::new()];
447 let mut map_stages = [Vec::new(), Vec::new()];
448
449 for table_index in TableIndex::iter() {
450 // Collect lookup indices for features.
451 let mut stage_index = 0;
452 let mut last_lookup = 0;
453
454 let coords = self.face.ttfp_face.variation_coordinates();
455 let variation_index = self
456 .face
457 .layout_table(table_index)
458 .and_then(|t| t.variations?.find_index(coords));
459
460 for stage in 0..self.current_stage[table_index] {
461 if let Some(feature_index) = required_feature_index[table_index] {
462 if required_feature_stage[table_index] == stage {
463 self.add_lookups(
464 &mut map_lookups[table_index],
465 table_index,
466 feature_index,
467 variation_index,
468 Self::GLOBAL_BIT_MASK,
469 true,
470 true,
471 false,
472 );
473 }
474 }
475
476 for feature in map_features {
477 if let Some(feature_index) = feature.index[table_index] {
478 if feature.stage[table_index] == stage {
479 self.add_lookups(
480 &mut map_lookups[table_index],
481 table_index,
482 feature_index,
483 variation_index,
484 feature.mask,
485 feature.auto_zwnj,
486 feature.auto_zwj,
487 feature.random,
488 );
489 }
490 }
491 }
492
493 // Sort lookups and merge duplicates.
494 let lookups = &mut map_lookups[table_index];
495 let len = lookups.len();
496
497 if last_lookup < len {
498 lookups[last_lookup..].sort();
499
500 let mut j = last_lookup;
501 for i in j + 1..len {
502 if lookups[i].index != lookups[j].index {
503 j += 1;
504 lookups[j] = lookups[i];
505 } else {
506 lookups[j].mask |= lookups[i].mask;
507 lookups[j].auto_zwnj &= lookups[i].auto_zwnj;
508 lookups[j].auto_zwj &= lookups[i].auto_zwj;
509 }
510 }
511
512 lookups.truncate(j + 1);
513 }
514
515 last_lookup = lookups.len();
516
517 if let Some(info) = self.stages[table_index].get(stage_index) {
518 if info.index == stage {
519 map_stages[table_index].push(StageMap {
520 last_lookup,
521 pause_func: info.pause_func,
522 });
523
524 stage_index += 1;
525 }
526 }
527 }
528 }
529
530 (map_lookups, map_stages)
531 }
532
533 fn add_lookups(
534 &self,
535 lookups: &mut Vec<LookupMap>,
536 table_index: TableIndex,
537 feature_index: FeatureIndex,
538 variation_index: Option<VariationIndex>,
539 mask: Mask,
540 auto_zwnj: bool,
541 auto_zwj: bool,
542 random: bool,
543 ) -> Option<()> {
544 let table = self.face.layout_table(table_index)?;
545
546 let lookup_count = table.lookups.len();
547 let feature = match variation_index {
548 Some(idx) => table
549 .variations
550 .and_then(|var| var.find_substitute(feature_index, idx))
551 .or_else(|| table.features.get(feature_index))?,
552 None => table.features.get(feature_index)?,
553 };
554
555 for index in feature.lookup_indices {
556 if index < lookup_count {
557 lookups.push(LookupMap {
558 mask,
559 index,
560 auto_zwnj,
561 auto_zwj,
562 random,
563 });
564 }
565 }
566
567 Some(())
568 }
569}
570