1use alloc::vec::Vec;
2use core::cmp::Ordering;
3use core::ops::Range;
4use ttf_parser::FromData;
5
6use ttf_parser::opentype_layout::{
7 FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex, VariationIndex,
8};
9
10use super::buffer::{glyph_flag, hb_buffer_t};
11use super::ot_layout::{LayoutTableExt, TableIndex};
12use super::ot_shape_plan::hb_ot_shape_plan_t;
13use super::{hb_font_t, hb_mask_t, hb_tag_t, tag, Language, Script};
14
15pub struct hb_ot_map_t {
16 found_script: [bool; 2],
17 chosen_script: [Option<hb_tag_t>; 2],
18 global_mask: hb_mask_t,
19 features: Vec<feature_map_t>,
20 lookups: [Vec<lookup_map_t>; 2],
21 stages: [Vec<StageMap>; 2],
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub struct feature_map_t {
26 tag: hb_tag_t,
27 index: [Option<FeatureIndex>; 2],
29 stage: [usize; 2],
30 shift: u32,
31 mask: hb_mask_t,
32 one_mask: hb_mask_t,
34 auto_zwnj: bool,
35 auto_zwj: bool,
36 random: bool,
37 per_syllable: bool,
38}
39
40impl Ord for feature_map_t {
41 fn cmp(&self, other: &Self) -> Ordering {
42 self.tag.cmp(&other.tag)
43 }
44}
45
46impl PartialOrd for feature_map_t {
47 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48 self.tag.partial_cmp(&other.tag)
49 }
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
53pub struct lookup_map_t {
54 pub index: LookupIndex,
55 pub auto_zwnj: bool,
57 pub auto_zwj: bool,
58 pub random: bool,
59 pub mask: hb_mask_t,
60 pub per_syllable: bool,
61}
62
63#[derive(Clone, Copy)]
64pub struct StageMap {
65 pub last_lookup: usize,
67 pub pause_func: Option<pause_func_t>,
68}
69
70pub type pause_func_t = fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t) -> bool;
73
74impl hb_ot_map_t {
75 pub const MAX_BITS: u32 = 8;
76 pub const MAX_VALUE: u32 = (1 << Self::MAX_BITS) - 1;
77
78 #[inline]
79 pub fn found_script(&self, table_index: TableIndex) -> bool {
80 self.found_script[table_index]
81 }
82
83 #[inline]
84 pub fn chosen_script(&self, table_index: TableIndex) -> Option<hb_tag_t> {
85 self.chosen_script[table_index]
86 }
87
88 #[inline]
89 pub fn get_global_mask(&self) -> hb_mask_t {
90 self.global_mask
91 }
92
93 #[inline]
94 pub fn get_mask(&self, feature_tag: hb_tag_t) -> (hb_mask_t, u32) {
95 self.features
96 .binary_search_by_key(&feature_tag, |f| f.tag)
97 .map_or((0, 0), |idx| {
98 (self.features[idx].mask, self.features[idx].shift)
99 })
100 }
101
102 #[inline]
103 pub fn get_1_mask(&self, feature_tag: hb_tag_t) -> hb_mask_t {
104 self.features
105 .binary_search_by_key(&feature_tag, |f| f.tag)
106 .map_or(0, |idx| self.features[idx].one_mask)
107 }
108
109 #[inline]
110 pub fn get_feature_index(
111 &self,
112 table_index: TableIndex,
113 feature_tag: hb_tag_t,
114 ) -> Option<FeatureIndex> {
115 self.features
116 .binary_search_by_key(&feature_tag, |f| f.tag)
117 .ok()
118 .and_then(|idx| self.features[idx].index[table_index])
119 }
120
121 #[inline]
122 pub fn get_feature_stage(
123 &self,
124 table_index: TableIndex,
125 feature_tag: hb_tag_t,
126 ) -> Option<usize> {
127 self.features
128 .binary_search_by_key(&feature_tag, |f| f.tag)
129 .map(|idx| self.features[idx].stage[table_index])
130 .ok()
131 }
132
133 #[inline]
134 pub fn stages(&self, table_index: TableIndex) -> &[StageMap] {
135 &self.stages[table_index]
136 }
137
138 #[inline]
139 pub fn lookup(&self, table_index: TableIndex, index: usize) -> &lookup_map_t {
140 &self.lookups[table_index][index]
141 }
142
143 #[inline]
144 pub fn stage_lookups(&self, table_index: TableIndex, stage: usize) -> &[lookup_map_t] {
145 &self.lookups[table_index][self.stage_lookup_range(table_index, stage)]
146 }
147
148 #[inline]
149 pub fn stage_lookup_range(&self, table_index: TableIndex, stage: usize) -> Range<usize> {
150 let stages = &self.stages[table_index];
151 let lookups = &self.lookups[table_index];
152 let start = stage
153 .checked_sub(1)
154 .map_or(0, |prev| stages[prev].last_lookup);
155 let end = stages
156 .get(stage)
157 .map_or(lookups.len(), |curr| curr.last_lookup);
158 start..end
159 }
160}
161
162pub type hb_ot_map_feature_flags_t = u32;
163pub const F_NONE: u32 = 0x0000;
164pub const F_GLOBAL: u32 = 0x0001; pub const F_HAS_FALLBACK: u32 = 0x0002; pub const F_MANUAL_ZWNJ: u32 = 0x0004; pub const F_MANUAL_ZWJ: u32 = 0x0008; pub const F_MANUAL_JOINERS: u32 = F_MANUAL_ZWNJ | F_MANUAL_ZWJ;
169pub const F_GLOBAL_MANUAL_JOINERS: u32 = F_GLOBAL | F_MANUAL_JOINERS;
170pub const F_GLOBAL_HAS_FALLBACK: u32 = F_GLOBAL | F_HAS_FALLBACK;
171pub const F_GLOBAL_SEARCH: u32 = 0x0010; pub const F_RANDOM: u32 = 0x0020; pub const F_PER_SYLLABLE: u32 = 0x0040; pub struct hb_ot_map_builder_t<'a> {
176 face: &'a hb_font_t<'a>,
177 found_script: [bool; 2],
178 script_index: [Option<ScriptIndex>; 2],
179 chosen_script: [Option<hb_tag_t>; 2],
180 lang_index: [Option<LanguageIndex>; 2],
181 current_stage: [usize; 2],
182 feature_infos: Vec<feature_info_t>,
183 stages: [Vec<stage_info_t>; 2],
184 pub(crate) is_simple: bool,
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
188struct feature_info_t {
189 tag: hb_tag_t,
190 seq: usize,
192 max_value: u32,
193 flags: hb_ot_map_feature_flags_t,
194 default_value: u32,
196 stage: [usize; 2],
198}
199
200#[derive(Clone, Copy)]
201struct stage_info_t {
202 index: usize,
203 pause_func: Option<pause_func_t>,
204}
205
206const GLOBAL_BIT_SHIFT: u32 = 8 * u32::SIZE as u32 - 1;
207const GLOBAL_BIT_MASK: hb_mask_t = 1 << GLOBAL_BIT_SHIFT;
208
209impl<'a> hb_ot_map_builder_t<'a> {
210 pub fn new(
211 face: &'a hb_font_t<'a>,
212 script: Option<Script>,
213 language: Option<&Language>,
214 ) -> Self {
215 let (script_tags, lang_tags) = tag::tags_from_script_and_language(script, language);
218
219 let mut found_script = [false; 2];
220 let mut script_index = [None; 2];
221 let mut chosen_script = [None; 2];
222 let mut lang_index = [None; 2];
223
224 for (table_index, table) in face.layout_tables() {
225 if let Some((found, idx, tag)) = table.select_script(&script_tags) {
226 chosen_script[table_index] = Some(tag);
227 found_script[table_index] = found;
228 script_index[table_index] = Some(idx);
229
230 if let Some(idx) = table.select_script_language(idx, &lang_tags) {
231 lang_index[table_index] = Some(idx);
232 }
233 }
234 }
235
236 Self {
237 face,
238 found_script,
239 script_index,
240 chosen_script,
241 lang_index,
242 current_stage: [0, 0],
243 feature_infos: Vec::new(),
244 stages: [Vec::new(), Vec::new()],
245 is_simple: false,
246 }
247 }
248
249 #[inline]
250 pub fn chosen_script(&self, table_index: TableIndex) -> Option<hb_tag_t> {
251 self.chosen_script[table_index]
252 }
253
254 #[inline]
255 pub fn has_feature(&self, tag: hb_tag_t) -> bool {
256 for (table_index, table) in self.face.layout_tables() {
257 if let Some(script_index) = self.script_index[table_index] {
258 if table
259 .find_language_feature(script_index, self.lang_index[table_index], tag)
260 .is_some()
261 {
262 return true;
263 }
264 }
265 }
266
267 false
268 }
269
270 #[inline]
271 pub fn add_feature(&mut self, tag: hb_tag_t, flags: hb_ot_map_feature_flags_t, value: u32) {
272 if !tag.is_null() {
273 let seq = self.feature_infos.len();
274 self.feature_infos.push(feature_info_t {
275 tag,
276 seq,
277 max_value: value,
278 flags,
279 default_value: if flags & F_GLOBAL != 0 { value } else { 0 },
280 stage: self.current_stage,
281 });
282 }
283 }
284
285 #[inline]
286 pub fn enable_feature(&mut self, tag: hb_tag_t, flags: hb_ot_map_feature_flags_t, value: u32) {
287 self.add_feature(tag, flags | F_GLOBAL, value);
288 }
289
290 #[inline]
291 pub fn disable_feature(&mut self, tag: hb_tag_t) {
292 self.add_feature(tag, F_GLOBAL, 0);
293 }
294
295 #[inline]
296 pub fn add_gsub_pause(&mut self, pause: Option<pause_func_t>) {
297 self.add_pause(TableIndex::GSUB, pause);
298 }
299
300 #[inline]
301 pub fn add_gpos_pause(&mut self, pause: Option<pause_func_t>) {
302 self.add_pause(TableIndex::GPOS, pause);
303 }
304
305 fn add_pause(&mut self, table_index: TableIndex, pause: Option<pause_func_t>) {
306 self.stages[table_index].push(stage_info_t {
307 index: self.current_stage[table_index],
308 pause_func: pause,
309 });
310
311 self.current_stage[table_index] += 1;
312 }
313
314 pub fn compile(&mut self) -> hb_ot_map_t {
315 let mut required_index = [None; 2];
319 let mut required_tag = [None; 2];
320
321 for (table_index, table) in self.face.layout_tables() {
322 if let Some(script) = self.script_index[table_index] {
323 let lang = self.lang_index[table_index];
324 if let Some((idx, tag)) = table.get_required_language_feature(script, lang) {
325 required_index[table_index] = Some(idx);
326 required_tag[table_index] = Some(tag);
327 }
328 }
329 }
330
331 let (features, required_stage, global_mask) = self.collect_feature_maps(required_tag);
332
333 self.add_gsub_pause(None);
334 self.add_gpos_pause(None);
335
336 let (lookups, stages) =
337 self.collect_lookup_stages(&features, required_index, required_stage);
338
339 hb_ot_map_t {
340 found_script: self.found_script,
341 chosen_script: self.chosen_script,
342 global_mask,
343 features,
344 lookups,
345 stages,
346 }
347 }
348
349 fn collect_feature_maps(
350 &mut self,
351 required_tag: [Option<hb_tag_t>; 2],
352 ) -> (Vec<feature_map_t>, [usize; 2], hb_mask_t) {
353 let mut map_features = Vec::new();
354 let mut required_stage = [0; 2];
355 let mut global_mask = GLOBAL_BIT_MASK;
356 let mut next_bit = glyph_flag::DEFINED.count_ones() + 1;
357
358 self.dedup_feature_infos();
360
361 for info in &self.feature_infos {
362 let bits_needed = if info.flags & F_GLOBAL != 0 && info.max_value == 1 {
363 0
365 } else {
366 let v = info.max_value;
368 let num_bits = 8 * core::mem::size_of_val(&v) as u32 - v.leading_zeros();
369 hb_ot_map_t::MAX_BITS.min(num_bits)
370 };
371
372 if info.max_value == 0 || next_bit + bits_needed >= GLOBAL_BIT_SHIFT {
373 continue;
375 }
376
377 let mut found = false;
378 let mut feature_index = [None; 2];
379
380 for (table_index, table) in self.face.layout_tables() {
381 if required_tag[table_index] == Some(info.tag) {
382 required_stage[table_index] = info.stage[table_index];
383 }
384
385 if let Some(script) = self.script_index[table_index] {
386 let lang = self.lang_index[table_index];
387 if let Some(idx) = table.find_language_feature(script, lang, info.tag) {
388 feature_index[table_index] = Some(idx);
389 found = true;
390 }
391 }
392 }
393
394 if !found && info.flags & F_GLOBAL_SEARCH != 0 {
395 for (table_index, table) in self.face.layout_tables() {
397 if let Some(idx) = table.features.index(info.tag) {
398 feature_index[table_index] = Some(idx);
399 found = true;
400 }
401 }
402 }
403
404 if !found && !info.flags & F_HAS_FALLBACK != 0 {
405 continue;
406 }
407
408 let (shift, mask) = if info.flags & F_GLOBAL != 0 && info.max_value == 1 {
409 (GLOBAL_BIT_SHIFT, GLOBAL_BIT_MASK)
411 } else {
412 let shift = next_bit;
413 let mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
414 next_bit += bits_needed;
415 global_mask |= (info.default_value << shift) & mask;
416 (shift, mask)
417 };
418
419 map_features.push(feature_map_t {
420 tag: info.tag,
421 index: feature_index,
422 stage: info.stage,
423 shift,
424 mask,
425 one_mask: (1 << shift) & mask,
426 auto_zwnj: info.flags & F_MANUAL_ZWNJ == 0,
427 auto_zwj: info.flags & F_MANUAL_ZWJ == 0,
428 random: info.flags & F_RANDOM != 0,
429 per_syllable: info.flags & F_PER_SYLLABLE != 0,
430 });
431 }
432
433 if self.is_simple {
434 map_features.sort();
435 }
436
437 (map_features, required_stage, global_mask)
438 }
439
440 fn dedup_feature_infos(&mut self) {
441 let feature_infos = &mut self.feature_infos;
442 if feature_infos.is_empty() {
443 return;
444 }
445
446 if !self.is_simple {
447 feature_infos.sort();
448 }
449
450 let mut j = 0;
451 for i in 1..feature_infos.len() {
452 if feature_infos[i].tag != feature_infos[j].tag {
453 j += 1;
454 feature_infos[j] = feature_infos[i];
455 } else {
456 if feature_infos[i].flags & F_GLOBAL != 0 {
457 feature_infos[j].flags |= F_GLOBAL;
458 feature_infos[j].max_value = feature_infos[i].max_value;
459 feature_infos[j].default_value = feature_infos[i].default_value;
460 } else {
461 if feature_infos[j].flags & F_GLOBAL != 0 {
462 feature_infos[j].flags ^= F_GLOBAL;
463 }
464 feature_infos[j].max_value =
465 feature_infos[j].max_value.max(feature_infos[i].max_value);
466 }
468 let flags = feature_infos[i].flags & F_HAS_FALLBACK;
469 feature_infos[j].flags |= flags;
470 feature_infos[j].stage[0] =
471 feature_infos[j].stage[0].min(feature_infos[i].stage[0]);
472 feature_infos[j].stage[1] =
473 feature_infos[j].stage[1].min(feature_infos[i].stage[1]);
474 }
475 }
476
477 feature_infos.truncate(j + 1);
478 }
479
480 fn collect_lookup_stages(
481 &self,
482 map_features: &[feature_map_t],
483 required_feature_index: [Option<FeatureIndex>; 2],
484 required_feature_stage: [usize; 2],
485 ) -> ([Vec<lookup_map_t>; 2], [Vec<StageMap>; 2]) {
486 let mut map_lookups = [Vec::new(), Vec::new()];
487 let mut map_stages = [Vec::new(), Vec::new()];
488
489 for table_index in TableIndex::iter() {
490 let mut stage_index = 0;
492 let mut last_lookup = 0;
493
494 let coords = self.face.ttfp_face.variation_coordinates();
495 let variation_index = self
496 .face
497 .layout_table(table_index)
498 .and_then(|t| t.variations?.find_index(coords));
499
500 for stage in 0..self.current_stage[table_index] {
501 if let Some(feature_index) = required_feature_index[table_index] {
502 if required_feature_stage[table_index] == stage {
503 self.add_lookups(
504 &mut map_lookups[table_index],
505 table_index,
506 feature_index,
507 variation_index,
508 GLOBAL_BIT_MASK,
509 true,
510 true,
511 false,
512 false,
513 );
514 }
515 }
516
517 for feature in map_features {
518 if let Some(feature_index) = feature.index[table_index] {
519 if feature.stage[table_index] == stage {
520 self.add_lookups(
521 &mut map_lookups[table_index],
522 table_index,
523 feature_index,
524 variation_index,
525 feature.mask,
526 feature.auto_zwnj,
527 feature.auto_zwj,
528 feature.random,
529 feature.per_syllable,
530 );
531 }
532 }
533 }
534
535 let lookups = &mut map_lookups[table_index];
537 let len = lookups.len();
538
539 if last_lookup + 1 < len {
540 lookups[last_lookup..].sort();
541
542 let mut j = last_lookup;
543 for i in j + 1..len {
544 if lookups[i].index != lookups[j].index {
545 j += 1;
546 lookups[j] = lookups[i];
547 } else {
548 lookups[j].mask |= lookups[i].mask;
549 lookups[j].auto_zwnj &= lookups[i].auto_zwnj;
550 lookups[j].auto_zwj &= lookups[i].auto_zwj;
551 }
552 }
553
554 lookups.truncate(j + 1);
555 }
556
557 last_lookup = lookups.len();
558
559 if let Some(info) = self.stages[table_index].get(stage_index) {
560 if info.index == stage {
561 map_stages[table_index].push(StageMap {
562 last_lookup,
563 pause_func: info.pause_func,
564 });
565
566 stage_index += 1;
567 }
568 }
569 }
570 }
571
572 (map_lookups, map_stages)
573 }
574
575 fn add_lookups(
576 &self,
577 lookups: &mut Vec<lookup_map_t>,
578 table_index: TableIndex,
579 feature_index: FeatureIndex,
580 variation_index: Option<VariationIndex>,
581 mask: hb_mask_t,
582 auto_zwnj: bool,
583 auto_zwj: bool,
584 random: bool,
585 per_syllable: bool,
586 ) -> Option<()> {
587 let table = self.face.layout_table(table_index)?;
588
589 let lookup_count = table.lookups.len();
590 let feature = match variation_index {
591 Some(idx) => table
592 .variations
593 .and_then(|var| var.find_substitute(feature_index, idx))
594 .or_else(|| table.features.get(feature_index))?,
595 None => table.features.get(feature_index)?,
596 };
597
598 for index in feature.lookup_indices {
599 if index < lookup_count {
600 lookups.push(lookup_map_t {
601 mask,
602 index,
603 auto_zwnj,
604 auto_zwj,
605 random,
606 per_syllable,
607 });
608 }
609 }
610
611 Some(())
612 }
613}