1use std::collections::hash_map::Entry;
10use std::collections::BTreeMap;
11use std::fmt::Debug;
12use std::u16;
13
14use bitflags::bitflags;
15use tinyvec::{tiny_vec, TinyVec};
16
17use crate::context::{ContextLookupHelper, Glyph, GlyphTable, MatchType};
18use crate::error::{ParseError, ShapingError};
19use crate::layout::{
20 chain_context_lookup_info, context_lookup_info, AlternateSet, AlternateSubst,
21 ChainContextLookup, ContextLookup, FeatureTableSubstitution, GDEFTable, LangSys, LayoutCache,
22 LayoutTable, Ligature, LigatureSubst, LookupCacheItem, LookupList, MultipleSubst,
23 ReverseChainSingleSubst, SequenceTable, SingleSubst, SubstLookup, GSUB,
24};
25use crate::scripts::{self, ScriptType};
26use crate::tables::variable_fonts::Tuple;
27use crate::tag;
28use crate::unicode::VariationSelector;
29
30const SUBST_RECURSION_LIMIT: usize = 2;
31
32#[derive(Copy, Clone, Eq, PartialEq, Debug)]
33pub struct FeatureInfo {
34 pub feature_tag: u32,
35 pub alternate: Option<usize>,
36}
37
38#[derive(Clone, Eq, PartialEq, Debug)]
40pub enum Features {
41 Custom(Vec<FeatureInfo>),
45 Mask(FeatureMask),
54}
55
56impl Default for Features {
57 fn default() -> Self {
58 Self::Mask(FeatureMask::default())
59 }
60}
61
62type SubstContext<'a> = ContextLookupHelper<'a, GSUB>;
63
64impl Ligature {
65 pub fn matches<T>(
66 &self,
67 match_type: MatchType,
68 opt_gdef_table: Option<&GDEFTable>,
69 i: usize,
70 glyphs: &[RawGlyph<T>],
71 ) -> bool {
72 let mut last_index = 0;
73 match_type.match_front(
74 opt_gdef_table,
75 &GlyphTable::ById(&self.component_glyphs),
76 glyphs,
77 i,
78 &mut last_index,
79 )
80 }
81
82 pub fn apply<T: GlyphData>(
83 &self,
84 match_type: MatchType,
85 opt_gdef_table: Option<&GDEFTable>,
86 i: usize,
87 glyphs: &mut Vec<RawGlyph<T>>,
88 ) -> usize {
89 let mut index = i + 1;
90 let mut matched = 0;
91 let mut skip = 0;
92 while matched < self.component_glyphs.len() {
93 if index < glyphs.len() {
94 if match_type.match_glyph(opt_gdef_table, &glyphs[index]) {
95 matched += 1;
96 let mut matched_glyph = glyphs.remove(index);
97 glyphs[i].unicodes.append(&mut matched_glyph.unicodes);
98 glyphs[i].extra_data =
99 GlyphData::merge(glyphs[i].extra_data.clone(), matched_glyph.extra_data);
100 glyphs[i].flags.set(RawGlyphFlags::LIGATURE, true);
101 } else {
102 glyphs[index].liga_component_pos = matched as u16;
103 skip += 1;
104 index += 1;
105 }
106 } else {
107 panic!("ran out of glyphs");
108 }
109 }
110 while index < glyphs.len()
111 && MatchType::marks_only().match_glyph(opt_gdef_table, &glyphs[index])
112 {
113 glyphs[index].liga_component_pos = matched as u16;
114 index += 1;
115 }
116 glyphs[i].glyph_index = self.ligature_glyph;
117 glyphs[i].glyph_origin = GlyphOrigin::Direct;
118 skip
119 }
120}
121
122#[derive(Clone, Debug)]
123pub struct RawGlyph<T> {
124 pub unicodes: TinyVec<[char; 1]>,
125 pub glyph_index: u16,
126 pub liga_component_pos: u16,
127 pub glyph_origin: GlyphOrigin,
128 pub flags: RawGlyphFlags,
129 pub variation: Option<VariationSelector>,
130 pub extra_data: T,
131}
132
133bitflags! {
134 pub struct RawGlyphFlags: u8 {
135 const SMALL_CAPS = 1 << 0;
136 const MULTI_SUBST_DUP = 1 << 1;
137 const IS_VERT_ALT = 1 << 2;
138 const LIGATURE = 1 << 3;
139 const FAKE_BOLD = 1 << 4;
140 const FAKE_ITALIC = 1 << 5;
141 }
142}
143
144pub trait GlyphData: Clone {
147 fn merge(data1: Self, data2: Self) -> Self;
148}
149
150impl GlyphData for () {
151 fn merge(_data1: (), _data2: ()) {}
152}
153
154#[derive(Clone, Copy, PartialEq, Debug)]
155pub enum GlyphOrigin {
156 Char(char),
157 Direct,
158}
159
160impl<T> RawGlyph<T> {
161 pub fn small_caps(&self) -> bool {
162 self.flags.contains(RawGlyphFlags::SMALL_CAPS)
163 }
164
165 pub fn multi_subst_dup(&self) -> bool {
166 self.flags.contains(RawGlyphFlags::MULTI_SUBST_DUP)
167 }
168
169 pub fn is_vert_alt(&self) -> bool {
170 self.flags.contains(RawGlyphFlags::IS_VERT_ALT)
171 }
172
173 pub fn ligature(&self) -> bool {
174 self.flags.contains(RawGlyphFlags::LIGATURE)
175 }
176
177 pub fn fake_bold(&self) -> bool {
178 self.flags.contains(RawGlyphFlags::FAKE_BOLD)
179 }
180
181 pub fn fake_italic(&self) -> bool {
182 self.flags.contains(RawGlyphFlags::FAKE_ITALIC)
183 }
184}
185
186impl<T> Glyph for RawGlyph<T> {
187 fn get_glyph_index(&self) -> u16 {
188 self.glyph_index
189 }
190}
191
192pub fn gsub_feature_would_apply<T: GlyphData>(
193 gsub_cache: &LayoutCache<GSUB>,
194 gsub_table: &LayoutTable<GSUB>,
195 opt_gdef_table: Option<&GDEFTable>,
196 langsys: &LangSys,
197 feature_variations: Option<&FeatureTableSubstitution<'_>>,
198 feature_tag: u32,
199 glyphs: &[RawGlyph<T>],
200 i: usize,
201) -> Result<bool, ParseError> {
202 if let Some(feature_table) =
203 gsub_table.find_langsys_feature(langsys, feature_tag, feature_variations)?
204 {
205 if let Some(ref lookup_list) = gsub_table.opt_lookup_list {
206 for lookup_index in &feature_table.lookup_indices {
207 let lookup_index = usize::from(*lookup_index);
208 let lookup_cache_item = lookup_list.lookup_cache_gsub(gsub_cache, lookup_index)?;
209 if gsub_lookup_would_apply(opt_gdef_table, &lookup_cache_item, glyphs, i)? {
210 return Ok(true);
211 }
212 }
213 }
214 }
215 Ok(false)
216}
217
218pub fn gsub_lookup_would_apply<T: GlyphData>(
219 opt_gdef_table: Option<&GDEFTable>,
220 lookup: &LookupCacheItem<SubstLookup>,
221 glyphs: &[RawGlyph<T>],
222 i: usize,
223) -> Result<bool, ParseError> {
224 let match_type = MatchType::from_lookup_flag(lookup.lookup_flag);
225 if i < glyphs.len() && match_type.match_glyph(opt_gdef_table, &glyphs[i]) {
226 return match lookup.lookup_subtables {
227 SubstLookup::SingleSubst(ref subtables) => {
228 match singlesubst_would_apply(subtables, &glyphs[i])? {
229 Some(_output_glyph) => Ok(true),
230 None => Ok(false),
231 }
232 }
233 SubstLookup::MultipleSubst(ref subtables) => {
234 match multiplesubst_would_apply(subtables, i, glyphs)? {
235 Some(_sequence_table) => Ok(true),
236 None => Ok(false),
237 }
238 }
239 SubstLookup::AlternateSubst(ref subtables) => {
240 match alternatesubst_would_apply(subtables, &glyphs[i])? {
241 Some(_alternate_set) => Ok(true),
242 None => Ok(false),
243 }
244 }
245 SubstLookup::LigatureSubst(ref subtables) => {
246 match ligaturesubst_would_apply(opt_gdef_table, subtables, match_type, i, glyphs)? {
247 Some(_ligature) => Ok(true),
248 None => Ok(false),
249 }
250 }
251 SubstLookup::ContextSubst(ref subtables) => {
252 match contextsubst_would_apply(opt_gdef_table, subtables, match_type, i, glyphs)? {
253 Some(_subst) => Ok(true),
254 None => Ok(false),
255 }
256 }
257 SubstLookup::ChainContextSubst(ref subtables) => {
258 match chaincontextsubst_would_apply(
259 opt_gdef_table,
260 subtables,
261 match_type,
262 i,
263 glyphs,
264 )? {
265 Some(_subst) => Ok(true),
266 None => Ok(false),
267 }
268 }
269 SubstLookup::ReverseChainSingleSubst(ref subtables) => {
270 match reversechainsinglesubst_would_apply(
271 opt_gdef_table,
272 subtables,
273 match_type,
274 i,
275 glyphs,
276 )? {
277 Some(_subst) => Ok(true),
278 None => Ok(false),
279 }
280 }
281 };
282 }
283 Ok(false)
284}
285
286pub fn gsub_apply_lookup<T: GlyphData>(
287 gsub_cache: &LayoutCache<GSUB>,
288 gsub_table: &LayoutTable<GSUB>,
289 opt_gdef_table: Option<&GDEFTable>,
290 lookup_index: usize,
291 feature_tag: u32,
292 opt_alternate: Option<usize>,
293 glyphs: &mut Vec<RawGlyph<T>>,
294 start: usize,
295 mut length: usize,
296 pred: impl Fn(&RawGlyph<T>) -> bool,
297) -> Result<usize, ParseError> {
298 if let Some(ref lookup_list) = gsub_table.opt_lookup_list {
299 let lookup = lookup_list.lookup_cache_gsub(gsub_cache, lookup_index)?;
300 let match_type = MatchType::from_lookup_flag(lookup.lookup_flag);
301 match lookup.lookup_subtables {
302 SubstLookup::SingleSubst(ref subtables) => {
303 for glyph in glyphs[start..(start + length)].iter_mut() {
304 if match_type.match_glyph(opt_gdef_table, glyph) && pred(glyph) {
305 singlesubst(subtables, feature_tag, glyph)?;
306 }
307 }
308 }
309 SubstLookup::MultipleSubst(ref subtables) => {
310 let mut i = start;
311 while i < start + length {
312 if match_type.match_glyph(opt_gdef_table, &glyphs[i]) && pred(&glyphs[i]) {
313 match multiplesubst(subtables, i, glyphs)? {
314 Some(replace_count) => {
315 i += replace_count;
316 length += replace_count;
317 length -= 1;
318 }
319 None => i += 1,
320 }
321 } else {
322 i += 1;
323 }
324 }
325 }
326 SubstLookup::AlternateSubst(ref subtables) => {
327 for glyph in glyphs[start..(start + length)].iter_mut() {
328 if match_type.match_glyph(opt_gdef_table, glyph) && pred(glyph) {
329 let alternate = opt_alternate.unwrap_or(0);
330 alternatesubst(subtables, alternate, glyph)?;
331 }
332 }
333 }
334 SubstLookup::LigatureSubst(ref subtables) => {
335 let mut i = start;
336 while i < start + length {
337 if match_type.match_glyph(opt_gdef_table, &glyphs[i]) && pred(&glyphs[i]) {
338 match ligaturesubst(opt_gdef_table, subtables, match_type, i, glyphs)? {
339 Some((removed_count, skip_count)) => {
340 i += skip_count + 1;
341 length -= removed_count;
342 }
343 None => i += 1,
344 }
345 } else {
346 i += 1;
347 }
348 }
349 }
350 SubstLookup::ContextSubst(ref subtables) => {
351 let mut i = start;
352 while i < start + length {
353 if match_type.match_glyph(opt_gdef_table, &glyphs[i]) && pred(&glyphs[i]) {
354 match contextsubst(
355 SUBST_RECURSION_LIMIT,
356 gsub_cache,
357 lookup_list,
358 opt_gdef_table,
359 subtables,
360 feature_tag,
361 match_type,
362 i,
363 glyphs,
364 )? {
365 Some((input_length, changes)) => {
366 i += input_length;
367 length = checked_add(length, changes).unwrap();
368 }
369 None => i += 1,
370 }
371 } else {
372 i += 1;
373 }
374 }
375 }
376 SubstLookup::ChainContextSubst(ref subtables) => {
377 let mut i = start;
378 while i < start + length {
379 if match_type.match_glyph(opt_gdef_table, &glyphs[i]) && pred(&glyphs[i]) {
380 match chaincontextsubst(
381 SUBST_RECURSION_LIMIT,
382 gsub_cache,
383 lookup_list,
384 opt_gdef_table,
385 subtables,
386 feature_tag,
387 match_type,
388 i,
389 glyphs,
390 )? {
391 Some((input_length, changes)) => {
392 i += input_length;
393 length = checked_add(length, changes).unwrap();
394 }
395 None => i += 1,
396 }
397 } else {
398 i += 1;
399 }
400 }
401 }
402 SubstLookup::ReverseChainSingleSubst(ref subtables) => {
403 for i in (start..start + length).rev() {
404 if match_type.match_glyph(opt_gdef_table, &glyphs[i]) && pred(&glyphs[i]) {
405 reversechainsinglesubst(opt_gdef_table, subtables, match_type, i, glyphs)?;
406 }
407 }
408 }
409 }
410 }
411 Ok(length)
412}
413
414fn singlesubst_would_apply<T: GlyphData>(
415 subtables: &[SingleSubst],
416 glyph: &RawGlyph<T>,
417) -> Result<Option<u16>, ParseError> {
418 let glyph_index = glyph.glyph_index;
419 for single_subst in subtables {
420 if let Some(glyph_index) = single_subst.apply_glyph(glyph_index)? {
421 return Ok(Some(glyph_index));
422 }
423 }
424 Ok(None)
425}
426
427fn singlesubst<T: GlyphData>(
428 subtables: &[SingleSubst],
429 subst_tag: u32,
430 glyph: &mut RawGlyph<T>,
431) -> Result<(), ParseError> {
432 if let Some(output_glyph) = singlesubst_would_apply(subtables, glyph)? {
433 glyph.glyph_index = output_glyph;
434 glyph.glyph_origin = GlyphOrigin::Direct;
435 if subst_tag == tag::VERT || subst_tag == tag::VRT2 {
436 glyph.flags.set(RawGlyphFlags::IS_VERT_ALT, true);
437 }
438 }
439 Ok(())
440}
441
442fn multiplesubst_would_apply<'a, T: GlyphData>(
443 subtables: &'a [MultipleSubst],
444 i: usize,
445 glyphs: &[RawGlyph<T>],
446) -> Result<Option<&'a SequenceTable>, ParseError> {
447 let glyph_index = glyphs[i].glyph_index;
448 for multiple_subst in subtables {
449 if let Some(sequence_table) = multiple_subst.apply_glyph(glyph_index)? {
450 return Ok(Some(sequence_table));
451 }
452 }
453 Ok(None)
454}
455
456fn multiplesubst<T: GlyphData>(
457 subtables: &[MultipleSubst],
458 i: usize,
459 glyphs: &mut Vec<RawGlyph<T>>,
460) -> Result<Option<usize>, ParseError> {
461 match multiplesubst_would_apply(subtables, i, glyphs)? {
462 Some(sequence_table) => {
463 if !sequence_table.substitute_glyphs.is_empty() {
464 let first_glyph_index = sequence_table.substitute_glyphs[0];
465 glyphs[i].glyph_index = first_glyph_index;
466 glyphs[i].glyph_origin = GlyphOrigin::Direct;
467 for j in 1..sequence_table.substitute_glyphs.len() {
468 let output_glyph_index = sequence_table.substitute_glyphs[j];
469 let mut flags = glyphs[i].flags;
470 flags.set(RawGlyphFlags::MULTI_SUBST_DUP, true);
471 flags.set(RawGlyphFlags::LIGATURE, false);
472 let glyph = RawGlyph {
473 unicodes: glyphs[i].unicodes.clone(),
474 glyph_index: output_glyph_index,
475 liga_component_pos: 0, glyph_origin: GlyphOrigin::Direct,
477 flags,
478 extra_data: glyphs[i].extra_data.clone(),
479 variation: glyphs[i].variation,
480 };
481 glyphs.insert(i + j, glyph);
482 }
483 Ok(Some(sequence_table.substitute_glyphs.len()))
484 } else {
485 glyphs.remove(i);
487 Ok(Some(0))
488 }
489 }
490 None => Ok(None),
491 }
492}
493
494fn alternatesubst_would_apply<'a, T: GlyphData>(
495 subtables: &'a [AlternateSubst],
496 glyph: &RawGlyph<T>,
497) -> Result<Option<&'a AlternateSet>, ParseError> {
498 let glyph_index = glyph.glyph_index;
499 for alternate_subst in subtables {
500 if let Some(alternate_set) = alternate_subst.apply_glyph(glyph_index)? {
501 return Ok(Some(alternate_set));
502 }
503 }
504 Ok(None)
505}
506
507fn alternatesubst<T: GlyphData>(
508 subtables: &[AlternateSubst],
509 alternate: usize,
510 glyph: &mut RawGlyph<T>,
511) -> Result<(), ParseError> {
512 if let Some(alternateset) = alternatesubst_would_apply(subtables, glyph)? {
513 if alternate < alternateset.alternate_glyphs.len() {
515 glyph.glyph_index = alternateset.alternate_glyphs[alternate];
516 glyph.glyph_origin = GlyphOrigin::Direct;
517 }
518 }
519 Ok(())
520}
521
522fn ligaturesubst_would_apply<'a, T: GlyphData>(
523 opt_gdef_table: Option<&GDEFTable>,
524 subtables: &'a [LigatureSubst],
525 match_type: MatchType,
526 i: usize,
527 glyphs: &[RawGlyph<T>],
528) -> Result<Option<&'a Ligature>, ParseError> {
529 let glyph_index = glyphs[i].glyph_index;
530 for ligature_subst in subtables {
531 if let Some(ligatureset) = ligature_subst.apply_glyph(glyph_index)? {
532 for ligature in &ligatureset.ligatures {
533 if ligature.matches(match_type, opt_gdef_table, i, glyphs) {
534 return Ok(Some(ligature));
535 }
536 }
537 }
538 }
539 Ok(None)
540}
541
542fn ligaturesubst<T: GlyphData>(
543 opt_gdef_table: Option<&GDEFTable>,
544 subtables: &[LigatureSubst],
545 match_type: MatchType,
546 i: usize,
547 glyphs: &mut Vec<RawGlyph<T>>,
548) -> Result<Option<(usize, usize)>, ParseError> {
549 match ligaturesubst_would_apply(opt_gdef_table, subtables, match_type, i, glyphs)? {
550 Some(ligature) => Ok(Some((
551 ligature.component_glyphs.len(),
552 ligature.apply(match_type, opt_gdef_table, i, glyphs),
553 ))),
554 None => Ok(None),
555 }
556}
557
558fn contextsubst_would_apply<'a, T: GlyphData>(
559 opt_gdef_table: Option<&GDEFTable>,
560 subtables: &'a [ContextLookup<GSUB>],
561 match_type: MatchType,
562 i: usize,
563 glyphs: &[RawGlyph<T>],
564) -> Result<Option<Box<SubstContext<'a>>>, ParseError> {
565 let glyph_index = glyphs[i].glyph_index;
566 for context_lookup in subtables {
567 if let Some(context) = context_lookup_info(context_lookup, glyph_index, |context| {
568 context.matches(opt_gdef_table, match_type, glyphs, i)
569 })? {
570 return Ok(Some(context));
571 }
572 }
573 Ok(None)
574}
575
576fn contextsubst<T: GlyphData>(
577 recursion_limit: usize,
578 gsub_cache: &LayoutCache<GSUB>,
579 lookup_list: &LookupList<GSUB>,
580 opt_gdef_table: Option<&GDEFTable>,
581 subtables: &[ContextLookup<GSUB>],
582 feature_tag: u32,
583 match_type: MatchType,
584 i: usize,
585 glyphs: &mut Vec<RawGlyph<T>>,
586) -> Result<Option<(usize, isize)>, ParseError> {
587 match contextsubst_would_apply(opt_gdef_table, subtables, match_type, i, glyphs)? {
588 Some(subst) => apply_subst_context(
589 recursion_limit,
590 gsub_cache,
591 lookup_list,
592 opt_gdef_table,
593 feature_tag,
594 match_type,
595 &subst,
596 i,
597 glyphs,
598 ),
599 None => Ok(None),
600 }
601}
602
603fn chaincontextsubst_would_apply<'a, T: GlyphData>(
604 opt_gdef_table: Option<&GDEFTable>,
605 subtables: &'a [ChainContextLookup<GSUB>],
606 match_type: MatchType,
607 i: usize,
608 glyphs: &[RawGlyph<T>],
609) -> Result<Option<Box<SubstContext<'a>>>, ParseError> {
610 let glyph_index = glyphs[i].glyph_index;
611 for chain_context_lookup in subtables {
612 if let Some(context) =
613 chain_context_lookup_info(chain_context_lookup, glyph_index, |context| {
614 context.matches(opt_gdef_table, match_type, glyphs, i)
615 })?
616 {
617 return Ok(Some(context));
618 }
619 }
620 Ok(None)
621}
622
623fn chaincontextsubst<T: GlyphData>(
624 recursion_limit: usize,
625 gsub_cache: &LayoutCache<GSUB>,
626 lookup_list: &LookupList<GSUB>,
627 opt_gdef_table: Option<&GDEFTable>,
628 subtables: &[ChainContextLookup<GSUB>],
629 feature_tag: u32,
630 match_type: MatchType,
631 i: usize,
632 glyphs: &mut Vec<RawGlyph<T>>,
633) -> Result<Option<(usize, isize)>, ParseError> {
634 match chaincontextsubst_would_apply(opt_gdef_table, subtables, match_type, i, glyphs)? {
635 Some(subst) => apply_subst_context(
636 recursion_limit,
637 gsub_cache,
638 lookup_list,
639 opt_gdef_table,
640 feature_tag,
641 match_type,
642 &subst,
643 i,
644 glyphs,
645 ),
646 None => Ok(None),
647 }
648}
649
650fn reversechainsinglesubst_would_apply<T: GlyphData>(
651 opt_gdef_table: Option<&GDEFTable>,
652 subtables: &[ReverseChainSingleSubst],
653 match_type: MatchType,
654 i: usize,
655 glyphs: &[RawGlyph<T>],
656) -> Result<Option<u16>, ParseError> {
657 let glyph_index = glyphs[i].glyph_index;
658 for reversechainsinglesubst in subtables {
659 if let new_glyph_index @ Some(_) = reversechainsinglesubst
660 .apply_glyph(glyph_index, |context| {
661 context.matches(opt_gdef_table, match_type, glyphs, i)
662 })?
663 {
664 return Ok(new_glyph_index);
665 }
666 }
667 Ok(None)
668}
669
670fn reversechainsinglesubst<T: GlyphData>(
671 opt_gdef_table: Option<&GDEFTable>,
672 subtables: &[ReverseChainSingleSubst],
673 match_type: MatchType,
674 i: usize,
675 glyphs: &mut [RawGlyph<T>],
676) -> Result<(), ParseError> {
677 if let Some(output_glyph_index) =
678 reversechainsinglesubst_would_apply(opt_gdef_table, subtables, match_type, i, glyphs)?
679 {
680 glyphs[i].glyph_index = output_glyph_index;
681 glyphs[i].glyph_origin = GlyphOrigin::Direct;
682 }
683 Ok(())
684}
685
686fn apply_subst_context<T: GlyphData>(
687 recursion_limit: usize,
688 gsub_cache: &LayoutCache<GSUB>,
689 lookup_list: &LookupList<GSUB>,
690 opt_gdef_table: Option<&GDEFTable>,
691 feature_tag: u32,
692 match_type: MatchType,
693 subst: &SubstContext<'_>,
694 i: usize,
695 glyphs: &mut Vec<RawGlyph<T>>,
696) -> Result<Option<(usize, isize)>, ParseError> {
697 let mut changes = 0;
698 let len = match match_type.find_nth(
699 opt_gdef_table,
700 glyphs,
701 i,
702 subst.match_context.input_table.len(),
703 ) {
704 Some(last) => last - i + 1,
705 None => return Ok(None), };
707 for (subst_index, subst_lookup_index) in subst.lookup_array {
708 match apply_subst(
709 recursion_limit,
710 gsub_cache,
711 lookup_list,
712 opt_gdef_table,
713 match_type,
714 usize::from(*subst_index),
715 usize::from(*subst_lookup_index),
716 feature_tag,
717 glyphs,
718 i,
719 )? {
720 Some(changes0) => changes += changes0,
721 None => {}
722 }
723 }
724 match checked_add(len, changes) {
725 Some(new_len) => Ok(Some((new_len, changes))),
726 None => panic!("apply_subst_context: len < 0"),
727 }
728}
729
730fn checked_add(base: usize, changes: isize) -> Option<usize> {
731 if changes < 0 {
732 base.checked_sub(changes.wrapping_abs() as usize)
733 } else {
734 base.checked_add(changes as usize)
735 }
736}
737
738fn apply_subst<T: GlyphData>(
739 recursion_limit: usize,
740 gsub_cache: &LayoutCache<GSUB>,
741 lookup_list: &LookupList<GSUB>,
742 opt_gdef_table: Option<&GDEFTable>,
743 parent_match_type: MatchType,
744 subst_index: usize,
745 lookup_index: usize,
746 feature_tag: u32,
747 glyphs: &mut Vec<RawGlyph<T>>,
748 index: usize,
749) -> Result<Option<isize>, ParseError> {
750 let lookup = lookup_list.lookup_cache_gsub(gsub_cache, lookup_index)?;
751 let match_type = MatchType::from_lookup_flag(lookup.lookup_flag);
752 let i = match parent_match_type.find_nth(opt_gdef_table, glyphs, index, subst_index) {
753 Some(index1) => index1,
754 None => return Ok(None), };
756 match lookup.lookup_subtables {
757 SubstLookup::SingleSubst(ref subtables) => {
758 singlesubst(subtables, feature_tag, &mut glyphs[i])?;
759 Ok(Some(0))
760 }
761 SubstLookup::MultipleSubst(ref subtables) => match multiplesubst(subtables, i, glyphs)? {
762 Some(replace_count) => Ok(Some((replace_count as isize) - 1)),
763 None => Ok(None),
764 },
765 SubstLookup::AlternateSubst(ref subtables) => {
766 alternatesubst(subtables, 0, &mut glyphs[i])?;
767 Ok(Some(0))
768 }
769 SubstLookup::LigatureSubst(ref subtables) => {
770 match ligaturesubst(opt_gdef_table, subtables, match_type, i, glyphs)? {
771 Some((removed_count, _skip_count)) => Ok(Some(-(removed_count as isize))),
772 None => Ok(None), }
774 }
775 SubstLookup::ContextSubst(ref subtables) => {
776 if recursion_limit > 0 {
777 match contextsubst(
778 recursion_limit - 1,
779 gsub_cache,
780 lookup_list,
781 opt_gdef_table,
782 subtables,
783 feature_tag,
784 match_type,
785 i,
786 glyphs,
787 )? {
788 Some((_length, change)) => Ok(Some(change)),
789 None => Ok(None),
790 }
791 } else {
792 Err(ParseError::LimitExceeded)
793 }
794 }
795 SubstLookup::ChainContextSubst(ref subtables) => {
796 if recursion_limit > 0 {
797 match chaincontextsubst(
798 recursion_limit - 1,
799 gsub_cache,
800 lookup_list,
801 opt_gdef_table,
802 subtables,
803 feature_tag,
804 match_type,
805 i,
806 glyphs,
807 )? {
808 Some((_length, change)) => Ok(Some(change)),
809 None => Ok(None),
810 }
811 } else {
812 Err(ParseError::LimitExceeded)
813 }
814 }
815 SubstLookup::ReverseChainSingleSubst(ref subtables) => {
816 reversechainsinglesubst(opt_gdef_table, subtables, match_type, i, glyphs)?;
817 Ok(Some(0))
818 }
819 }
820}
821
822struct LookupsCustom {
825 rvrn: Option<Vec<u16>>,
826 lookups: BTreeMap<usize, u32>,
827}
828
829fn build_lookups_custom(
830 gsub_table: &LayoutTable<GSUB>,
831 langsys: &LangSys,
832 feature_tags: &[FeatureInfo],
833 feature_variations: Option<&FeatureTableSubstitution<'_>>,
834) -> Result<LookupsCustom, ParseError> {
835 let mut rvrn = None;
836 let mut lookups = BTreeMap::new();
837 for feature_info in feature_tags {
838 let feature_table = gsub_table.find_langsys_feature(
839 langsys,
840 feature_info.feature_tag,
841 feature_variations,
842 )?;
843 if let Some(feature_table) = feature_table {
844 if feature_info.feature_tag == tag::RVRN {
845 rvrn = Some(feature_table.lookup_indices.clone());
846 } else {
847 lookups.extend(
848 feature_table
849 .lookup_indices
850 .iter()
851 .map(|&lookup_index| (usize::from(lookup_index), feature_info.feature_tag)),
852 )
853 }
854 }
855 }
856 Ok(LookupsCustom { rvrn, lookups })
857}
858
859fn build_lookups_default(
860 gsub_table: &LayoutTable<GSUB>,
861 langsys: &LangSys,
862 feature_masks: FeatureMask,
863 feature_variations: Option<&FeatureTableSubstitution<'_>>,
864) -> Result<Vec<(usize, u32)>, ParseError> {
865 let mut lookups = BTreeMap::new();
866 for (feature_mask, feature_tag) in FEATURE_MASKS {
867 if feature_masks.contains(*feature_mask) {
868 if let Some(feature_table) =
869 gsub_table.find_langsys_feature(langsys, *feature_tag, feature_variations)?
870 {
871 for lookup_index in &feature_table.lookup_indices {
872 lookups.insert(usize::from(*lookup_index), *feature_tag);
873 }
874 } else if *feature_tag == tag::VRT2 {
875 let vert_tag = tag::VERT;
876 if let Some(feature_table) =
877 gsub_table.find_langsys_feature(langsys, vert_tag, feature_variations)?
878 {
879 for lookup_index in &feature_table.lookup_indices {
880 lookups.insert(usize::from(*lookup_index), vert_tag);
881 }
882 }
883 }
884 }
885 }
886
887 Ok(lookups.into_iter().collect())
889}
890
891fn make_supported_features_mask(
892 gsub_table: &LayoutTable<GSUB>,
893 langsys: &LangSys,
894) -> Result<FeatureMask, ParseError> {
895 let mut feature_mask = FeatureMask::empty();
896 for feature_index in langsys.feature_indices_iter() {
897 let feature_record = gsub_table.feature_by_index(*feature_index)?;
898 feature_mask |= FeatureMask::from_tag(feature_record.feature_tag);
899 }
900 Ok(feature_mask)
901}
902
903fn lang_tag_key(opt_lang_tag: Option<u32>) -> u32 {
904 opt_lang_tag.unwrap_or(tag::DFLT)
906}
907
908fn get_supported_features(
909 gsub_cache: &LayoutCache<GSUB>,
910 script_tag: u32,
911 opt_lang_tag: Option<u32>,
912) -> Result<FeatureMask, ParseError> {
913 let feature_mask = match gsub_cache
914 .supported_features
915 .borrow_mut()
916 .entry((script_tag, lang_tag_key(opt_lang_tag)))
917 {
918 Entry::Occupied(entry) => FeatureMask::from_bits_truncate(*entry.get()),
919 Entry::Vacant(entry) => {
920 let gsub_table = &gsub_cache.layout_table;
921 let feature_mask =
922 if let Some(script) = gsub_table.find_script_or_default(script_tag)? {
923 if let Some(langsys) = script.find_langsys_or_default(opt_lang_tag)? {
924 make_supported_features_mask(gsub_table, langsys)?
925 } else {
926 FeatureMask::empty()
927 }
928 } else {
929 FeatureMask::empty()
930 };
931 entry.insert(feature_mask.bits());
932 feature_mask
933 }
934 };
935 Ok(feature_mask)
936}
937
938fn find_alternate(features_list: &[FeatureInfo], feature_tag: u32) -> Option<usize> {
939 for feature_info in features_list {
940 if feature_info.feature_tag == feature_tag {
941 return feature_info.alternate;
942 }
943 }
944 None
945}
946
947pub fn apply(
1061 dotted_circle_index: u16,
1062 gsub_cache: &LayoutCache<GSUB>,
1063 opt_gdef_table: Option<&GDEFTable>,
1064 script_tag: u32,
1065 opt_lang_tag: Option<u32>,
1066 features: &Features,
1067 tuple: Option<Tuple<'_>>,
1068 num_glyphs: u16,
1069 glyphs: &mut Vec<RawGlyph<()>>,
1070) -> Result<(), ShapingError> {
1071 match features {
1072 Features::Custom(features_list) => gsub_apply_custom(
1073 gsub_cache,
1074 opt_gdef_table,
1075 script_tag,
1076 opt_lang_tag,
1077 features_list,
1078 tuple,
1079 num_glyphs,
1080 glyphs,
1081 ),
1082 Features::Mask(feature_mask) => gsub_apply_default(
1083 dotted_circle_index,
1084 gsub_cache,
1085 opt_gdef_table,
1086 script_tag,
1087 opt_lang_tag,
1088 *feature_mask,
1089 tuple,
1090 num_glyphs,
1091 glyphs,
1092 ),
1093 }
1094}
1095
1096fn gsub_apply_custom(
1097 gsub_cache: &LayoutCache<GSUB>,
1098 opt_gdef_table: Option<&GDEFTable>,
1099 script_tag: u32,
1100 opt_lang_tag: Option<u32>,
1101 features_list: &[FeatureInfo],
1102 tuple: Option<Tuple<'_>>,
1103 num_glyphs: u16,
1104 glyphs: &mut Vec<RawGlyph<()>>,
1105) -> Result<(), ShapingError> {
1106 let gsub_table = &gsub_cache.layout_table;
1107 if let Some(script) = gsub_table.find_script_or_default(script_tag)? {
1108 if let Some(langsys) = script.find_langsys_or_default(opt_lang_tag)? {
1109 let feature_variations = gsub_table.feature_variations(tuple)?;
1110 let feature_variations = feature_variations.as_ref();
1111 let lookups =
1112 build_lookups_custom(gsub_table, langsys, features_list, feature_variations)?;
1113
1114 if let Some(rvrn_lookups) = lookups.rvrn {
1122 for lookup_index in rvrn_lookups {
1123 gsub_apply_lookup(
1124 gsub_cache,
1125 gsub_table,
1126 opt_gdef_table,
1127 usize::from(lookup_index),
1128 tag::RVRN,
1129 None,
1130 glyphs,
1131 0,
1132 glyphs.len(),
1133 |_| true,
1134 )?;
1135 }
1136 }
1137
1138 for (lookup_index, feature_tag) in lookups.lookups {
1140 let alternate = find_alternate(features_list, feature_tag);
1141 if feature_tag == tag::FINA && !glyphs.is_empty() {
1142 gsub_apply_lookup(
1143 gsub_cache,
1144 gsub_table,
1145 opt_gdef_table,
1146 lookup_index,
1147 feature_tag,
1148 alternate,
1149 glyphs,
1150 glyphs.len() - 1,
1151 1,
1152 |_| true,
1153 )?;
1154 } else {
1155 gsub_apply_lookup(
1156 gsub_cache,
1157 gsub_table,
1158 opt_gdef_table,
1159 lookup_index,
1160 feature_tag,
1161 alternate,
1162 glyphs,
1163 0,
1164 glyphs.len(),
1165 |_| true,
1166 )?;
1167 }
1168 }
1169 }
1170 }
1171 replace_missing_glyphs(glyphs, num_glyphs);
1172 Ok(())
1173}
1174
1175pub fn replace_missing_glyphs<T: GlyphData>(glyphs: &mut [RawGlyph<T>], num_glyphs: u16) {
1176 for glyph in glyphs.iter_mut() {
1177 if glyph.glyph_index >= num_glyphs {
1178 glyph.unicodes = tiny_vec![];
1179 glyph.glyph_index = 0;
1180 glyph.liga_component_pos = 0;
1181 glyph.glyph_origin = GlyphOrigin::Direct;
1182 glyph.flags = RawGlyphFlags::empty();
1183 glyph.variation = None;
1184 }
1185 }
1186}
1187
1188fn strip_joiners<T: GlyphData>(glyphs: &mut Vec<RawGlyph<T>>) {
1189 glyphs.retain(|g| match g.glyph_origin {
1190 GlyphOrigin::Char('\u{200C}') => false,
1191 GlyphOrigin::Char('\u{200D}') => false,
1192 _ => true,
1193 })
1194}
1195
1196bitflags! {
1197 pub struct FeatureMask: u64 {
1200 const ABVF = 1 << 0;
1201 const ABVS = 1 << 1;
1202 const AFRC = 1 << 2;
1203 const AKHN = 1 << 3;
1204 const BLWF = 1 << 4;
1205 const BLWS = 1 << 5;
1206 const C2SC = 1 << 6;
1207 const CALT = 1 << 7;
1208 const CCMP = 1 << 8;
1209 const CFAR = 1 << 9;
1210 const CJCT = 1 << 10;
1211 const CLIG = 1 << 11;
1212 const DLIG = 1 << 12;
1213 const FINA = 1 << 13;
1214 const FIN2 = 1 << 14;
1215 const FIN3 = 1 << 15;
1216 const FRAC = 1 << 16;
1217 const HALF = 1 << 17;
1218 const HALN = 1 << 18;
1219 const HLIG = 1 << 19;
1220 const INIT = 1 << 20;
1221 const ISOL = 1 << 21;
1222 const LIGA = 1 << 22;
1223 const LNUM = 1 << 23;
1224 const LOCL = 1 << 24;
1225 const MEDI = 1 << 25;
1226 const MED2 = 1 << 26;
1227 const MSET = 1 << 27;
1228 const NUKT = 1 << 28;
1229 const ONUM = 1 << 29;
1230 const ORDN = 1 << 30;
1231 const PNUM = 1 << 31;
1232 const PREF = 1 << 32;
1233 const PRES = 1 << 33;
1234 const PSTF = 1 << 34;
1235 const PSTS = 1 << 35;
1236 const RCLT = 1 << 36;
1237 const RKRF = 1 << 37;
1238 const RLIG = 1 << 38;
1239 const RPHF = 1 << 39;
1240 const SMCP = 1 << 40;
1241 const TNUM = 1 << 41;
1242 const VATU = 1 << 42;
1243 const VRT2_OR_VERT = 1 << 43;
1244 const ZERO = 1 << 44;
1245 const RVRN = 1 << 45;
1246 }
1247}
1248const FEATURE_MASKS: &[(FeatureMask, u32)] = &[
1249 (FeatureMask::ABVF, tag::ABVF),
1250 (FeatureMask::ABVS, tag::ABVS),
1251 (FeatureMask::AFRC, tag::AFRC),
1252 (FeatureMask::AKHN, tag::AKHN),
1253 (FeatureMask::BLWF, tag::BLWF),
1254 (FeatureMask::BLWS, tag::BLWS),
1255 (FeatureMask::C2SC, tag::C2SC),
1256 (FeatureMask::CALT, tag::CALT),
1257 (FeatureMask::CCMP, tag::CCMP),
1258 (FeatureMask::CFAR, tag::CFAR),
1259 (FeatureMask::CJCT, tag::CJCT),
1260 (FeatureMask::CLIG, tag::CLIG),
1261 (FeatureMask::DLIG, tag::DLIG),
1262 (FeatureMask::FINA, tag::FINA),
1263 (FeatureMask::FIN2, tag::FIN2),
1264 (FeatureMask::FIN3, tag::FIN3),
1265 (FeatureMask::FRAC, tag::FRAC),
1266 (FeatureMask::HALF, tag::HALF),
1267 (FeatureMask::HALN, tag::HALN),
1268 (FeatureMask::HLIG, tag::HLIG),
1269 (FeatureMask::INIT, tag::INIT),
1270 (FeatureMask::ISOL, tag::ISOL),
1271 (FeatureMask::LIGA, tag::LIGA),
1272 (FeatureMask::LNUM, tag::LNUM),
1273 (FeatureMask::LOCL, tag::LOCL),
1274 (FeatureMask::MEDI, tag::MEDI),
1275 (FeatureMask::MED2, tag::MED2),
1276 (FeatureMask::MSET, tag::MSET),
1277 (FeatureMask::NUKT, tag::NUKT),
1278 (FeatureMask::ONUM, tag::ONUM),
1279 (FeatureMask::ORDN, tag::ORDN),
1280 (FeatureMask::PNUM, tag::PNUM),
1281 (FeatureMask::PREF, tag::PREF),
1282 (FeatureMask::PRES, tag::PRES),
1283 (FeatureMask::PSTF, tag::PSTF),
1284 (FeatureMask::PSTS, tag::PSTS),
1285 (FeatureMask::RCLT, tag::RCLT),
1286 (FeatureMask::RKRF, tag::RKRF),
1287 (FeatureMask::RLIG, tag::RLIG),
1288 (FeatureMask::RPHF, tag::RPHF),
1289 (FeatureMask::RVRN, tag::RVRN),
1290 (FeatureMask::SMCP, tag::SMCP),
1291 (FeatureMask::TNUM, tag::TNUM),
1292 (FeatureMask::VATU, tag::VATU),
1293 (FeatureMask::VRT2_OR_VERT, tag::VRT2),
1294 (FeatureMask::ZERO, tag::ZERO),
1295];
1296
1297impl FeatureMask {
1298 pub fn from_tag(tag: u32) -> FeatureMask {
1299 match tag {
1300 tag::ABVF => FeatureMask::ABVF,
1301 tag::ABVS => FeatureMask::ABVS,
1302 tag::AFRC => FeatureMask::AFRC,
1303 tag::AKHN => FeatureMask::AKHN,
1304 tag::BLWF => FeatureMask::BLWF,
1305 tag::BLWS => FeatureMask::BLWS,
1306 tag::C2SC => FeatureMask::C2SC,
1307 tag::CALT => FeatureMask::CALT,
1308 tag::CCMP => FeatureMask::CCMP,
1309 tag::CFAR => FeatureMask::CFAR,
1310 tag::CJCT => FeatureMask::CJCT,
1311 tag::CLIG => FeatureMask::CLIG,
1312 tag::DLIG => FeatureMask::DLIG,
1313 tag::FINA => FeatureMask::FINA,
1314 tag::FIN2 => FeatureMask::FIN2,
1315 tag::FIN3 => FeatureMask::FIN3,
1316 tag::FRAC => FeatureMask::FRAC,
1317 tag::HALF => FeatureMask::HALF,
1318 tag::HALN => FeatureMask::HALN,
1319 tag::HLIG => FeatureMask::HLIG,
1320 tag::INIT => FeatureMask::INIT,
1321 tag::ISOL => FeatureMask::ISOL,
1322 tag::LIGA => FeatureMask::LIGA,
1323 tag::LNUM => FeatureMask::LNUM,
1324 tag::LOCL => FeatureMask::LOCL,
1325 tag::MEDI => FeatureMask::MEDI,
1326 tag::MED2 => FeatureMask::MED2,
1327 tag::MSET => FeatureMask::MSET,
1328 tag::NUKT => FeatureMask::NUKT,
1329 tag::ONUM => FeatureMask::ONUM,
1330 tag::ORDN => FeatureMask::ORDN,
1331 tag::PNUM => FeatureMask::PNUM,
1332 tag::PREF => FeatureMask::PREF,
1333 tag::PRES => FeatureMask::PRES,
1334 tag::PSTF => FeatureMask::PSTF,
1335 tag::PSTS => FeatureMask::PSTS,
1336 tag::RCLT => FeatureMask::RCLT,
1337 tag::RKRF => FeatureMask::RKRF,
1338 tag::RLIG => FeatureMask::RLIG,
1339 tag::RPHF => FeatureMask::RPHF,
1340 tag::RVRN => FeatureMask::RVRN,
1341 tag::SMCP => FeatureMask::SMCP,
1342 tag::TNUM => FeatureMask::TNUM,
1343 tag::VATU => FeatureMask::VATU,
1344 tag::VERT => FeatureMask::VRT2_OR_VERT,
1345 tag::VRT2 => FeatureMask::VRT2_OR_VERT,
1346 tag::ZERO => FeatureMask::ZERO,
1347 _ => FeatureMask::empty(),
1348 }
1349 }
1350
1351 pub fn iter(&self) -> impl Iterator<Item = FeatureInfo> + '_ {
1352 let limit = if self.is_empty() {
1353 0
1355 } else {
1356 FeatureMask::all().bits().count_ones()
1357 };
1358 (0..limit).filter_map(move |i| {
1359 FeatureMask::from_bits(1 << i).and_then(|flag| {
1360 if self.contains(flag) {
1361 Some(FeatureInfo {
1362 feature_tag: flag.as_tag().unwrap(),
1364 alternate: None,
1365 })
1366 } else {
1367 None
1368 }
1369 })
1370 })
1371 }
1372
1373 fn as_tag(&self) -> Option<u32> {
1374 FEATURE_MASKS
1375 .iter()
1376 .find(|(mask, _)| self == mask)
1377 .map(|(_, tag)| *tag)
1378 }
1379}
1380
1381impl Default for FeatureMask {
1382 fn default() -> Self {
1383 FeatureMask::CCMP
1384 | FeatureMask::RLIG
1385 | FeatureMask::CLIG
1386 | FeatureMask::LIGA
1387 | FeatureMask::LOCL
1388 | FeatureMask::CALT
1389 }
1390}
1391
1392pub fn features_supported(
1393 gsub_cache: &LayoutCache<GSUB>,
1394 script_tag: u32,
1395 opt_lang_tag: Option<u32>,
1396 feature_mask: FeatureMask,
1397) -> Result<bool, ShapingError> {
1398 let supported_features = get_supported_features(gsub_cache, script_tag, opt_lang_tag)?;
1399 Ok(supported_features.contains(feature_mask))
1400}
1401
1402pub fn get_lookups_cache_index(
1403 gsub_cache: &LayoutCache<GSUB>,
1404 script_tag: u32,
1405 opt_lang_tag: Option<u32>,
1406 feature_variations: Option<&FeatureTableSubstitution<'_>>,
1407 feature_mask: FeatureMask,
1408) -> Result<usize, ParseError> {
1409 let index = match gsub_cache.lookups_index.borrow_mut().entry((
1410 script_tag,
1411 lang_tag_key(opt_lang_tag),
1412 feature_mask.bits(),
1413 )) {
1414 Entry::Occupied(entry) => *entry.get(),
1415 Entry::Vacant(entry) => {
1416 let gsub_table = &gsub_cache.layout_table;
1417 if let Some(script) = gsub_table.find_script_or_default(script_tag)? {
1418 if let Some(langsys) = script.find_langsys_or_default(opt_lang_tag)? {
1419 let lookups = build_lookups_default(
1420 gsub_table,
1421 langsys,
1422 feature_mask,
1423 feature_variations,
1424 )?;
1425 let index = gsub_cache.cached_lookups.borrow().len();
1426 gsub_cache.cached_lookups.borrow_mut().push(lookups);
1427 *entry.insert(index)
1428 } else {
1429 *entry.insert(0)
1430 }
1431 } else {
1432 *entry.insert(0)
1433 }
1434 }
1435 };
1436 Ok(index)
1437}
1438
1439fn gsub_apply_default(
1440 dotted_circle_index: u16,
1441 gsub_cache: &LayoutCache<GSUB>,
1442 opt_gdef_table: Option<&GDEFTable>,
1443 script_tag: u32,
1444 opt_lang_tag: Option<u32>,
1445 mut feature_mask: FeatureMask,
1446 tuple: Option<Tuple<'_>>,
1447 num_glyphs: u16,
1448 glyphs: &mut Vec<RawGlyph<()>>,
1449) -> Result<(), ShapingError> {
1450 let gsub_table = &gsub_cache.layout_table;
1451 let feature_variations = gsub_table.feature_variations(tuple)?;
1452 let feature_variations = feature_variations.as_ref();
1453
1454 if tuple.is_some() {
1465 apply_rvrn(
1466 &gsub_cache,
1467 opt_gdef_table,
1468 script_tag,
1469 opt_lang_tag,
1470 feature_variations,
1471 glyphs,
1472 )?;
1473 }
1474 feature_mask.remove(FeatureMask::RVRN);
1475
1476 match ScriptType::from(script_tag) {
1477 ScriptType::Arabic => scripts::arabic::gsub_apply_arabic(
1478 gsub_cache,
1479 gsub_table,
1480 opt_gdef_table,
1481 script_tag,
1482 opt_lang_tag,
1483 feature_variations,
1484 glyphs,
1485 )?,
1486 ScriptType::Indic => scripts::indic::gsub_apply_indic(
1487 dotted_circle_index,
1488 gsub_cache,
1489 gsub_table,
1490 opt_gdef_table,
1491 script_tag,
1492 opt_lang_tag,
1493 feature_variations,
1494 glyphs,
1495 )?,
1496 ScriptType::Khmer => scripts::khmer::gsub_apply_khmer(
1497 dotted_circle_index,
1498 gsub_cache,
1499 gsub_table,
1500 opt_gdef_table,
1501 script_tag,
1502 opt_lang_tag,
1503 feature_variations,
1504 glyphs,
1505 )?,
1506 ScriptType::Syriac => scripts::syriac::gsub_apply_syriac(
1507 gsub_cache,
1508 gsub_table,
1509 opt_gdef_table,
1510 script_tag,
1511 opt_lang_tag,
1512 feature_variations,
1513 glyphs,
1514 )?,
1515 ScriptType::ThaiLao => scripts::thai_lao::gsub_apply_thai_lao(
1516 gsub_cache,
1517 gsub_table,
1518 opt_gdef_table,
1519 script_tag,
1520 opt_lang_tag,
1521 feature_variations,
1522 glyphs,
1523 )?,
1524 ScriptType::Default => {
1525 feature_mask &= get_supported_features(gsub_cache, script_tag, opt_lang_tag)?;
1526 if feature_mask.contains(FeatureMask::FRAC) {
1527 let index_frac = get_lookups_cache_index(
1528 gsub_cache,
1529 script_tag,
1530 opt_lang_tag,
1531 feature_variations,
1532 feature_mask,
1533 )?;
1534 feature_mask.remove(FeatureMask::FRAC);
1535 let index = get_lookups_cache_index(
1536 gsub_cache,
1537 script_tag,
1538 opt_lang_tag,
1539 feature_variations,
1540 feature_mask,
1541 )?;
1542 let lookups = &gsub_cache.cached_lookups.borrow()[index];
1543 let lookups_frac = &gsub_cache.cached_lookups.borrow()[index_frac];
1544 gsub_apply_lookups_frac(
1545 gsub_cache,
1546 gsub_table,
1547 opt_gdef_table,
1548 lookups,
1549 lookups_frac,
1550 glyphs,
1551 )?;
1552 } else {
1553 let index = get_lookups_cache_index(
1554 gsub_cache,
1555 script_tag,
1556 opt_lang_tag,
1557 feature_variations,
1558 feature_mask,
1559 )?;
1560 let lookups = &gsub_cache.cached_lookups.borrow()[index];
1561 gsub_apply_lookups(gsub_cache, gsub_table, opt_gdef_table, lookups, glyphs)?;
1562 }
1563 }
1564 }
1565
1566 strip_joiners(glyphs);
1567 replace_missing_glyphs(glyphs, num_glyphs);
1568 Ok(())
1569}
1570
1571fn gsub_apply_lookups(
1572 gsub_cache: &LayoutCache<GSUB>,
1573 gsub_table: &LayoutTable<GSUB>,
1574 opt_gdef_table: Option<&GDEFTable>,
1575 lookups: &[(usize, u32)],
1576 glyphs: &mut Vec<RawGlyph<()>>,
1577) -> Result<(), ShapingError> {
1578 gsub_apply_lookups_impl(
1579 gsub_cache,
1580 gsub_table,
1581 opt_gdef_table,
1582 lookups,
1583 glyphs,
1584 0,
1585 glyphs.len(),
1586 )?;
1587 Ok(())
1588}
1589
1590fn gsub_apply_lookups_impl(
1591 gsub_cache: &LayoutCache<GSUB>,
1592 gsub_table: &LayoutTable<GSUB>,
1593 opt_gdef_table: Option<&GDEFTable>,
1594 lookups: &[(usize, u32)],
1595 glyphs: &mut Vec<RawGlyph<()>>,
1596 start: usize,
1597 mut length: usize,
1598) -> Result<usize, ShapingError> {
1599 for (lookup_index, feature_tag) in lookups {
1600 length = gsub_apply_lookup(
1601 gsub_cache,
1602 gsub_table,
1603 opt_gdef_table,
1604 *lookup_index,
1605 *feature_tag,
1606 None,
1607 glyphs,
1608 start,
1609 length,
1610 |_| true,
1611 )?;
1612 }
1613 Ok(length)
1614}
1615
1616fn gsub_apply_lookups_frac(
1617 gsub_cache: &LayoutCache<GSUB>,
1618 gsub_table: &LayoutTable<GSUB>,
1619 opt_gdef_table: Option<&GDEFTable>,
1620 lookups: &[(usize, u32)],
1621 lookups_frac: &[(usize, u32)],
1622 glyphs: &mut Vec<RawGlyph<()>>,
1623) -> Result<(), ShapingError> {
1624 let mut i = 0;
1625 while i < glyphs.len() {
1626 if let Some((start_pos, _slash_pos, end_pos)) = find_fraction(&glyphs[i..]) {
1627 if start_pos > 0 {
1628 i += gsub_apply_lookups_impl(
1629 gsub_cache,
1630 gsub_table,
1631 opt_gdef_table,
1632 lookups,
1633 glyphs,
1634 i,
1635 start_pos,
1636 )?;
1637 }
1638 i += gsub_apply_lookups_impl(
1639 gsub_cache,
1640 gsub_table,
1641 opt_gdef_table,
1642 lookups_frac,
1643 glyphs,
1644 i,
1645 end_pos - start_pos + 1,
1646 )?;
1647 } else {
1648 gsub_apply_lookups_impl(
1649 gsub_cache,
1650 gsub_table,
1651 opt_gdef_table,
1652 lookups,
1653 glyphs,
1654 i,
1655 glyphs.len() - i,
1656 )?;
1657 break;
1658 }
1659 }
1660 Ok(())
1661}
1662
1663fn find_fraction(glyphs: &[RawGlyph<()>]) -> Option<(usize, usize, usize)> {
1664 let slash_pos = glyphs
1665 .iter()
1666 .position(|g| g.glyph_origin == GlyphOrigin::Char('/'))?;
1667 let mut start_pos = slash_pos;
1668 while start_pos > 0 {
1669 match glyphs[start_pos - 1].glyph_origin {
1670 GlyphOrigin::Char(c) if c.is_ascii_digit() => {
1671 start_pos -= 1;
1672 }
1673 _ => break,
1674 }
1675 }
1676 let mut end_pos = slash_pos;
1677 while end_pos + 1 < glyphs.len() {
1678 match glyphs[end_pos + 1].glyph_origin {
1679 GlyphOrigin::Char(c) if c.is_ascii_digit() => {
1680 end_pos += 1;
1681 }
1682 _ => break,
1683 }
1684 }
1685 if start_pos < slash_pos && slash_pos < end_pos {
1686 Some((start_pos, slash_pos, end_pos))
1687 } else {
1688 None
1689 }
1690}
1691
1692fn apply_rvrn(
1693 gsub_cache: &LayoutCache<GSUB>,
1694 opt_gdef_table: Option<&GDEFTable>,
1695 script_tag: u32,
1696 opt_lang_tag: Option<u32>,
1697 feature_variations: Option<&FeatureTableSubstitution<'_>>,
1698 glyphs: &mut Vec<RawGlyph<()>>,
1699) -> Result<(), ShapingError> {
1700 let gsub_table = &gsub_cache.layout_table;
1701 let index = get_lookups_cache_index(
1702 gsub_cache,
1703 script_tag,
1704 opt_lang_tag,
1705 feature_variations,
1706 FeatureMask::RVRN,
1707 )?;
1708 let lookups = &gsub_cache.cached_lookups.borrow()[index];
1709 gsub_apply_lookups(gsub_cache, gsub_table, opt_gdef_table, lookups, glyphs)?;
1710 Ok(())
1711}
1712
1713#[cfg(test)]
1714mod tests {
1715 use super::*;
1716
1717 #[test]
1718 fn feature_mask_iter() {
1719 let mask = FeatureMask::empty();
1720 assert_eq!(mask.iter().count(), 0);
1721
1722 let mask = FeatureMask::default();
1723 let expected = &[
1724 FeatureInfo {
1725 feature_tag: tag::CALT,
1726 alternate: None,
1727 },
1728 FeatureInfo {
1729 feature_tag: tag::CCMP,
1730 alternate: None,
1731 },
1732 FeatureInfo {
1733 feature_tag: tag::CLIG,
1734 alternate: None,
1735 },
1736 FeatureInfo {
1737 feature_tag: tag::LIGA,
1738 alternate: None,
1739 },
1740 FeatureInfo {
1741 feature_tag: tag::LOCL,
1742 alternate: None,
1743 },
1744 FeatureInfo {
1745 feature_tag: tag::RLIG,
1746 alternate: None,
1747 },
1748 ];
1749 assert_eq!(&mask.iter().collect::<Vec<_>>(), expected);
1750 }
1751}