1mod blob;
4mod cache;
5mod format;
6mod source;
7mod tables;
8
9#[doc(inline)]
10pub use blob::Blob;
11#[doc(inline)]
12pub use format::Format;
13#[doc(inline)]
14pub use source::Source;
15#[doc(inline)]
16pub use tables::{TableFunction, Tables};
17
18#[doc(hidden)]
20#[rust_analyzer::completions(hidden_from_completion)]
21pub mod interop;
22
23use super::attribute::Attributes;
24use super::charmap::{Charmap, EncodingTables, SfntUnicodeCharmap};
25use super::glyph::{
26 bitmap::BitmapStrikes,
27 color::{ColorGlyphs, Palettes},
28 outline::{varc, OutlineGlyphs, OutlineSource},
29 GlyphSource, GlyphSourceOrder,
30};
31use super::metrics::{empty_glyph_metrics, GlyphMetrics, Metrics, StyleMetrics};
32use super::name::{self, GlyphName, Names};
33use super::once::Once;
34use super::variation::{Axes, NamedInstances, NormalizedCoord, Variation};
35use crate::tables::glyf::outline::OutlineContext;
36use crate::tables::loca::LocaGlyph;
37use crate::tables::{
38 avar::Avar,
39 fvar::Fvar,
40 glyf::{Glyf, PHANTOM_POINT_COUNT},
41 gvar::{GlyphVariationData, SelectedGvar},
42 hmtx::GlyphMetricRecords,
43 hvar::Hvar,
44 layout::SelectedFeatureVariations,
45 loca::Loca,
46 vorg::Vorg,
47 vvar::Vvar,
48};
49use crate::{
50 ps::{
51 cff::{CffFontRef, Subfont},
52 type1::{MultipleMaster, Type1Font, Type1Instance},
53 },
54 tables::{
55 cbdt::Cbdt, cblc::Cblc, cpal::Cpal, ebdt::Ebdt, eblc::Eblc, fvar::VariationAxisRecord,
56 sbix::Sbix,
57 },
58 TableProvider,
59};
60use alloc::{boxed::Box, sync::Arc};
61pub(crate) use cache::ColrTables;
62use cache::{
63 CbdtTables, Cff2Font, CffFont, CpalTable, EbdtTables, GlyfLoca, GvarTable, HvarTable,
64 MetricsTable, SbixTable, TableCache, VorgTable, VvarTable,
65};
66use core::{
67 any::Any,
68 sync::atomic::{self, AtomicBool, AtomicU32},
69};
70use smallvec::SmallVec;
71use types::{F2Dot14, Fixed, GlyphId, Point};
72use yoke::Yoke;
73
74#[derive(Clone)]
78pub struct Font(Repr);
79
80#[derive(Clone)]
81enum Repr {
82 Default(SharedFont),
83 Varied(Arc<VariedInstance>),
84}
85
86impl Font {
87 pub fn new(source: impl Into<Source>, index: u32) -> Option<Self> {
101 Some(Font(Repr::Default(SharedFont::new(source, index)?)))
102 }
103
104 pub fn axes(&self) -> Axes<'_> {
107 Axes::new(self)
108 }
109
110 pub fn named_instances(&self) -> NamedInstances<'_> {
112 NamedInstances::new(self)
113 }
114
115 pub fn instance_builder(&self) -> InstanceBuilder {
120 InstanceBuilder {
121 font: self.shared().clone(),
122 coords: CoordStorage::default(),
123 type1_blend: SmallVec::new(),
124 }
125 }
126
127 #[inline]
129 pub fn default_instance(&self) -> Font {
130 Font(Repr::Default(self.shared().clone()))
131 }
132
133 pub fn normalized_coords(&self) -> &[NormalizedCoord] {
135 match &self.0 {
136 Repr::Default(_) => &[],
137 Repr::Varied(varied) => varied.coords.as_slice(),
138 }
139 }
140
141 pub fn feature_variations(&self) -> SelectedFeatureVariations {
143 match self.sfnt_location() {
144 Some(location) => location
145 .feature_vars
146 .load(self.shared(), self.normalized_coords()),
147 None => SelectedFeatureVariations::default(),
149 }
150 }
151
152 #[inline]
156 pub fn num_glyphs(&self) -> u32 {
157 self.shared().num_glyphs()
158 }
159
160 #[inline]
164 pub fn units_per_em(&self) -> u16 {
165 self.shared().units_per_em()
166 }
167
168 pub fn default_attributes(&self) -> Attributes {
179 Attributes::from_font(self)
180 }
181
182 pub fn is_hint_reliant(&self) -> bool {
190 use core::sync::atomic::Ordering;
191 const UNKNOWN: u8 = 0;
192 const KNOWN: u8 = 1;
193 let Some(sfnt) = self.shared().sfnt() else {
195 return false;
196 };
197 let cell = &sfnt.hint_reliant;
198 match cell.load(Ordering::Relaxed) {
199 UNKNOWN => {
200 let answer = crate::tables::glyf::is_hint_reliant(&self.tables());
201 cell.store(KNOWN + answer as u8, Ordering::Relaxed);
202 answer
203 }
204 state => state > KNOWN,
205 }
206 }
207
208 pub fn names(&self) -> Names<'_> {
210 match self.kind() {
211 Kind::Type1(font) => Names::from_type1(font),
212 Kind::Cff(ref font, _) => Names::from_cff(font),
213 _ => Names::from_sfnt(&self.tables()),
214 }
215 }
216
217 pub fn glyph_name(&self, glyph: GlyphId) -> Option<GlyphName> {
221 name::glyph_name(self, glyph)
222 }
223
224 pub fn glyph_names(&self) -> impl Iterator<Item = (GlyphId, GlyphName)> + '_ {
229 name::glyph_names(self)
230 }
231
232 pub fn glyph_by_name(&self, name: &str) -> Option<GlyphId> {
240 self.shared()
241 .0
242 .glyph_name_index
243 .get_or_init(|| name::GlyphNameIndex::new(self))
244 .get(name)
245 }
246
247 pub fn is_cid_keyed(&self) -> bool {
253 self.shared()
254 .standalone_cff()
255 .is_some_and(|cff| cff.is_cid())
256 }
257
258 pub fn max_cid(&self) -> Option<u32> {
264 Some(self.shared().cid_glyphs()?.len() as u32 - 1)
265 }
266
267 pub fn glyph_by_cid(&self, cid: u32) -> Option<GlyphId> {
274 let glyph = *self.shared().cid_glyphs()?.get(cid as usize)?;
275 (cid == 0 || glyph != 0).then(|| GlyphId::new(glyph as u32))
276 }
277
278 pub fn charmap(&self) -> Charmap<'_> {
280 Charmap::new(self)
281 }
282
283 pub fn metrics(&self) -> &Metrics {
286 match &self.0 {
287 Repr::Default(font) => font.metrics(),
289 Repr::Varied(varied) if matches!(varied.kind, VariedKind::Type1(_)) => {
292 varied.font.metrics()
293 }
294 Repr::Varied(varied) => varied.location.metrics.get_or_init(|| {
295 debug_assert!(!varied.coords.as_slice().is_empty());
296 Metrics::from_sfnt(&varied.font.tables(), varied.coords.as_slice())
297 }),
298 }
299 }
300
301 pub fn style_metrics(&self) -> &StyleMetrics {
305 match &self.0 {
306 Repr::Default(font) => font.style_metrics(),
307 Repr::Varied(varied) if matches!(varied.kind, VariedKind::Type1(_)) => {
308 varied.font.style_metrics()
309 }
310 Repr::Varied(varied) => varied
311 .location
312 .style_metrics
313 .get_or_init(|| {
314 Box::new(StyleMetrics::from_sfnt(
315 &varied.font.tables(),
316 varied.coords.as_slice(),
317 ))
318 })
319 .as_ref(),
320 }
321 }
322
323 #[inline]
326 pub fn glyph_metrics(&self) -> GlyphMetrics<'_> {
327 GlyphMetrics::new(self, self.metrics(), self.normalized_coords())
328 }
329
330 pub fn outline_glyphs(&self) -> OutlineGlyphs<'_> {
333 OutlineGlyphs::new(self, GlyphSourceOrder::DEFAULT)
334 }
335
336 pub fn outline_glyphs_with(&self, order: GlyphSourceOrder) -> OutlineGlyphs<'_> {
344 OutlineGlyphs::new(self, order)
345 }
346
347 pub fn bitmap_strikes(&self) -> BitmapStrikes<'_> {
350 BitmapStrikes::new(self, GlyphSourceOrder::BITMAPS)
351 }
352
353 pub fn bitmap_strikes_with(&self, order: GlyphSourceOrder) -> BitmapStrikes<'_> {
358 BitmapStrikes::new(self, order)
359 }
360
361 pub fn color_glyphs(&self) -> ColorGlyphs<'_> {
363 ColorGlyphs::new(self)
364 }
365
366 pub fn palettes(&self) -> Palettes<'_> {
368 Palettes::new(self)
369 }
370
371 #[inline]
373 pub fn source(&self) -> &Source {
374 self.shared().source()
375 }
376
377 #[inline]
379 pub fn kind(&self) -> Kind<'_> {
380 self.shared().kind()
381 }
382
383 #[inline]
387 pub fn tables(&self) -> &Tables {
388 self.shared().tables()
389 }
390}
391
392impl Font {
393 pub(super) fn scaling_data(&self) -> &Once<Box<dyn Any + Send + Sync>> {
399 &self.location_caches().scaling_data
400 }
401
402 fn location_caches(&self) -> &LocationCaches {
407 match &self.0 {
408 Repr::Default(font) => &font.0.default_location,
409 Repr::Varied(varied) => &varied.location,
410 }
411 }
412
413 fn sfnt_location(&self) -> Option<&SfntLocation> {
416 match &self.0 {
417 Repr::Default(font) => font.sfnt().map(|sfnt| &sfnt.default_location),
418 Repr::Varied(varied) => match &varied.kind {
419 VariedKind::Sfnt(sfnt) => Some(&sfnt.location),
420 VariedKind::Type1(_) => None,
421 },
422 }
423 }
424
425 pub(super) fn shared(&self) -> &SharedFont {
427 match &self.0 {
428 Repr::Default(font) => font,
429 Repr::Varied(varied) => &varied.font,
430 }
431 }
432
433 pub(crate) fn type1_axes(&self) -> Option<&[VariationAxisRecord]> {
436 self.shared().multiple_master()?;
437 Some(self.shared().type1_axes())
438 }
439
440 pub(crate) fn type1_instance(&self) -> Option<Type1Instance<'_>> {
442 let Kind::Type1(font) = self.shared().kind() else {
443 return None;
444 };
445 Some(match &self.0 {
446 Repr::Varied(varied) => match &varied.kind {
447 VariedKind::Type1(type1) => font.instance(&type1.weights),
448 VariedKind::Sfnt(_) => font.default_instance(),
449 },
450 Repr::Default(_) => font.default_instance(),
451 })
452 }
453
454 #[inline]
456 pub(crate) fn h_metric_records(&self) -> &GlyphMetricRecords<'_> {
457 self.shared().h_metric_records()
458 }
459
460 #[inline]
462 pub(crate) fn v_metric_records(&self) -> &GlyphMetricRecords<'_> {
463 self.shared().v_metric_records()
464 }
465
466 #[inline]
468 pub(crate) fn selected_glyf_loca(&self) -> Option<&(Glyf<'_>, Loca<'_>)> {
469 self.shared().selected_glyf_loca()
470 }
471
472 #[inline]
474 pub(crate) fn hvar(&self) -> Option<&Hvar<'_>> {
475 self.shared().hvar()
476 }
477
478 #[inline]
480 pub(crate) fn vvar(&self) -> Option<&Vvar<'_>> {
481 self.shared().vvar()
482 }
483
484 #[inline]
486 pub(crate) fn selected_gvar(&self) -> Option<&SelectedGvar<'_>> {
487 self.shared().selected_gvar()
488 }
489
490 pub(crate) fn gvar_scalars(&self) -> &[Fixed] {
493 self.location_scalars(
494 |sfnt| &sfnt.gvar_scalars,
495 |varied| match varied.font.selected_gvar() {
496 Some(gvar) => scalars_with(gvar.shared_tuple_count() as usize, |out| {
497 gvar.compute_scalars(varied.coords.as_slice(), out)
498 }),
499 None => Box::new([]),
500 },
501 )
502 }
503
504 pub(crate) fn hvar_scalars(&self) -> &[Fixed] {
506 self.location_scalars(
507 |sfnt| &sfnt.hvar_scalars,
508 |varied| {
509 let store = varied
510 .font
511 .hvar()
512 .and_then(|hvar| hvar.item_variation_store().ok());
513 store_scalars(store.as_ref(), varied.coords.as_slice())
514 },
515 )
516 }
517
518 pub(crate) fn vvar_scalars(&self) -> &[Fixed] {
520 self.location_scalars(
521 |sfnt| &sfnt.vvar_scalars,
522 |varied| {
523 let store = varied
524 .font
525 .vvar()
526 .and_then(|vvar| vvar.item_variation_store().ok());
527 store_scalars(store.as_ref(), varied.coords.as_slice())
528 },
529 )
530 }
531
532 pub(crate) fn colr_scalars(&self) -> &[Fixed] {
534 self.location_scalars(
535 |sfnt| &sfnt.colr_scalars,
536 |varied| {
537 let store = varied
538 .font
539 .colr_tables()
540 .and_then(|tables| tables.var_store.as_ref());
541 store_scalars(store, varied.coords.as_slice())
542 },
543 )
544 }
545
546 fn location_scalars(
549 &self,
550 cache: fn(&VariedSfnt) -> &Once<Box<[Fixed]>>,
551 compute: impl FnOnce(&VariedInstance) -> Box<[Fixed]>,
552 ) -> &[Fixed] {
553 match &self.0 {
554 Repr::Default(_) => &[],
555 Repr::Varied(varied) => match &varied.kind {
556 VariedKind::Sfnt(sfnt) => cache(sfnt).get_or_init(|| compute(varied)),
557 VariedKind::Type1(_) => &[],
558 },
559 }
560 }
561
562 #[inline]
564 pub(crate) fn cff(&self) -> Option<&CffFontRef<'_>> {
565 self.shared().cff()
566 }
567
568 pub(crate) fn charstrings(&self, source: OutlineSource) -> Option<&CffFontRef<'_>> {
573 let shared = self.shared();
574 match source {
575 OutlineSource::Cff2 => shared.cff2(),
576 OutlineSource::Cff => shared.cff().or_else(|| shared.standalone_cff()),
577 _ => None,
578 }
579 }
580
581 pub(crate) fn cff_subfont(&self, source: OutlineSource, index: u16) -> Option<Subfont> {
591 let cff = self.charstrings(source)?;
592 if let Some(subfonts) = self.shared().standalone_cff_subfonts() {
593 return match subfonts.get(usize::from(index)) {
594 Some(subfont) => *subfont,
595 None => cff.subfont(index, &[]).ok(),
596 };
597 }
598 let location = self.sfnt_location()?;
599 let cache = match source {
600 OutlineSource::Cff2 => &location.cff2_subfonts,
601 _ => &location.cff_subfonts,
602 };
603 let coords = self.normalized_coords();
604 let subfonts = cache.get_or_init(|| {
605 let count = (cff.num_subfonts() as usize).min(CACHED_SUBFONTS);
606 (0..count).map(|_| Once::new()).collect()
607 });
608 match subfonts.get(usize::from(index)) {
609 Some(subfont) => *subfont.get_or_init(|| cff.subfont(index, coords).ok()),
610 None => cff.subfont(index, coords).ok(),
611 }
612 }
613
614 pub(crate) fn has_layered_source(&self, source: GlyphSource) -> bool {
620 use core::sync::atomic::Ordering;
621 let Some(sfnt) = self.shared().sfnt() else {
622 return false;
623 };
624 let shift = 2 * (source as u32 - 1);
625 match (sfnt.glyph_sources.load(Ordering::Relaxed) >> shift) & 3 {
626 SOURCE_ABSENT => return false,
627 SOURCE_PRESENT => return true,
628 _ => {}
629 }
630 let present = match source {
631 GlyphSource::Sbix | GlyphSource::Cbdt | GlyphSource::Ebdt => !self
632 .bitmap_strikes_with(GlyphSourceOrder::new(&[source]))
633 .is_empty(),
634 GlyphSource::Colr => !self.color_glyphs().is_empty(),
635 GlyphSource::Varc => self.varc_outlines().is_some(),
636 _ => return false,
637 };
638 let state = if present {
639 SOURCE_PRESENT
640 } else {
641 SOURCE_ABSENT
642 };
643 sfnt.glyph_sources
644 .fetch_or(state << shift, Ordering::Relaxed);
645 present
646 }
647
648 pub(crate) fn has_layered_source_in(&self, order: GlyphSourceOrder) -> bool {
654 let Some(sfnt) = self.shared().sfnt() else {
655 return false;
656 };
657 let known = sfnt
658 .glyph_sources
659 .load(core::sync::atomic::Ordering::Relaxed);
660 order.iter().any(|source| {
661 if !matches!(
662 source,
663 GlyphSource::Sbix
664 | GlyphSource::Cbdt
665 | GlyphSource::Ebdt
666 | GlyphSource::Colr
667 | GlyphSource::Varc
668 ) {
669 return false;
670 }
671 match (known >> (2 * (source as u32 - 1))) & 3 {
672 SOURCE_ABSENT => false,
673 SOURCE_PRESENT => true,
674 _ => self.has_layered_source(source),
675 }
676 })
677 }
678
679 #[cfg(test)]
681 pub(crate) fn probed_glyph_sources(&self) -> u32 {
682 self.shared().sfnt().map_or(0, |sfnt| {
683 sfnt.glyph_sources
684 .load(core::sync::atomic::Ordering::Relaxed)
685 })
686 }
687
688 #[inline]
690 pub(crate) fn varc_outlines(&self) -> Option<&varc::Outlines<'_>> {
691 self.shared().varc_outlines()
692 }
693
694 #[inline]
696 pub(crate) fn colr_tables(&self) -> Option<&ColrTables<'_>> {
697 self.shared().colr_tables()
698 }
699
700 #[inline]
702 pub(crate) fn cpal(&self) -> Option<&Cpal<'_>> {
703 self.shared().cpal()
704 }
705
706 #[inline]
708 pub(crate) fn sbix(&self) -> Option<&Sbix<'_>> {
709 self.shared().sbix()
710 }
711
712 #[inline]
714 pub(crate) fn cbdt(&self) -> Option<&(Cblc<'_>, Cbdt<'_>)> {
715 self.shared().cbdt()
716 }
717
718 #[inline]
720 pub(crate) fn ebdt(&self) -> Option<&(Eblc<'_>, Ebdt<'_>)> {
721 self.shared().ebdt()
722 }
723
724 #[inline]
726 pub(crate) fn vorg(&self) -> Option<&Vorg<'_>> {
727 self.shared().vorg()
728 }
729}
730
731impl<'a> OutlineContext<'a> for &'a Font {
732 fn glyph(&self, glyph: GlyphId) -> Option<LocaGlyph<'a>> {
733 let (glyf, loca) = self.selected_glyf_loca()?;
734 loca.get(glyph, glyf)
735 }
736
737 fn glyph_variation_data(&self, glyph: GlyphId) -> Option<GlyphVariationData<'a>> {
738 self.selected_gvar()?.glyph_variation_data(glyph).ok()?
739 }
740
741 fn has_gvar(&self) -> bool {
742 self.selected_gvar().is_some()
743 }
744
745 fn phantom_point_deltas(&self, glyph: GlyphId) -> Option<[Point<Fixed>; PHANTOM_POINT_COUNT]> {
746 let (glyf, loca) = self.selected_glyf_loca()?;
747 self.selected_gvar()?.phantom_point_deltas_with_scalars(
748 glyf,
749 loca,
750 self.normalized_coords(),
751 self.gvar_scalars(),
752 glyph,
753 )
754 }
755
756 fn gvar_scalars(&self) -> &[Fixed] {
757 Font::gvar_scalars(self)
758 }
759
760 fn coords(&self) -> &[F2Dot14] {
761 self.normalized_coords()
762 }
763
764 fn units_per_em(&self) -> u16 {
765 Font::units_per_em(self)
766 }
767
768 fn h_metrics(&self, glyph: GlyphId) -> (i32, i32) {
769 let metrics = self.h_metric_records();
770 (
771 metrics
772 .stated_side_bearing(glyph)
773 .unwrap_or_default()
774 .to_i32(),
775 metrics.stated_advance(self.num_glyphs(), glyph).to_i32(),
776 )
777 }
778
779 fn v_metrics(&self, glyph: GlyphId) -> Option<(i32, i32)> {
780 let metrics = self.v_metric_records();
781 if metrics.is_empty() {
782 return None;
783 }
784 Some((
785 metrics
786 .stated_side_bearing(glyph)
787 .unwrap_or_default()
788 .to_i32(),
789 metrics.stated_advance(self.num_glyphs(), glyph).to_i32(),
790 ))
791 }
792
793 fn h_line_metrics(&self) -> (i32, i32) {
794 self.shared().metrics().h_line().map_or((0, 0), |line| {
795 (line.ascender.to_i32(), line.descender.to_i32())
796 })
797 }
798
799 fn has_hvar(&self) -> bool {
800 self.hvar().is_some()
801 }
802}
803
804#[derive(Clone)]
806pub enum Kind<'a> {
807 Sfnt(&'a Tables, u32),
809 Type1(&'a Type1Font),
811 Cff(CffFontRef<'a>, u32),
813}
814
815pub struct InstanceBuilder {
817 font: SharedFont,
818 coords: CoordStorage,
819 type1_blend: SmallVec<[Fixed; 4]>,
822}
823
824impl InstanceBuilder {
825 pub fn variations<V>(mut self, variations: V) -> Self
833 where
834 V: IntoIterator,
835 V::Item: Into<Variation>,
836 {
837 self.set_variations(variations);
838 self
839 }
840
841 pub fn normalized_coords(mut self, coords: impl IntoIterator<Item = NormalizedCoord>) -> Self {
851 self.set_coords(coords);
852 self
853 }
854
855 pub fn named_instance(mut self, index: u32) -> Self {
862 self.set_named_instance(index);
863 self
864 }
865
866 pub fn named_instance_with_overrides<V>(mut self, index: u32, overrides: V) -> Self
874 where
875 V: IntoIterator,
876 V::Item: Into<Variation>,
877 {
878 self.set_named_instance_with_overrides(index, overrides);
879 self
880 }
881
882 pub fn build(self) -> Font {
884 if self.coords.as_slice().is_empty() {
887 return Font(Repr::Default(self.font));
888 }
889 let kind = match &self.font.0.kind {
890 KindRepr::Sfnt(_) => VariedKind::Sfnt(VariedSfnt {
891 location: SfntLocation::new(),
892 gvar_scalars: Once::new(),
893 hvar_scalars: Once::new(),
894 colr_scalars: Once::new(),
895 vvar_scalars: Once::new(),
896 }),
897 KindRepr::Type1(type1) => {
898 let Some(mm) = type1.font.multiple_master() else {
899 return Font(Repr::Default(self.font));
900 };
901 let mut weights = SmallVec::from_elem(Fixed::ZERO, mm.num_masters());
902 mm.blend_to_weights(&self.type1_blend, &mut weights);
903 VariedKind::Type1(VariedType1 { weights })
904 }
905 KindRepr::StandaloneCff(_) => return Font(Repr::Default(self.font)),
908 };
909 Font(Repr::Varied(Arc::new(VariedInstance {
910 font: self.font,
911 coords: self.coords,
912 location: LocationCaches::new(),
913 kind,
914 })))
915 }
916}
917
918impl InstanceBuilder {
919 fn set_variations<V>(&mut self, variations: V)
920 where
921 V: IntoIterator,
922 V::Item: Into<Variation>,
923 {
924 if let Some(mm) = self.font.multiple_master() {
925 let axes = self.font.type1_axes();
927 let mut design: SmallVec<[Fixed; 4]> =
928 axes.iter().map(|axis| axis.default_value()).collect();
929 let mut named: SmallVec<[bool; 4]> = SmallVec::from_elem(false, design.len());
930 for variation in variations {
931 let variation = variation.into();
932 for ((axis, value), named) in axes.iter().zip(design.iter_mut()).zip(&mut named) {
933 if axis.axis_tag() == variation.tag {
934 *value = Fixed::from_f64(variation.value as f64);
935 *named = true;
936 }
937 }
938 }
939 let mut blend = SmallVec::from_elem(Fixed::ZERO, design.len());
940 mm.design_to_blend(&design, &mut blend);
941 let mut default = SmallVec::<[Fixed; 4]>::from_elem(Fixed::ZERO, design.len());
944 mm.default_blend(&mut default);
945 for ((value, default), named) in blend.iter_mut().zip(default).zip(named) {
946 if !named {
947 *value = default;
948 }
949 }
950 self.set_type1_blend(blend);
951 return;
952 }
953 let tables = self.font.tables();
954 if let Ok(fvar) = tables.fvar() {
955 set_variations(&fvar, tables.avar().ok(), &mut self.coords, variations);
956 } else {
957 self.coords.clear();
958 }
959 }
960
961 fn set_coords(&mut self, coords: impl IntoIterator<Item = NormalizedCoord>) {
962 if let Some(mm) = self.font.multiple_master() {
963 let mut blend: SmallVec<[Fixed; 4]> = SmallVec::from_elem(Fixed::ZERO, mm.axes().len());
965 mm.default_blend(&mut blend);
966 for (dst, src) in blend.iter_mut().zip(coords) {
967 *dst = src.to_fixed();
968 }
969 self.set_type1_blend(blend);
970 return;
971 }
972 if let Ok(fvar) = self.font.tables().fvar() {
973 let count = fvar.axis_count() as usize;
974 self.coords.resize(count, NormalizedCoord::ZERO);
975 for (dst, src) in self.coords.as_mut_slice().iter_mut().zip(
976 coords
977 .into_iter()
978 .chain(core::iter::repeat(NormalizedCoord::ZERO)),
979 ) {
980 *dst = src;
981 }
982 clear_if_all_zeroes(&mut self.coords);
983 } else {
984 self.coords.clear();
985 }
986 }
987
988 fn set_type1_blend(&mut self, blend: SmallVec<[Fixed; 4]>) {
997 let font = self.font.clone();
998 let Some(mm) = font.multiple_master() else {
999 return;
1000 };
1001 let mut weights: SmallVec<[Fixed; 16]> = SmallVec::from_elem(Fixed::ZERO, mm.num_masters());
1002 mm.blend_to_weights(&blend, &mut weights);
1003 let mut reported: SmallVec<[Fixed; 4]> = SmallVec::from_elem(Fixed::ZERO, blend.len());
1004 mm.weights_to_blend(&weights, &mut reported);
1005 self.coords = reported.iter().map(|value| value.to_f2dot14()).collect();
1006 self.type1_blend = blend;
1007 }
1008
1009 fn set_named_instance(&mut self, index: u32) {
1010 let tables = self.font.tables();
1011 if let Ok(fvar) = tables.fvar() {
1012 set_variations(
1013 &fvar,
1014 tables.avar().ok(),
1015 &mut self.coords,
1016 named_instance_variations(&fvar, index),
1017 );
1018 } else {
1019 self.coords.clear();
1020 }
1021 }
1022
1023 fn set_named_instance_with_overrides<V>(&mut self, index: u32, overrides: V)
1024 where
1025 V: IntoIterator,
1026 V::Item: Into<Variation>,
1027 {
1028 if self.font.multiple_master().is_some() {
1031 self.set_variations(overrides);
1032 return;
1033 }
1034 let tables = self.font.tables();
1035 if let Ok(fvar) = tables.fvar() {
1036 set_variations(
1037 &fvar,
1038 tables.avar().ok(),
1039 &mut self.coords,
1040 named_instance_variations(&fvar, index)
1041 .chain(overrides.into_iter().map(Into::into)),
1042 );
1043 } else {
1044 self.coords.clear();
1045 }
1046 }
1047}
1048
1049fn named_instance_variations<'a>(
1051 fvar: &'a Fvar,
1052 index: u32,
1053) -> impl Iterator<Item = Variation> + 'a {
1054 fvar.axis_instance_arrays()
1055 .ok()
1056 .and_then(|arrays| {
1057 let axes = arrays.axes();
1058 arrays.instances().get(index as usize).ok().map(|instance| {
1059 axes.iter()
1060 .zip(instance.coordinates)
1061 .map(|(axis, coord)| Variation::new(axis.axis_tag(), coord.get().to_f32()))
1062 })
1063 })
1064 .into_iter()
1065 .flatten()
1066}
1067
1068fn set_variations<V>(fvar: &Fvar, avar: Option<Avar>, coords: &mut CoordStorage, variations: V)
1072where
1073 V: IntoIterator,
1074 V::Item: Into<Variation>,
1075{
1076 coords.resize(fvar.axis_count() as usize, NormalizedCoord::ZERO);
1077 fvar.user_to_normalized(
1078 avar.as_ref(),
1079 variations
1080 .into_iter()
1081 .map(Into::into)
1082 .map(|var| (var.tag, Fixed::from_f64(var.value as _))),
1083 coords.as_mut_slice(),
1084 );
1085 clear_if_all_zeroes(coords);
1086}
1087
1088#[derive(Clone)]
1093pub(super) struct SharedFont(Arc<SharedFontRepr>);
1094
1095impl SharedFont {
1096 fn new(source: impl Into<Source>, index: u32) -> Option<Self> {
1100 let source = source.into();
1101 let kind = if let Some(tables) = Tables::new(source.clone(), index) {
1102 Some(KindRepr::Sfnt(SfntCaches::new(Arc::new(tables), index)))
1103 } else if let Source::Blob(blob) = &source {
1104 match Format::new(blob) {
1105 Some(Format::Type1) => Type1Font::new(blob).ok().map(|font| {
1106 let axes = font
1107 .multiple_master()
1108 .map(super::variation::type1_axis_records)
1109 .unwrap_or_default();
1110 KindRepr::Type1(Type1Data { font, axes })
1111 }),
1112 Some(Format::Cff(count)) if index < count => {
1113 let font = Yoke::<CffFontRef<'static>, Arc<Blob>>::try_attach_to_cart(
1114 Arc::new(blob.clone()),
1115 |blob| CffFontRef::new_cff(blob, index, None),
1116 )
1117 .ok()?;
1118 let count = font.get().num_subfonts().min(CACHED_SUBFONTS as _);
1121 let subfonts = (0..count)
1122 .map(|index| font.get().subfont(index, &[]).ok())
1123 .collect();
1124 Some(KindRepr::StandaloneCff(StandaloneCffData {
1125 font,
1126 subfonts,
1127 index,
1128 unicode_charmap: Once::new(),
1129 cid_glyphs: Once::new(),
1130 }))
1131 }
1132 _ => None,
1133 }
1134 } else {
1135 None
1136 };
1137 let kind = kind?;
1138 let repr = SharedFontRepr {
1139 source,
1140 kind,
1141 shaping_data: Once::new(),
1142 glyph_name_index: Once::new(),
1143 default_location: LocationCaches::new(),
1144 };
1145 Some(Self(Arc::new(repr)))
1146 }
1147
1148 fn source(&self) -> &Source {
1150 &self.0.source
1151 }
1152
1153 pub(super) fn kind(&self) -> Kind<'_> {
1155 match &self.0.kind {
1156 KindRepr::Sfnt(sfnt) => Kind::Sfnt(&sfnt.tables, sfnt.index),
1157 KindRepr::Type1(type1) => Kind::Type1(&type1.font),
1158 KindRepr::StandaloneCff(cff) => Kind::Cff(cff.font.get().clone(), cff.index),
1159 }
1160 }
1161
1162 fn multiple_master(&self) -> Option<&MultipleMaster> {
1164 match &self.0.kind {
1165 KindRepr::Type1(type1) => type1.font.multiple_master(),
1166 _ => None,
1167 }
1168 }
1169
1170 fn type1_axes(&self) -> &[VariationAxisRecord] {
1173 match &self.0.kind {
1174 KindRepr::Type1(type1) => &type1.axes,
1175 _ => &[],
1176 }
1177 }
1178
1179 #[inline]
1182 fn metrics(&self) -> &Metrics {
1183 self.0
1184 .default_location
1185 .metrics
1186 .get_or_init(|| match self.kind() {
1187 Kind::Type1(font) => Metrics::from_type1(font),
1188 Kind::Cff(font, _) => Metrics::from_cff(&font),
1189 _ => Metrics::from_sfnt(&self.tables(), &[]),
1190 })
1191 }
1192
1193 fn style_metrics(&self) -> &StyleMetrics {
1195 self.0
1196 .default_location
1197 .style_metrics
1198 .get_or_init(|| {
1199 Box::new(match self.kind() {
1200 Kind::Type1(font) => StyleMetrics::from_type1(font),
1201 Kind::Cff(font, _) => StyleMetrics::from_cff(&font),
1202 _ => StyleMetrics::from_sfnt(&self.tables(), &[]),
1203 })
1204 })
1205 .as_ref()
1206 }
1207
1208 #[inline]
1212 pub(super) fn num_glyphs(&self) -> u32 {
1213 self.metrics().num_glyphs
1214 }
1215
1216 #[inline]
1220 fn units_per_em(&self) -> u16 {
1221 self.metrics().units_per_em
1222 }
1223
1224 #[inline]
1227 pub(super) fn sfnt(&self) -> Option<&SfntCaches> {
1228 match &self.0.kind {
1229 KindRepr::Sfnt(sfnt) => Some(sfnt),
1230 _ => None,
1231 }
1232 }
1233
1234 fn tables_arc(&self) -> Option<&Arc<Tables>> {
1236 self.sfnt().map(|sfnt| &sfnt.tables)
1237 }
1238
1239 pub(super) fn unicode_charmap(&self) -> Option<&SfntUnicodeCharmap<'_>> {
1241 let sfnt = self.sfnt()?;
1242 Some(
1243 sfnt.charmap
1244 .get_or_init(|| {
1245 TableCache::read(sfnt.tables.clone(), |tables| {
1246 SfntUnicodeCharmap::read(&tables)
1247 })
1248 })
1249 .get(),
1250 )
1251 }
1252
1253 pub(super) fn encodings(&self) -> Option<&EncodingTables<'_>> {
1255 let sfnt = self.sfnt()?;
1256 Some(
1257 sfnt.encodings
1258 .get_or_init(|| {
1259 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1260 EncodingTables::read(&tables)
1261 }))
1262 })
1263 .get(),
1264 )
1265 }
1266
1267 pub(super) fn standalone_cff_unicode_charmap(&self) -> Option<&crate::ps::UnicodeCharmap> {
1268 let KindRepr::StandaloneCff(cff) = &self.0.kind else {
1269 return None;
1270 };
1271 Some(cff.unicode_charmap.get_or_init(|| {
1272 #[cfg(feature = "agl")]
1273 {
1274 let cff = cff.font.get();
1275 if cff.is_cid() {
1276 return crate::ps::UnicodeCharmap::default();
1277 }
1278 crate::ps::UnicodeCharmap::from_glyph_names(
1279 cff.charset()
1280 .into_iter()
1281 .flat_map(|charset| charset.iter())
1282 .filter_map(|(glyph, sid)| {
1283 let name = core::str::from_utf8(cff.string(sid)?).ok()?;
1284 Some((glyph, name))
1285 }),
1286 )
1287 }
1288 #[cfg(not(feature = "agl"))]
1289 {
1290 let _ = cff;
1291 crate::ps::UnicodeCharmap::default()
1292 }
1293 }))
1294 }
1295
1296 #[inline]
1298 fn h_metric_records(&self) -> &GlyphMetricRecords<'_> {
1299 let Some(sfnt) = self.sfnt() else {
1300 return empty_glyph_metrics();
1301 };
1302 &sfnt
1303 .h_metrics
1304 .get_or_init(|| {
1305 TableCache::read(sfnt.tables.clone(), |tables| {
1306 MetricsTable(tables.h_glyph_metric_records().unwrap_or_default())
1307 })
1308 })
1309 .get()
1310 .0
1311 }
1312
1313 #[inline]
1318 fn v_metric_records(&self) -> &GlyphMetricRecords<'_> {
1319 let Some(sfnt) = self.sfnt() else {
1320 return empty_glyph_metrics();
1321 };
1322 &sfnt
1323 .v_metrics
1324 .get_or_init(|| {
1325 TableCache::read(sfnt.tables.clone(), |tables| {
1326 MetricsTable(tables.v_glyph_metric_records().unwrap_or_default())
1327 })
1328 })
1329 .get()
1330 .0
1331 }
1332
1333 #[inline]
1338 fn selected_glyf_loca(&self) -> Option<&(Glyf<'_>, Loca<'_>)> {
1339 let sfnt = self.sfnt()?;
1340 sfnt.glyf_loca
1341 .get_or_init(|| TableCache::read(sfnt.tables.clone(), |tables| GlyfLoca::read(&tables)))
1342 .get()
1343 .0
1344 .as_ref()
1345 }
1346
1347 #[inline]
1349 fn hvar(&self) -> Option<&Hvar<'_>> {
1350 let sfnt = self.sfnt()?;
1351 sfnt.hvar
1352 .get_or_init(|| {
1353 TableCache::read(sfnt.tables.clone(), |tables| HvarTable::read(&tables))
1354 })
1355 .get()
1356 .0
1357 .as_ref()
1358 }
1359
1360 #[inline]
1362 fn vvar(&self) -> Option<&Vvar<'_>> {
1363 let sfnt = self.sfnt()?;
1364 sfnt.vvar
1365 .get_or_init(|| {
1366 TableCache::read(sfnt.tables.clone(), |tables| VvarTable::read(&tables))
1367 })
1368 .get()
1369 .0
1370 .as_ref()
1371 }
1372
1373 #[inline]
1378 fn selected_gvar(&self) -> Option<&SelectedGvar<'_>> {
1379 let sfnt = self.sfnt()?;
1380 sfnt.gvar
1381 .get_or_init(|| {
1382 TableCache::read(sfnt.tables.clone(), |tables| GvarTable::read(&tables))
1383 })
1384 .get()
1385 .0
1386 .as_ref()
1387 }
1388
1389 #[inline]
1391 fn vorg(&self) -> Option<&Vorg<'_>> {
1392 let sfnt = self.sfnt()?;
1393 sfnt.vorg
1394 .get_or_init(|| {
1395 TableCache::read(sfnt.tables.clone(), |tables| VorgTable::read(&tables))
1396 })
1397 .get()
1398 .0
1399 .as_ref()
1400 }
1401
1402 #[inline]
1407 fn cff2(&self) -> Option<&CffFontRef<'_>> {
1408 let sfnt = self.sfnt()?;
1409 sfnt.cff2
1410 .get_or_init(|| TableCache::read(sfnt.tables.clone(), |tables| Cff2Font::read(&tables)))
1411 .get()
1412 .0
1413 .as_ref()
1414 }
1415
1416 #[inline]
1418 fn cff(&self) -> Option<&CffFontRef<'_>> {
1419 let sfnt = self.sfnt()?;
1420 sfnt.cff
1421 .get_or_init(|| TableCache::read(sfnt.tables.clone(), |tables| CffFont::read(&tables)))
1422 .get()
1423 .0
1424 .as_ref()
1425 }
1426
1427 fn standalone_cff(&self) -> Option<&CffFontRef<'_>> {
1428 match &self.0.kind {
1429 KindRepr::StandaloneCff(cff) => Some(cff.font.get()),
1430 _ => None,
1431 }
1432 }
1433
1434 fn cid_glyphs(&self) -> Option<&[u16]> {
1440 let KindRepr::StandaloneCff(data) = &self.0.kind else {
1441 return None;
1442 };
1443 let cff = data.font.get();
1444 if !cff.is_cid() {
1445 return None;
1446 }
1447 let glyphs = data.cid_glyphs.get_or_init(|| {
1448 let num_glyphs = cff.num_glyphs();
1449 let Some(charset) = cff.charset() else {
1450 return Box::new([0]);
1451 };
1452 cid_table(|| {
1453 charset
1454 .iter()
1455 .filter(move |(glyph, _)| glyph.to_u32() < num_glyphs)
1456 .map(|(glyph, cid)| (glyph.to_u32() as u16, cid.to_u16() as usize))
1457 })
1458 });
1459 Some(glyphs)
1460 }
1461
1462 fn standalone_cff_subfonts(&self) -> Option<&[Option<Subfont>]> {
1463 match &self.0.kind {
1464 KindRepr::StandaloneCff(cff) => Some(&cff.subfonts),
1465 _ => None,
1466 }
1467 }
1468
1469 fn varc_outlines(&self) -> Option<&varc::Outlines<'_>> {
1471 let sfnt = self.sfnt()?;
1472 sfnt.varc
1473 .get_or_init(|| {
1474 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1475 varc::Outlines::read(&tables)
1476 }))
1477 })
1478 .get()
1479 .as_ref()
1480 }
1481
1482 fn colr_tables(&self) -> Option<&ColrTables<'_>> {
1484 let sfnt = self.sfnt()?;
1485 sfnt.colr
1486 .get_or_init(|| {
1487 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1488 ColrTables::read(&tables)
1489 }))
1490 })
1491 .get()
1492 .as_ref()
1493 }
1494
1495 fn cpal(&self) -> Option<&Cpal<'_>> {
1497 let sfnt = self.sfnt()?;
1498 sfnt.cpal
1499 .get_or_init(|| {
1500 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1501 CpalTable::read(&tables)
1502 }))
1503 })
1504 .get()
1505 .0
1506 .as_ref()
1507 }
1508
1509 fn sbix(&self) -> Option<&Sbix<'_>> {
1511 let sfnt = self.sfnt()?;
1512 sfnt.sbix
1513 .get_or_init(|| {
1514 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1515 SbixTable::read(&tables)
1516 }))
1517 })
1518 .get()
1519 .0
1520 .as_ref()
1521 }
1522
1523 fn cbdt(&self) -> Option<&(Cblc<'_>, Cbdt<'_>)> {
1525 let sfnt = self.sfnt()?;
1526 sfnt.cbdt
1527 .get_or_init(|| {
1528 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1529 CbdtTables::read(&tables)
1530 }))
1531 })
1532 .get()
1533 .0
1534 .as_ref()
1535 }
1536
1537 fn ebdt(&self) -> Option<&(Eblc<'_>, Ebdt<'_>)> {
1539 let sfnt = self.sfnt()?;
1540 sfnt.ebdt
1541 .get_or_init(|| {
1542 Box::new(TableCache::read(sfnt.tables.clone(), |tables| {
1543 EbdtTables::read(&tables)
1544 }))
1545 })
1546 .get()
1547 .0
1548 .as_ref()
1549 }
1550
1551 fn tables(&self) -> &Tables {
1555 if let KindRepr::Sfnt(sfnt) = &self.0.kind {
1556 &sfnt.tables
1557 } else {
1558 &tables::EMPTY_FONT_TABLES
1559 }
1560 }
1561}
1562
1563fn cid_table<I: Iterator<Item = (u16, usize)>>(cids: impl Fn() -> I) -> Box<[u16]> {
1568 let max_cid = cids().map(|(_, cid)| cid).max().unwrap_or(0);
1569 let mut glyphs = alloc::vec![0u16; max_cid + 1];
1570 for (glyph, cid) in cids() {
1573 if cid != 0 && glyphs[cid] == 0 {
1574 glyphs[cid] = glyph;
1575 }
1576 }
1577 glyphs.into_boxed_slice()
1578}
1579
1580struct SharedFontRepr {
1581 source: Source,
1582 kind: KindRepr,
1583 shaping_data: Once<Box<dyn Any + Send + Sync>>,
1585 glyph_name_index: Once<name::GlyphNameIndex>,
1587 default_location: LocationCaches,
1590}
1591
1592#[allow(
1598 clippy::large_enum_variant,
1599 reason = "boxing the SFNT caches would put a pointer chase on every advance, to save space only in the rare fonts of the other kinds"
1600)]
1601enum KindRepr {
1602 Sfnt(SfntCaches),
1603 Type1(Type1Data),
1604 StandaloneCff(StandaloneCffData),
1605}
1606
1607struct Type1Data {
1609 font: Type1Font,
1610 axes: Box<[VariationAxisRecord]>,
1611}
1612
1613pub(super) struct SfntCaches {
1615 tables: Arc<Tables>,
1616 index: u32,
1617 hint_reliant: core::sync::atomic::AtomicU8,
1620 glyph_sources: core::sync::atomic::AtomicU32,
1624 default_location: SfntLocation,
1627 h_metrics: Once<TableCache<MetricsTable<'static>>>,
1630 v_metrics: Once<TableCache<MetricsTable<'static>>>,
1633 glyf_loca: Once<TableCache<GlyfLoca<'static>>>,
1638 hvar: Once<TableCache<HvarTable<'static>>>,
1639 vvar: Once<TableCache<VvarTable<'static>>>,
1640 gvar: Once<TableCache<GvarTable<'static>>>,
1641 cff2: Once<TableCache<Cff2Font<'static>>>,
1645 cff: Once<TableCache<CffFont<'static>>>,
1646 varc: Once<Box<TableCache<Option<varc::Outlines<'static>>>>>,
1649 colr: Once<Box<TableCache<Option<ColrTables<'static>>>>>,
1650 cpal: Once<Box<TableCache<CpalTable<'static>>>>,
1651 sbix: Once<Box<TableCache<SbixTable<'static>>>>,
1652 cbdt: Once<Box<TableCache<CbdtTables<'static>>>>,
1653 ebdt: Once<Box<TableCache<EbdtTables<'static>>>>,
1654 vorg: Once<TableCache<VorgTable<'static>>>,
1656 charmap: Once<TableCache<SfntUnicodeCharmap<'static>>>,
1658 encodings: Once<Box<TableCache<EncodingTables<'static>>>>,
1660}
1661
1662impl SfntCaches {
1663 fn new(tables: Arc<Tables>, index: u32) -> Self {
1664 Self {
1665 tables,
1666 index,
1667 hint_reliant: core::sync::atomic::AtomicU8::new(0),
1668 glyph_sources: core::sync::atomic::AtomicU32::new(0),
1669 default_location: SfntLocation::new(),
1670 h_metrics: Once::new(),
1671 v_metrics: Once::new(),
1672 glyf_loca: Once::new(),
1673 hvar: Once::new(),
1674 vvar: Once::new(),
1675 gvar: Once::new(),
1676 cff2: Once::new(),
1677 cff: Once::new(),
1678 varc: Once::new(),
1679 colr: Once::new(),
1680 cpal: Once::new(),
1681 sbix: Once::new(),
1682 cbdt: Once::new(),
1683 ebdt: Once::new(),
1684 vorg: Once::new(),
1685 charmap: Once::new(),
1686 encodings: Once::new(),
1687 }
1688 }
1689}
1690
1691struct StandaloneCffData {
1692 font: Yoke<CffFontRef<'static>, Arc<Blob>>,
1693 subfonts: Box<[Option<Subfont>]>,
1696 index: u32,
1697 unicode_charmap: Once<crate::ps::UnicodeCharmap>,
1698 cid_glyphs: Once<Box<[u16]>>,
1700}
1701
1702struct LocationCaches {
1704 scaling_data: Once<Box<dyn Any + Send + Sync>>,
1706 metrics: Once<Metrics>,
1710 style_metrics: Once<Box<StyleMetrics>>,
1711}
1712
1713impl LocationCaches {
1714 const fn new() -> Self {
1715 Self {
1716 scaling_data: Once::new(),
1717 metrics: Once::new(),
1718 style_metrics: Once::new(),
1719 }
1720 }
1721}
1722
1723struct SfntLocation {
1725 feature_vars: FeatureVarsStorage,
1728 cff2_subfonts: SubfontCache,
1731 cff_subfonts: SubfontCache,
1732}
1733
1734impl SfntLocation {
1735 fn new() -> Self {
1736 Self {
1737 feature_vars: FeatureVarsStorage::new(),
1738 cff2_subfonts: Once::new(),
1739 cff_subfonts: Once::new(),
1740 }
1741 }
1742}
1743
1744struct VariedInstance {
1746 font: SharedFont,
1747 coords: CoordStorage,
1749 location: LocationCaches,
1750 kind: VariedKind,
1751}
1752
1753enum VariedKind {
1757 Sfnt(VariedSfnt),
1758 Type1(VariedType1),
1759}
1760
1761struct VariedType1 {
1763 weights: SmallVec<[Fixed; 16]>,
1765}
1766
1767struct VariedSfnt {
1769 location: SfntLocation,
1770 gvar_scalars: Once<Box<[Fixed]>>,
1773 hvar_scalars: Once<Box<[Fixed]>>,
1774 vvar_scalars: Once<Box<[Fixed]>>,
1775 colr_scalars: Once<Box<[Fixed]>>,
1776}
1777
1778const MAX_PRECOMPUTED_SCALARS: usize = 128;
1779
1780const SOURCE_ABSENT: u32 = 1;
1782const SOURCE_PRESENT: u32 = 2;
1784
1785fn scalars_with(count: usize, compute: impl FnOnce(&mut [Fixed]) -> usize) -> Box<[Fixed]> {
1788 let mut scalars = alloc::vec![Fixed::ZERO; count.min(MAX_PRECOMPUTED_SCALARS)];
1789 let written = compute(&mut scalars);
1790 scalars.truncate(written);
1791 scalars.into_boxed_slice()
1792}
1793
1794fn store_scalars(
1797 store: Option<&crate::tables::variations::ItemVariationStore<'_>>,
1798 coords: &[NormalizedCoord],
1799) -> Box<[Fixed]> {
1800 let Some(store) = store else {
1801 return Box::new([]);
1802 };
1803 let count = store
1804 .variation_region_list()
1805 .ok()
1806 .map_or(0, |regions| regions.variation_regions().len());
1807 scalars_with(count, |out| store.compute_scalars(coords, out))
1808}
1809
1810struct FeatureVarsStorage {
1814 gsub: AtomicU32,
1815 gpos: AtomicU32,
1816 has_lookup_variations: AtomicBool,
1818}
1819
1820impl Default for FeatureVarsStorage {
1821 fn default() -> Self {
1822 Self::new()
1823 }
1824}
1825
1826impl FeatureVarsStorage {
1827 const UNCHECKED: u32 = u32::MAX;
1832 const ABSENT: u32 = u32::MAX - 1;
1834
1835 fn new() -> Self {
1836 Self {
1837 gsub: AtomicU32::new(Self::UNCHECKED),
1838 gpos: AtomicU32::new(Self::UNCHECKED),
1839 has_lookup_variations: AtomicBool::new(false),
1840 }
1841 }
1842
1843 fn load(&self, font: &SharedFont, coords: &[NormalizedCoord]) -> SelectedFeatureVariations {
1844 let (gsub, gpos) = (
1845 self.gsub.load(atomic::Ordering::Acquire),
1846 self.gpos.load(atomic::Ordering::Acquire),
1847 );
1848 if gsub != Self::UNCHECKED && gpos != Self::UNCHECKED {
1849 return SelectedFeatureVariations {
1850 gsub: Self::decode(gsub),
1851 gpos: Self::decode(gpos),
1852 has_lookup_variations: self.has_lookup_variations.load(atomic::Ordering::Relaxed),
1853 };
1854 }
1855 let selected = SelectedFeatureVariations::new(&font.tables(), coords);
1858 self.has_lookup_variations
1859 .store(selected.has_lookup_variations, atomic::Ordering::Relaxed);
1860 self.gsub
1861 .store(Self::encode(selected.gsub), atomic::Ordering::Release);
1862 self.gpos
1863 .store(Self::encode(selected.gpos), atomic::Ordering::Release);
1864 selected
1865 }
1866
1867 fn decode(value: u32) -> Option<u32> {
1869 (value < Self::ABSENT).then_some(value)
1870 }
1871
1872 fn encode(index: Option<u32>) -> u32 {
1874 index.unwrap_or(Self::ABSENT)
1875 }
1876}
1877
1878type SubfontCache = Once<Box<[Once<Option<Subfont>>]>>;
1883
1884const CACHED_SUBFONTS: usize = 8;
1887
1888const MAX_INLINE_COORDS: usize = 12;
1893
1894type CoordStorage = SmallVec<[NormalizedCoord; MAX_INLINE_COORDS]>;
1895
1896fn clear_if_all_zeroes(coords: &mut CoordStorage) {
1899 if coords.iter().all(|&c| c == NormalizedCoord::ZERO) {
1900 coords.clear();
1901 }
1902}
1903
1904#[cfg(test)]
1905impl Font {
1906 fn feature_vars(&self) -> &FeatureVarsStorage {
1908 match &self.0 {
1909 Repr::Varied(varied) => &varied.sfnt().location.feature_vars,
1910 Repr::Default(_) => panic!("the default instance keeps no storage of its own"),
1911 }
1912 }
1913}
1914
1915#[cfg(test)]
1916impl VariedInstance {
1917 fn sfnt(&self) -> &VariedSfnt {
1919 match &self.kind {
1920 VariedKind::Sfnt(sfnt) => sfnt,
1921 VariedKind::Type1(_) => panic!("not an SFNT instance"),
1922 }
1923 }
1924}
1925
1926#[cfg(test)]
1927mod tests {
1928 use super::*;
1929 use crate::ps::Sid;
1930 use crate::tables::glyf::outline::{Outline, Unscaled};
1931 use core::sync::atomic::Ordering;
1932 use types::Tag;
1933
1934 fn type1_mm_start(font: &Font) -> f32 {
1937 use crate::model::glyph::outline::{DrawMemory, DrawOptions, PathElement};
1938 let mut path = alloc::vec::Vec::<PathElement>::new();
1939 font.outline_glyphs()
1940 .get(GlyphId::new(1))
1941 .unwrap()
1942 .draw(&DrawOptions::default(), DrawMemory::Scratch, &mut path)
1943 .unwrap();
1944 match path[0] {
1945 PathElement::MoveTo { x, .. } => x,
1946 _ => panic!("expected a move"),
1947 }
1948 }
1949
1950 #[test]
1953 fn type1_multiple_master_instances() {
1954 use crate::ps::type1::multiple_master_tests::{pfa_with_blend, ONE_AXIS};
1955 let font = Font::new(pfa_with_blend(ONE_AXIS), 0).unwrap();
1956 let axes = font.axes();
1957 assert_eq!(axes.len(), 1);
1958 let axis = axes.get(0).unwrap();
1959 assert_eq!(axis.tag(), Tag::new(b"wght"));
1960 assert_eq!(
1963 (axis.min_value(), axis.default_value(), axis.max_value()),
1964 (100.0, 300.0, 900.0)
1965 );
1966 let name = font.names().get(axis.name()).unwrap();
1967 assert_eq!(name.english_or_first().unwrap().name(), "Weight");
1968 assert!(font.named_instances().is_empty());
1969 assert!(font.normalized_coords().is_empty());
1970 assert_eq!(type1_mm_start(&font), 150.0);
1971 for (design, blend, start) in [
1974 (900.0, 1.0, 300.0),
1975 (500.0, 0.5, 200.0),
1976 (100.0, 0.0, 100.0),
1977 ] {
1978 let instance = font
1979 .instance_builder()
1980 .variations([("wght", design)])
1981 .build();
1982 assert_eq!(
1983 instance.normalized_coords(),
1984 [NormalizedCoord::from_f32(blend)],
1985 "{design}"
1986 );
1987 assert_eq!(type1_mm_start(&instance), start, "{design}");
1988 let extents = instance.glyph_metrics().extents(GlyphId::new(1)).unwrap();
1989 assert_eq!(extents.x_bearing, start, "{design}");
1990 let by_blend = font
1991 .instance_builder()
1992 .normalized_coords([NormalizedCoord::from_f32(blend)])
1993 .build();
1994 assert_eq!(type1_mm_start(&by_blend), start, "{design}");
1995 }
1996 }
1997
1998 #[test]
2002 fn type1_multiple_master_ignores_unknown_axes() {
2003 use crate::ps::type1::multiple_master_tests::pfa_with_blend;
2004 let font = Font::new(
2005 pfa_with_blend(
2006 "/BlendAxisTypes [/Weight] def /BlendDesignPositions [[0] [1]] def /BlendDesignMap [[[0 0] [500 0.5] [1000 1]]] def /WeightVector [0.25 0.75] def",
2007 ),
2008 0,
2009 )
2010 .unwrap();
2011 assert_eq!(type1_mm_start(&font), 250.0);
2012 let instance = font.instance_builder().variations([("XXXX", 42.0)]).build();
2013 assert_eq!(type1_mm_start(&instance), 250.0);
2014 }
2015
2016 #[test]
2019 fn type1_multiple_master_widest_axes() {
2020 use crate::ps::type1::multiple_master_tests::pfa_with_blend;
2021 let font = Font::new(
2022 pfa_with_blend(
2023 "/BlendAxisTypes [/Weight] def /BlendDesignPositions [[0] [1]] def /BlendDesignMap [[[-2147483648 0] [2147483647 1]]] def /WeightVector [0.5 0.5] def",
2024 ),
2025 0,
2026 )
2027 .unwrap();
2028 let axis = font.axes().get(0).unwrap();
2029 assert_eq!(
2030 (axis.min_value(), axis.default_value(), axis.max_value()),
2031 (-32768.0, 0.0, 32767.0)
2032 );
2033 }
2034
2035 #[test]
2038 fn type1_multiple_master_instances_keep_metrics() {
2039 use crate::ps::type1::multiple_master_tests::{pfa_with_blend, ONE_AXIS};
2040 let font = Font::new(
2041 pfa_with_blend(&format!(
2042 "{ONE_AXIS} /FontBBox [-10 -200 600 800] def \
2043 /ItalicAngle -12 def /UnderlinePosition -100 def \
2044 /UnderlineThickness 50 def"
2045 )),
2046 0,
2047 )
2048 .unwrap();
2049 let instance = font
2050 .instance_builder()
2051 .variations([("wght", 900.0)])
2052 .build();
2053 assert!(!instance.normalized_coords().is_empty());
2054 assert_eq!(instance.metrics().num_glyphs, 2);
2055 assert_eq!(instance.metrics(), font.metrics());
2056 assert!(instance.style_metrics().underline.is_some());
2057 assert_eq!(instance.style_metrics(), font.style_metrics());
2058 }
2059
2060 #[test]
2062 fn type1_multiple_master_named_instance_overrides() {
2063 use crate::ps::type1::multiple_master_tests::{pfa_with_blend, ONE_AXIS};
2064 let font = Font::new(pfa_with_blend(ONE_AXIS), 0).unwrap();
2065 let instance = font
2066 .instance_builder()
2067 .named_instance_with_overrides(0, [("wght", 900.0)])
2068 .build();
2069 assert_eq!(type1_mm_start(&instance), 300.0);
2070 }
2071
2072 #[test]
2075 fn new_uses_the_index_as_documented() {
2076 assert!(Font::new(font_test_data::TINOS_SUBSET, 0).is_some());
2077 assert!(Font::new(font_test_data::TINOS_SUBSET, 1).is_none());
2078 let count = crate::FileRef::new(font_test_data::ttc::TTC)
2079 .ok()
2080 .and_then(|file| match file {
2081 crate::FileRef::Collection(collection) => Some(collection.len()),
2082 _ => None,
2083 })
2084 .unwrap();
2085 assert!(count > 1);
2086 assert!(Font::new(font_test_data::ttc::TTC, count - 1).is_some());
2087 assert!(Font::new(font_test_data::ttc::TTC, count).is_none());
2088 let type1 = font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA;
2089 assert!(Font::new(type1, 7).is_some());
2090 let sfnt = crate::FontRef::new(font_test_data::NOTO_SANS_JP_CFF).unwrap();
2091 let cff = sfnt.cff().unwrap().offset_data().as_bytes().to_vec();
2092 assert!(Font::new(cff.clone(), 0).is_some());
2093 assert!(Font::new(cff, 1).is_none());
2094 }
2095
2096 #[test]
2099 fn cid_keyed_fonts_map_cids_to_glyphs() {
2100 let sfnt = crate::FontRef::new(font_test_data::NOTO_SANS_JP_CFF).unwrap();
2101 let font = Font::new(sfnt.cff().unwrap().offset_data().as_bytes(), 0).unwrap();
2102 assert!(font.is_cid_keyed());
2103 let charset = font.shared().standalone_cff().unwrap().charset().unwrap();
2104 let max = charset
2105 .iter()
2106 .map(|(_, cid)| cid.to_u16() as u32)
2107 .max()
2108 .unwrap();
2109 assert_eq!(font.max_cid(), Some(max));
2110 for cid in 0..=max + 1 {
2111 let expected = charset
2112 .glyph_id(Sid::new(cid as u16))
2113 .filter(|glyph| glyph.to_u32() < font.num_glyphs());
2114 assert_eq!(font.glyph_by_cid(cid), expected, "cid {cid}");
2115 }
2116 assert_eq!(font.glyph_by_cid(0x10000), None);
2117 }
2118
2119 #[test]
2122 fn the_first_glyph_keeps_its_cid() {
2123 let cids = [(0u16, 0usize), (1, 2), (2, 0), (3, 2), (4, 1)];
2124 assert_eq!(&*cid_table(|| cids.into_iter()), &[0, 4, 1]);
2125 }
2126
2127 #[test]
2128 fn other_fonts_have_no_cids() {
2129 for font in [
2130 Font::new(font_test_data::NOTO_SANS_JP_CFF, 0).unwrap(),
2132 Font::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED, 0).unwrap(),
2133 Font::new(font_test_data::VAZIRMATN_VAR, 0).unwrap(),
2134 Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA, 0).unwrap(),
2135 ] {
2136 assert!(!font.is_cid_keyed());
2137 assert_eq!(font.glyph_by_cid(1), None);
2138 assert_eq!(font.max_cid(), None);
2139 }
2140 }
2141
2142 #[test]
2143 fn standalone_cff_with_a_bad_private_dict_still_opens() {
2144 let sfnt = crate::FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap();
2145 let mut raw = sfnt.cff().unwrap().offset_data().as_bytes().to_vec();
2146 let private = CffFontRef::new_cff(&raw, 0, None)
2147 .unwrap()
2148 .sid_private_dict_range()
2149 .unwrap();
2150 let damage = [29u8, 0xff, 0xff, 0xff, 0xff, 19];
2152 assert!(private.len() >= damage.len());
2153 raw[private.start..private.start + damage.len()].copy_from_slice(&damage);
2154 let font = Font::new(raw, 0).unwrap();
2155 let subfonts = font.shared().standalone_cff_subfonts().unwrap();
2156 assert!(subfonts.len() == 1 && subfonts[0].is_none());
2157 assert!(font.num_glyphs() > 1);
2158 let outlines = font.outline_glyphs();
2161 assert_eq!(outlines.source(), Some(OutlineSource::Cff));
2162 for glyph in 0..font.num_glyphs() {
2163 let glyph = GlyphId::new(glyph);
2164 assert!(outlines.get(glyph).is_none());
2165 assert_eq!(font.glyph_metrics().h_advance(glyph), 0.0);
2166 assert!(font.glyph_metrics().extents(glyph).is_none());
2167 }
2168 }
2169
2170 #[test]
2171 fn standalone_cff_uses_selected_font_data() {
2172 let sfnt = crate::FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap();
2173 let raw = sfnt.cff().unwrap().offset_data().as_bytes();
2174 let direct = CffFontRef::new_cff(raw, 0, None).unwrap();
2175 let font = Font::new(raw.to_vec(), 0).unwrap();
2176 assert!(matches!(font.kind(), Kind::Cff(_, 0)));
2177 assert!(Font::new(raw.to_vec(), 1).is_none());
2178 assert_eq!(font.num_glyphs(), direct.num_glyphs());
2179 assert_eq!(font.units_per_em(), direct.upem() as u16);
2180 let bbox = direct.metadata().unwrap().bbox();
2181 assert_eq!(font.metrics().bounds.x_min, bbox.x_min.to_f48dot16());
2182 assert_eq!(font.metrics().bounds.y_max, bbox.y_max.to_f48dot16());
2183 assert_eq!(
2184 font.style_metrics().italic_angle,
2185 Some(direct.metadata().unwrap().italic_angle())
2186 );
2187 assert_eq!(
2188 font.shared().standalone_cff_subfonts().unwrap().len(),
2189 direct.num_subfonts() as usize
2190 );
2191 assert!(font.tables().cff().is_err());
2192 assert!(font.cff().is_none());
2193 let metadata = direct.metadata().unwrap();
2194 assert_eq!(metadata.version(), Some("2.9"));
2195 assert_eq!(
2196 metadata.notice(),
2197 Some("Noto is a trademark of Google LLC.")
2198 );
2199 assert!(metadata.copyright().unwrap().starts_with("Copyright 2022"));
2200 for (id, expected) in [
2201 (types::NameId::COPYRIGHT_NOTICE, metadata.copyright()),
2202 (types::NameId::FAMILY_NAME, metadata.family_name()),
2203 (types::NameId::SUBFAMILY_NAME, metadata.weight()),
2204 (types::NameId::FULL_NAME, metadata.full_name()),
2205 (types::NameId::VERSION_STRING, metadata.version()),
2206 (types::NameId::POSTSCRIPT_NAME, metadata.name()),
2207 (types::NameId::TRADEMARK, metadata.notice()),
2208 ] {
2209 let entries: Vec<_> = font
2210 .names()
2211 .get(id)
2212 .map(|name| name.iter().collect())
2213 .unwrap_or_default();
2214 assert_eq!(entries.len(), usize::from(expected.is_some()));
2215 if let Some(expected) = expected {
2216 assert_eq!(entries[0].name(), expected);
2217 assert!(entries[0].language().is_none());
2218 assert!(entries[0].sfnt_record().is_none());
2219 }
2220 }
2221 assert!(font
2222 .glyph_metrics()
2223 .extents_exact(GlyphId::new(1))
2224 .is_some());
2225 assert_eq!(font.glyph_name(GlyphId::new(1)).unwrap().as_str(), "i");
2226 #[cfg(feature = "agl")]
2227 assert_eq!(font.charmap().map_unicode('i'), Some(GlyphId::new(1)));
2228 let native = font
2229 .charmap()
2230 .encodings()
2231 .find(|encoding| encoding.kind() != super::super::charmap::EncodingKind::Unicode)
2232 .unwrap();
2233 for encoding in font.charmap().encodings() {
2235 assert_eq!(encoding.sfnt_index(), None);
2236 assert_eq!(encoding.sfnt_ids(), None);
2237 }
2238 assert_eq!(
2239 font.charmap().map_code(native, b'i' as u32),
2240 direct.encoding().unwrap().map(b'i')
2241 );
2242 assert!(font
2243 .charmap()
2244 .codes(native)
2245 .any(|(code, glyph)| code == b'i' as u32 && glyph == GlyphId::new(1)));
2246
2247 let names: Vec<_> = font.glyph_names().collect();
2248 assert_eq!(names.len(), font.num_glyphs() as usize);
2249 for (glyph, name) in names.iter().take(8) {
2250 assert_eq!(font.glyph_name(*glyph).unwrap().as_str(), name.as_str());
2251 let subfont = direct
2252 .subfont(direct.subfont_index(*glyph).unwrap(), &[])
2253 .unwrap();
2254 let width = direct.evaluate_width(&subfont, *glyph, &[]).unwrap();
2255 assert_eq!(
2256 font.glyph_metrics().h_advance_exact(*glyph),
2257 width.map(|width| width.to_f48dot16()).unwrap_or_default(),
2258 );
2259 }
2260 assert_eq!(
2261 font.glyph_metrics()
2262 .h_advance_exact(GlyphId::new(font.num_glyphs())),
2263 types::F48Dot16::ZERO
2264 );
2265 assert_eq!(
2266 font.default_instance()
2267 .glyph_name(GlyphId::new(1))
2268 .unwrap()
2269 .as_str(),
2270 font.glyph_name(GlyphId::new(1)).unwrap().as_str()
2271 );
2272 }
2273
2274 #[cfg(feature = "std")]
2275 #[test]
2276 fn variation_scalars_are_lazy_bounded_and_shared() {
2277 let font = Font::new(font_test_data::VAZIRMATN_VAR, 0).unwrap();
2278 let instance = font
2279 .instance_builder()
2280 .normalized_coords([NormalizedCoord::from_f32(-0.75)])
2281 .build();
2282 let Repr::Varied(varied) = &instance.0 else {
2283 panic!("expected a varied instance");
2284 };
2285 assert!(varied.sfnt().gvar_scalars.get().is_none());
2286 assert!(varied.sfnt().hvar_scalars.get().is_none());
2287
2288 let gvar_scalars = instance.gvar_scalars();
2289 assert!(!gvar_scalars.is_empty());
2290 let gvar = instance.selected_gvar().unwrap();
2291 assert_eq!(
2292 gvar_scalars.len(),
2293 (gvar.shared_tuple_count() as usize).min(MAX_PRECOMPUTED_SCALARS)
2294 );
2295 assert!(varied.sfnt().hvar_scalars.get().is_none());
2296 let mut expected = alloc::vec![Fixed::ZERO; gvar_scalars.len()];
2297 let count = gvar.compute_scalars(instance.normalized_coords(), &mut expected);
2298 assert_eq!(gvar_scalars, &expected[..count]);
2299
2300 let hvar_scalars = instance.hvar_scalars();
2301 assert!(!hvar_scalars.is_empty());
2302 let hvar = instance.hvar().unwrap();
2303 let regions = hvar
2304 .item_variation_store()
2305 .unwrap()
2306 .variation_region_list()
2307 .unwrap();
2308 assert_eq!(
2309 hvar_scalars.len(),
2310 regions
2311 .variation_regions()
2312 .len()
2313 .min(MAX_PRECOMPUTED_SCALARS)
2314 );
2315 expected.resize(hvar_scalars.len(), Fixed::ZERO);
2316 let count = hvar.compute_scalars(instance.normalized_coords(), &mut expected);
2317 assert_eq!(hvar_scalars, &expected[..count]);
2318 let clone = instance.clone();
2319 assert!(core::ptr::eq(gvar_scalars, clone.gvar_scalars()));
2320 assert!(core::ptr::eq(hvar_scalars, clone.hvar_scalars()));
2321 assert!(font.gvar_scalars().is_empty());
2322 assert!(font.hvar_scalars().is_empty());
2323 }
2324
2325 #[cfg(feature = "std")]
2326 #[test]
2327 fn vvar_scalars_are_lazy_bounded_and_shared() {
2328 let font = Font::new(font_test_data::MPLUS1CODE_VERTICAL_SUBSET, 0).unwrap();
2329 let instance = font
2330 .instance_builder()
2331 .normalized_coords([NormalizedCoord::from_f32(-0.75)])
2332 .build();
2333 let Repr::Varied(varied) = &instance.0 else {
2334 panic!("expected a varied instance");
2335 };
2336 assert!(varied.sfnt().vvar_scalars.get().is_none());
2337
2338 let scalars = instance.vvar_scalars();
2339 assert!(!scalars.is_empty());
2340 let vvar = instance.vvar().unwrap();
2341 let regions = vvar
2342 .item_variation_store()
2343 .unwrap()
2344 .variation_region_list()
2345 .unwrap();
2346 assert_eq!(
2347 scalars.len(),
2348 regions
2349 .variation_regions()
2350 .len()
2351 .min(MAX_PRECOMPUTED_SCALARS)
2352 );
2353 let mut expected = alloc::vec![Fixed::ZERO; scalars.len()];
2354 let count = vvar.compute_scalars(instance.normalized_coords(), &mut expected);
2355 assert_eq!(scalars, &expected[..count]);
2356 assert!(core::ptr::eq(scalars, instance.clone().vvar_scalars()));
2357 assert!(font.vvar_scalars().is_empty());
2358 }
2359
2360 #[test]
2361 fn font_outline_context_uses_stated_vertical_metrics() {
2362 let data = font_test_data::MPLUS1CODE_VERTICAL_SUBSET;
2363 let font = Font::new(data, 0).unwrap();
2364 let direct = crate::FontRef::new(data).unwrap();
2365 let gid = GlyphId::new(1);
2366 let glyph = direct
2367 .loca(None)
2368 .unwrap()
2369 .get_glyf(gid, &direct.glyf().unwrap())
2370 .unwrap()
2371 .unwrap();
2372 let tsb = direct.vmtx().unwrap().side_bearing(gid).unwrap();
2373 let context = &font;
2374 let mut outline = Outline::<Unscaled>::new();
2375 let loaded = outline.load_with(&context, gid, &Unscaled, None).unwrap();
2376 assert_eq!(
2377 loaded.phantom_points()[2].y,
2378 glyph.y_max() as i32 + tsb as i32
2379 );
2380 }
2381
2382 #[test]
2383 fn outline_context_uses_unvaried_h_line_metrics() {
2384 let font = Font::new(MVAR_FONT, 0).unwrap();
2385 let far = font
2386 .instance_builder()
2387 .normalized_coords([NormalizedCoord::from_f32(1.0); 12])
2388 .build();
2389 let line = font.shared().metrics().h_line().unwrap();
2390 assert_ne!(Some(line), font.metrics().typo_line);
2391 let expected = (line.ascender.to_i32(), line.descender.to_i32());
2392 assert_ne!(far.metrics().h_line(), font.metrics().h_line());
2393 assert_eq!((&font).h_line_metrics(), expected);
2394 assert_eq!((&far).h_line_metrics(), expected);
2395 }
2396
2397 #[test]
2398 fn named_instances() {
2399 let font = Font::new(font_test_data::CANTARELL_VF_TRIMMED, 0).unwrap();
2400 let cases = [
2401 (0, 100.0),
2403 (1, 300.0),
2404 (2, 400.0),
2405 (3, 700.0),
2406 (4, 800.0),
2407 ];
2408 for (index, weight) in cases {
2409 let named_instance = font.instance_builder().named_instance(index).build();
2410 let var_instance = font
2411 .instance_builder()
2412 .variations([("wght", weight)])
2413 .build();
2414 assert_eq!(
2415 named_instance.normalized_coords(),
2416 var_instance.normalized_coords(),
2417 "index={index}"
2418 );
2419 }
2420 let invalid_instance = font.instance_builder().named_instance(5).build();
2422 assert!(
2423 invalid_instance.normalized_coords().is_empty(),
2424 "out of bounds index should give default instance"
2425 );
2426 }
2427
2428 #[test]
2429 fn named_instance_with_overrides_override_named_value() {
2430 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2431 let actual = font
2432 .instance_builder()
2433 .named_instance_with_overrides(3, [("FILL", 1.0)])
2434 .build();
2435 let expected = font
2436 .instance_builder()
2437 .variations([
2438 ("FILL", 1.0),
2439 ("GRAD", 0.0),
2440 ("opsz", 24.0),
2441 ("wght", 400.0),
2442 ])
2443 .build();
2444 assert_eq!(actual.normalized_coords(), expected.normalized_coords());
2445 }
2446
2447 #[test]
2448 fn named_instance_with_overrides_invalid_index_uses_overrides_only() {
2449 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2450 let actual = font
2451 .instance_builder()
2452 .named_instance_with_overrides(999, [("FILL", 1.0), ("ZZZZ", 123.0)])
2453 .build();
2454 let expected = font
2455 .instance_builder()
2456 .variations([("FILL", 1.0), ("ZZZZ", 123.0)])
2457 .build();
2458 assert_eq!(actual.normalized_coords(), expected.normalized_coords());
2459 assert_eq!(actual.normalized_coords().len(), 4);
2460 }
2461
2462 #[test]
2463 fn named_instance_with_overrides_overwrites_previous_settings() {
2464 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2465 let actual = font
2466 .instance_builder()
2467 .variations([("FILL", 0.0), ("wght", 100.0)])
2468 .named_instance_with_overrides(5, [("GRAD", -25.0)])
2469 .build();
2470 let expected = font
2471 .instance_builder()
2472 .variations([
2473 ("FILL", 0.0),
2474 ("GRAD", -25.0),
2475 ("opsz", 24.0),
2476 ("wght", 600.0),
2477 ])
2478 .build();
2479 assert_eq!(actual.normalized_coords(), expected.normalized_coords());
2480 }
2481
2482 #[test]
2483 fn feature_variations() {
2484 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2485 let cases = [
2486 (0.0, [None, None]),
2488 (0.5, [None, None]),
2489 (0.98, [None, None]),
2490 (0.99, [Some(0), None]),
2491 (1.0, [Some(0), None]),
2492 ];
2493 for (fill, [gsub, gpos]) in cases {
2494 let instance = font.instance_builder().variations([("FILL", fill)]).build();
2495 let feature_vars = instance.feature_variations();
2496 let actual = [feature_vars.gsub, feature_vars.gpos];
2497 assert_eq!(actual, [gsub, gpos], "fill={fill}");
2498 }
2499 }
2500
2501 #[test]
2502 fn default_instance_evaluates_and_caches_feature_variations() {
2503 let source = crate::FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).unwrap();
2504 let tables = super::super::TableFunction::new(Arc::new(move |tag| {
2505 if tag == Tag::new(b"GSUB") {
2506 return Some(super::super::Blob::from(vec![
2509 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16,
2510 0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 0, 1, 0, 0, 0, 0, 0, 0,
2511 ]));
2512 }
2513 if tag == Tag::new(b"GPOS") {
2514 return None;
2515 }
2516 source
2517 .data_for_tag(tag)
2518 .map(|data| super::super::Blob::from(data.as_bytes().to_vec()))
2519 }));
2520 let font = Font::new(tables, 0).unwrap();
2521 assert_eq!(
2522 font.shared()
2523 .sfnt()
2524 .unwrap()
2525 .default_location
2526 .feature_vars
2527 .gsub
2528 .load(Ordering::Acquire),
2529 FeatureVarsStorage::UNCHECKED
2530 );
2531 let expected = SelectedFeatureVariations {
2532 gsub: Some(0),
2533 gpos: None,
2534 has_lookup_variations: false,
2535 };
2536 assert_eq!(font.feature_variations(), expected);
2537 assert_eq!(
2538 font.shared()
2539 .sfnt()
2540 .unwrap()
2541 .default_location
2542 .feature_vars
2543 .gsub
2544 .load(Ordering::Acquire),
2545 0
2546 );
2547 assert_eq!(font.default_instance().feature_variations(), expected);
2548 let default = font
2549 .instance_builder()
2550 .normalized_coords([NormalizedCoord::ZERO; 4])
2551 .build();
2552 assert!(default.normalized_coords().is_empty());
2553 assert_eq!(default.feature_variations(), expected);
2554 let varied = font
2555 .instance_builder()
2556 .normalized_coords([NormalizedCoord::from_f32(0.5); 4])
2557 .build();
2558 assert_eq!(
2559 varied.feature_variations(),
2560 SelectedFeatureVariations::default()
2561 );
2562 assert_eq!(font.feature_variations(), expected);
2563 }
2564
2565 #[test]
2566 fn feature_variation_cache_marks_both_absent() {
2567 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2568 let instance = font.instance_builder().variations([("FILL", 0.5)]).build();
2569 let storage = instance.feature_vars();
2570 assert_eq!(
2571 storage.gsub.load(Ordering::Acquire),
2572 FeatureVarsStorage::UNCHECKED
2573 );
2574 assert_eq!(
2575 storage.gpos.load(Ordering::Acquire),
2576 FeatureVarsStorage::UNCHECKED
2577 );
2578 assert_eq!(
2579 instance.feature_variations(),
2580 SelectedFeatureVariations::default()
2581 );
2582 assert_eq!(
2583 storage.gsub.load(Ordering::Acquire),
2584 FeatureVarsStorage::ABSENT
2585 );
2586 assert_eq!(
2587 storage.gpos.load(Ordering::Acquire),
2588 FeatureVarsStorage::ABSENT
2589 );
2590 }
2591
2592 #[test]
2593 fn feature_variation_cache_is_thread_safe_and_stable() {
2594 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2595 let instance = font.instance_builder().variations([("FILL", 1.0)]).build();
2596 std::thread::scope(|scope| {
2597 for _ in 0..8 {
2598 scope.spawn(|| {
2599 for _ in 0..64 {
2600 let vars = instance.feature_variations();
2601 assert_eq!(
2602 vars,
2603 SelectedFeatureVariations {
2604 gsub: Some(0),
2605 gpos: None,
2606 has_lookup_variations: false,
2607 }
2608 );
2609 }
2610 });
2611 }
2612 });
2613 let storage = instance.feature_vars();
2614 assert_eq!(storage.gsub.load(Ordering::Acquire), 0);
2615 assert_eq!(
2616 storage.gpos.load(Ordering::Acquire),
2617 FeatureVarsStorage::ABSENT
2618 );
2619 }
2620
2621 #[test]
2622 fn variations_last_value_wins_and_unknown_axis_ignored() {
2623 let font = Font::new(font_test_data::CANTARELL_VF_TRIMMED, 0).unwrap();
2624 let expected = font
2625 .instance_builder()
2626 .variations([("wght", 700.0)])
2627 .build();
2628 let repeated_axis = font
2629 .instance_builder()
2630 .variations([("wght", 100.0), ("wght", 700.0)])
2631 .build();
2632 assert_eq!(
2633 repeated_axis.normalized_coords(),
2634 expected.normalized_coords()
2635 );
2636 let unknown_axis = font
2637 .instance_builder()
2638 .variations([("wght", 700.0), ("ZZZZ", 123.0)])
2639 .build();
2640 assert_eq!(
2641 unknown_axis.normalized_coords(),
2642 expected.normalized_coords()
2643 );
2644 }
2645
2646 #[test]
2647 fn later_variation_call_overwrites_previous() {
2648 let font = Font::new(font_test_data::CANTARELL_VF_TRIMMED, 0).unwrap();
2649 let overwritten = font
2650 .instance_builder()
2651 .variations([("wght", 700.0)])
2652 .variations([("wght", 100.0)])
2653 .build();
2654 let expected = font
2655 .instance_builder()
2656 .variations([("wght", 100.0)])
2657 .build();
2658 assert_eq!(
2659 overwritten.normalized_coords(),
2660 expected.normalized_coords()
2661 );
2662 }
2663
2664 #[test]
2665 fn normalized_coords_empty_resets_to_default_instance() {
2666 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2667 let instance = font.instance_builder().normalized_coords([]).build();
2668 assert!(instance.normalized_coords().is_empty());
2669 }
2670
2671 #[test]
2672 fn normalized_coords_truncates_and_pads() {
2673 let font = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2674 let axis_count = font.tables().fvar().unwrap().axis_count() as usize;
2675 let values = [0.25, -0.5, 1.0, 0.75, -0.25].map(NormalizedCoord::from_f32);
2676 let instance = font.instance_builder().normalized_coords(values).build();
2677 let coords = instance.normalized_coords();
2678 assert_eq!(coords.len(), axis_count);
2679 let copied = values.len().min(axis_count);
2680 assert_eq!(&coords[..copied], &values[..copied]);
2681 assert!(coords[copied..]
2682 .iter()
2683 .all(|&coord| coord == NormalizedCoord::ZERO));
2684 }
2685
2686 const MVAR_FONT: &[u8] = font_test_data::AMSTELVAR_AVAR2_A;
2688
2689 #[test]
2690 fn ordinary_metrics_do_not_read_post() {
2691 let asked = Arc::new(std::sync::Mutex::new(Vec::new()));
2692 let logged = asked.clone();
2693 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag| {
2694 logged.lock().unwrap().push(tag);
2695 let font = crate::FontRef::new(font_test_data::TINOS_SUBSET).ok()?;
2696 font.table_data(tag)
2697 .map(|data| Blob::from(data.as_bytes().to_vec()))
2698 });
2699 let font = Font::new(source, 0).unwrap();
2700 asked.lock().unwrap().clear();
2701 let _ = font.metrics();
2702 assert!(!asked.lock().unwrap().contains(&Tag::new(b"post")));
2703 let _ = font.style_metrics();
2704 assert!(asked.lock().unwrap().contains(&Tag::new(b"post")));
2705 assert!(core::ptr::eq(font.style_metrics(), font.style_metrics()));
2706 }
2707
2708 #[test]
2709 fn an_instance_varies_its_metrics() {
2710 let font = Font::new(MVAR_FONT, 0).unwrap();
2711 let far = font
2712 .instance_builder()
2713 .normalized_coords([NormalizedCoord::from_f32(1.0); 12])
2714 .build();
2715 assert!(!far.normalized_coords().is_empty());
2716 assert_ne!(far.metrics(), font.metrics());
2717 }
2718
2719 #[test]
2720 fn an_instance_varies_and_shares_its_style_metrics() {
2721 let font = Font::new(MVAR_FONT, 0).unwrap();
2722 let far = font
2723 .instance_builder()
2724 .normalized_coords([NormalizedCoord::from_f32(1.0); 12])
2725 .build();
2726 assert_ne!(
2727 far.style_metrics().strike_through,
2728 font.style_metrics().strike_through
2729 );
2730 assert!(core::ptr::eq(
2731 far.style_metrics(),
2732 far.clone().style_metrics()
2733 ));
2734 }
2735
2736 #[test]
2737 fn an_instance_at_the_default_location_shares_the_font_metrics() {
2738 let font = Font::new(MVAR_FONT, 0).unwrap();
2740 let default = font.instance_builder().build();
2741 assert!(core::ptr::eq(default.metrics(), font.metrics()));
2742 }
2743
2744 #[test]
2745 fn clones_of_an_instance_share_one_set_of_metrics() {
2746 let font = Font::new(MVAR_FONT, 0).unwrap();
2747 let far = font
2748 .instance_builder()
2749 .normalized_coords([NormalizedCoord::from_f32(1.0); 12])
2750 .build();
2751 let shared = far.clone();
2752 assert!(core::ptr::eq(far.metrics(), shared.metrics()));
2754 }
2755
2756 #[test]
2757 fn the_fixed_numbers_do_not_resolve_a_location() {
2758 let asked = Arc::new(std::sync::Mutex::new(Vec::new()));
2761 let logged = asked.clone();
2762 let source: Arc<dyn Fn(Tag) -> Option<crate::model::Blob> + Send + Sync> =
2763 Arc::new(move |tag: Tag| {
2764 logged.lock().unwrap().push(tag);
2765 let font = crate::FontRef::new(MVAR_FONT).ok()?;
2766 Some(crate::model::Blob::from(
2767 font.table_data(tag)?.as_bytes().to_vec(),
2768 ))
2769 });
2770 let font = Font::new(source, 0).unwrap();
2771 let far = font
2772 .instance_builder()
2773 .normalized_coords([NormalizedCoord::from_f32(1.0); 12])
2774 .build();
2775 asked.lock().unwrap().clear();
2776
2777 assert_eq!(far.num_glyphs(), font.num_glyphs());
2778 assert_eq!(far.units_per_em(), font.units_per_em());
2779 assert!(
2780 !asked.lock().unwrap().contains(&Tag::new(b"MVAR")),
2781 "reading a fixed number resolved the location"
2782 );
2783
2784 let _ = far.metrics();
2786 assert!(asked.lock().unwrap().contains(&Tag::new(b"MVAR")));
2787 }
2788
2789 #[test]
2790 fn a_default_instance_is_only_its_font() {
2791 let font = Font::new(MVAR_FONT, 0).unwrap();
2794 for instance in [
2795 font.default_instance(),
2796 font.instance_builder().build(),
2797 font.instance_builder()
2800 .normalized_coords([NormalizedCoord::from_f32(0.0); 12])
2801 .build(),
2802 ] {
2803 assert!(matches!(instance.0, Repr::Default(_)));
2804 assert!(instance.normalized_coords().is_empty());
2805 assert!(core::ptr::eq(instance.metrics(), font.metrics()));
2806 }
2807 }
2808
2809 #[test]
2810 fn a_location_away_from_the_default_is_owned() {
2811 let font = Font::new(MVAR_FONT, 0).unwrap();
2812 let far = font
2813 .instance_builder()
2814 .normalized_coords([NormalizedCoord::from_f32(1.0); 12])
2815 .build();
2816 assert!(matches!(far.0, Repr::Varied(_)));
2817 assert_eq!(far.normalized_coords().len(), 12);
2818 }
2819}