1use alloc::vec::Vec;
2use core::ops::Range;
3
4use ttf_parser::opentype_layout::{
5 FeatureIndex, LanguageIndex, LookupIndex, ScriptIndex, VariationIndex,
6};
7
8use super::buffer::{glyph_flag, hb_buffer_t};
9use super::ot_layout::{LayoutTableExt, TableIndex};
10use super::ot_shape_plan::hb_ot_shape_plan_t;
11use super::{hb_font_t, hb_mask_t, hb_tag_t, tag, Language, Script};
12
13pub struct hb_ot_map_t {
14 found_script: [bool; 2],
15 chosen_script: [Option<hb_tag_t>; 2],
16 global_mask: hb_mask_t,
17 features: Vec<feature_map_t>,
18 lookups: [Vec<lookup_map_t>; 2],
19 stages: [Vec<StageMap>; 2],
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
23pub struct feature_map_t {
24 tag: hb_tag_t,
25 index: [Option<FeatureIndex>; 2],
27 stage: [usize; 2],
28 shift: u32,
29 mask: hb_mask_t,
30 one_mask: hb_mask_t,
32 auto_zwnj: bool,
33 auto_zwj: bool,
34 random: bool,
35 per_syllable: bool,
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
39pub struct lookup_map_t {
40 pub index: LookupIndex,
41 pub auto_zwnj: bool,
43 pub auto_zwj: bool,
44 pub random: bool,
45 pub mask: hb_mask_t,
46 pub per_syllable: bool,
47}
48
49#[derive(Clone, Copy)]
50pub struct StageMap {
51 pub last_lookup: usize,
53 pub pause_func: Option<pause_func_t>,
54}
55
56pub type pause_func_t = fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t);
57
58impl hb_ot_map_t {
59 pub const MAX_BITS: u32 = 8;
60 pub const MAX_VALUE: u32 = (1 << Self::MAX_BITS) - 1;
61
62 #[inline]
63 pub fn found_script(&self, table_index: TableIndex) -> bool {
64 self.found_script[table_index]
65 }
66
67 #[inline]
68 pub fn chosen_script(&self, table_index: TableIndex) -> Option<hb_tag_t> {
69 self.chosen_script[table_index]
70 }
71
72 #[inline]
73 pub fn get_global_mask(&self) -> hb_mask_t {
74 self.global_mask
75 }
76
77 #[inline]
78 pub fn get_mask(&self, feature_tag: hb_tag_t) -> (hb_mask_t, u32) {
79 self.features
80 .binary_search_by_key(&feature_tag, |f| f.tag)
81 .map_or((0, 0), |idx| {
82 (self.features[idx].mask, self.features[idx].shift)
83 })
84 }
85
86 #[inline]
87 pub fn get_1_mask(&self, feature_tag: hb_tag_t) -> hb_mask_t {
88 self.features
89 .binary_search_by_key(&feature_tag, |f| f.tag)
90 .map_or(0, |idx| self.features[idx].one_mask)
91 }
92
93 #[inline]
94 pub fn get_feature_index(
95 &self,
96 table_index: TableIndex,
97 feature_tag: hb_tag_t,
98 ) -> Option<FeatureIndex> {
99 self.features
100 .binary_search_by_key(&feature_tag, |f| f.tag)
101 .ok()
102 .and_then(|idx| self.features[idx].index[table_index])
103 }
104
105 #[inline]
106 pub fn get_feature_stage(
107 &self,
108 table_index: TableIndex,
109 feature_tag: hb_tag_t,
110 ) -> Option<usize> {
111 self.features
112 .binary_search_by_key(&feature_tag, |f| f.tag)
113 .map(|idx| self.features[idx].stage[table_index])
114 .ok()
115 }
116
117 #[inline]
118 pub fn stages(&self, table_index: TableIndex) -> &[StageMap] {
119 &self.stages[table_index]
120 }
121
122 #[inline]
123 pub fn lookup(&self, table_index: TableIndex, index: usize) -> &lookup_map_t {
124 &self.lookups[table_index][index]
125 }
126
127 #[inline]
128 pub fn stage_lookups(&self, table_index: TableIndex, stage: usize) -> &[lookup_map_t] {
129 &self.lookups[table_index][self.stage_lookup_range(table_index, stage)]
130 }
131
132 #[inline]
133 pub fn stage_lookup_range(&self, table_index: TableIndex, stage: usize) -> Range<usize> {
134 let stages = &self.stages[table_index];
135 let lookups = &self.lookups[table_index];
136 let start = stage
137 .checked_sub(1)
138 .map_or(0, |prev| stages[prev].last_lookup);
139 let end = stages
140 .get(stage)
141 .map_or(lookups.len(), |curr| curr.last_lookup);
142 start..end
143 }
144}
145
146pub type hb_ot_map_feature_flags_t = u32;
147pub const F_NONE: u32 = 0x0000;
148pub 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;
153pub const F_GLOBAL_MANUAL_JOINERS: u32 = F_GLOBAL | F_MANUAL_JOINERS;
154pub const F_GLOBAL_HAS_FALLBACK: u32 = F_GLOBAL | F_HAS_FALLBACK;
155pub 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> {
160 face: &'a hb_font_t<'a>,
161 found_script: [bool; 2],
162 script_index: [Option<ScriptIndex>; 2],
163 chosen_script: [Option<hb_tag_t>; 2],
164 lang_index: [Option<LanguageIndex>; 2],
165 current_stage: [usize; 2],
166 feature_infos: Vec<feature_info_t>,
167 stages: [Vec<stage_info_t>; 2],
168}
169
170#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
171struct feature_info_t {
172 tag: hb_tag_t,
173 seq: usize,
175 max_value: u32,
176 flags: hb_ot_map_feature_flags_t,
177 default_value: u32,
179 stage: [usize; 2],
181}
182
183#[derive(Clone, Copy)]
184struct stage_info_t {
185 index: usize,
186 pause_func: Option<pause_func_t>,
187}
188
189impl<'a> hb_ot_map_builder_t<'a> {
190 pub fn new(
191 face: &'a hb_font_t<'a>,
192 script: Option<Script>,
193 language: Option<&Language>,
194 ) -> Self {
195 let (script_tags, lang_tags) = tag::tags_from_script_and_language(script, language);
198
199 let mut found_script = [false; 2];
200 let mut script_index = [None; 2];
201 let mut chosen_script = [None; 2];
202 let mut lang_index = [None; 2];
203
204 for (table_index, table) in face.layout_tables() {
205 if let Some((found, idx, tag)) = table.select_script(&script_tags) {
206 chosen_script[table_index] = Some(tag);
207 found_script[table_index] = found;
208 script_index[table_index] = Some(idx);
209
210 if let Some(idx) = table.select_script_language(idx, &lang_tags) {
211 lang_index[table_index] = Some(idx);
212 }
213 }
214 }
215
216 Self {
217 face,
218 found_script,
219 script_index,
220 chosen_script,
221 lang_index,
222 current_stage: [0, 0],
223 feature_infos: Vec::new(),
224 stages: [Vec::new(), Vec::new()],
225 }
226 }
227
228 #[inline]
229 pub fn chosen_script(&self, table_index: TableIndex) -> Option<hb_tag_t> {
230 self.chosen_script[table_index]
231 }
232
233 #[inline]
234 pub fn add_feature(&mut self, tag: hb_tag_t, flags: hb_ot_map_feature_flags_t, value: u32) {
235 if !tag.is_null() {
236 let seq = self.feature_infos.len();
237 self.feature_infos.push(feature_info_t {
238 tag,
239 seq,
240 max_value: value,
241 flags,
242 default_value: if flags & F_GLOBAL != 0 { value } else { 0 },
243 stage: self.current_stage,
244 });
245 }
246 }
247
248 #[inline]
249 pub fn enable_feature(&mut self, tag: hb_tag_t, flags: hb_ot_map_feature_flags_t, value: u32) {
250 self.add_feature(tag, flags | F_GLOBAL, value);
251 }
252
253 #[inline]
254 pub fn disable_feature(&mut self, tag: hb_tag_t) {
255 self.add_feature(tag, F_GLOBAL, 0);
256 }
257
258 #[inline]
259 pub fn add_gsub_pause(&mut self, pause: Option<pause_func_t>) {
260 self.add_pause(TableIndex::GSUB, pause);
261 }
262
263 #[inline]
264 pub fn add_gpos_pause(&mut self, pause: Option<pause_func_t>) {
265 self.add_pause(TableIndex::GPOS, pause);
266 }
267
268 fn add_pause(&mut self, table_index: TableIndex, pause: Option<pause_func_t>) {
269 self.stages[table_index].push(stage_info_t {
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: hb_mask_t = glyph_flag::DEFINED + 1;
278 const GLOBAL_BIT_SHIFT: u32 = glyph_flag::DEFINED.count_ones();
279
280 pub fn compile(&mut self) -> hb_ot_map_t {
281 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 hb_ot_map_t {
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<hb_tag_t>; 2],
318 ) -> (Vec<feature_map_t>, [usize; 2], hb_mask_t) {
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 self.dedup_feature_infos();
326
327 for info in &self.feature_infos {
328 let bits_needed = if info.flags & F_GLOBAL != 0 && info.max_value == 1 {
329 0
331 } else {
332 let v = info.max_value;
334 let num_bits = 8 * core::mem::size_of_val(&v) as u32 - v.leading_zeros();
335 hb_ot_map_t::MAX_BITS.min(num_bits)
336 };
337
338 let bits_available = 8 * core::mem::size_of::<hb_mask_t>() as u32;
339 if info.max_value == 0 || next_bit + bits_needed > bits_available {
340 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 & F_GLOBAL_SEARCH != 0 {
362 for (table_index, table) in self.face.layout_tables() {
364 if let Some(idx) = table.features.index(info.tag) {
365 feature_index[table_index] = Some(idx);
366 found = true;
367 }
368 }
369 }
370
371 if !found && !info.flags & F_HAS_FALLBACK != 0 {
372 continue;
373 }
374
375 let (shift, mask) = if info.flags & F_GLOBAL != 0 && info.max_value == 1 {
376 (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(feature_map_t {
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 & F_MANUAL_ZWNJ == 0,
394 auto_zwj: info.flags & F_MANUAL_ZWJ == 0,
395 random: info.flags & F_RANDOM != 0,
396 per_syllable: info.flags & F_PER_SYLLABLE != 0,
397 });
398 }
399
400 (map_features, required_stage, global_mask)
401 }
402
403 fn dedup_feature_infos(&mut self) {
404 let feature_infos = &mut self.feature_infos;
405 if feature_infos.is_empty() {
406 return;
407 }
408
409 feature_infos.sort();
410
411 let mut j = 0;
412 for i in 1..feature_infos.len() {
413 if feature_infos[i].tag != feature_infos[j].tag {
414 j += 1;
415 feature_infos[j] = feature_infos[i];
416 } else {
417 if feature_infos[i].flags & F_GLOBAL != 0 {
418 feature_infos[j].flags |= F_GLOBAL;
419 feature_infos[j].max_value = feature_infos[i].max_value;
420 feature_infos[j].default_value = feature_infos[i].default_value;
421 } else {
422 if feature_infos[j].flags & F_GLOBAL != 0 {
423 feature_infos[j].flags ^= F_GLOBAL;
424 }
425 feature_infos[j].max_value =
426 feature_infos[j].max_value.max(feature_infos[i].max_value);
427 }
429 let flags = feature_infos[i].flags & F_HAS_FALLBACK;
430 feature_infos[j].flags |= flags;
431 feature_infos[j].stage[0] =
432 feature_infos[j].stage[0].min(feature_infos[i].stage[0]);
433 feature_infos[j].stage[1] =
434 feature_infos[j].stage[1].min(feature_infos[i].stage[1]);
435 }
436 }
437
438 feature_infos.truncate(j + 1);
439 }
440
441 fn collect_lookup_stages(
442 &self,
443 map_features: &[feature_map_t],
444 required_feature_index: [Option<FeatureIndex>; 2],
445 required_feature_stage: [usize; 2],
446 ) -> ([Vec<lookup_map_t>; 2], [Vec<StageMap>; 2]) {
447 let mut map_lookups = [Vec::new(), Vec::new()];
448 let mut map_stages = [Vec::new(), Vec::new()];
449
450 for table_index in TableIndex::iter() {
451 let mut stage_index = 0;
453 let mut last_lookup = 0;
454
455 let coords = self.face.ttfp_face.variation_coordinates();
456 let variation_index = self
457 .face
458 .layout_table(table_index)
459 .and_then(|t| t.variations?.find_index(coords));
460
461 for stage in 0..self.current_stage[table_index] {
462 if let Some(feature_index) = required_feature_index[table_index] {
463 if required_feature_stage[table_index] == stage {
464 self.add_lookups(
465 &mut map_lookups[table_index],
466 table_index,
467 feature_index,
468 variation_index,
469 Self::GLOBAL_BIT_MASK,
470 true,
471 true,
472 false,
473 false,
474 );
475 }
476 }
477
478 for feature in map_features {
479 if let Some(feature_index) = feature.index[table_index] {
480 if feature.stage[table_index] == stage {
481 self.add_lookups(
482 &mut map_lookups[table_index],
483 table_index,
484 feature_index,
485 variation_index,
486 feature.mask,
487 feature.auto_zwnj,
488 feature.auto_zwj,
489 feature.random,
490 feature.per_syllable,
491 );
492 }
493 }
494 }
495
496 let lookups = &mut map_lookups[table_index];
498 let len = lookups.len();
499
500 if last_lookup < len {
501 lookups[last_lookup..].sort();
502
503 let mut j = last_lookup;
504 for i in j + 1..len {
505 if lookups[i].index != lookups[j].index {
506 j += 1;
507 lookups[j] = lookups[i];
508 } else {
509 lookups[j].mask |= lookups[i].mask;
510 lookups[j].auto_zwnj &= lookups[i].auto_zwnj;
511 lookups[j].auto_zwj &= lookups[i].auto_zwj;
512 }
513 }
514
515 lookups.truncate(j + 1);
516 }
517
518 last_lookup = lookups.len();
519
520 if let Some(info) = self.stages[table_index].get(stage_index) {
521 if info.index == stage {
522 map_stages[table_index].push(StageMap {
523 last_lookup,
524 pause_func: info.pause_func,
525 });
526
527 stage_index += 1;
528 }
529 }
530 }
531 }
532
533 (map_lookups, map_stages)
534 }
535
536 fn add_lookups(
537 &self,
538 lookups: &mut Vec<lookup_map_t>,
539 table_index: TableIndex,
540 feature_index: FeatureIndex,
541 variation_index: Option<VariationIndex>,
542 mask: hb_mask_t,
543 auto_zwnj: bool,
544 auto_zwj: bool,
545 random: bool,
546 per_syllable: bool,
547 ) -> Option<()> {
548 let table = self.face.layout_table(table_index)?;
549
550 let lookup_count = table.lookups.len();
551 let feature = match variation_index {
552 Some(idx) => table
553 .variations
554 .and_then(|var| var.find_substitute(feature_index, idx))
555 .or_else(|| table.features.get(feature_index))?,
556 None => table.features.get(feature_index)?,
557 };
558
559 for index in feature.lookup_indices {
560 if index < lookup_count {
561 lookups.push(lookup_map_t {
562 mask,
563 index,
564 auto_zwnj,
565 auto_zwj,
566 random,
567 per_syllable,
568 });
569 }
570 }
571
572 Some(())
573 }
574}