Skip to main content

read_fonts/model/
font.rs

1//! Font representation.
2
3mod 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// Do our best to not expose this to users through docs or rust-analyzer.
19#[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/// A font at one location in its design space.
75///
76/// Cloning is cheap: clones share one instance rather than copying it.
77#[derive(Clone)]
78pub struct Font(Repr);
79
80#[derive(Clone)]
81enum Repr {
82    Default(SharedFont),
83    Varied(Arc<VariedInstance>),
84}
85
86impl Font {
87    /// Creates a font from `source`, at its default location.
88    ///
89    /// `index` selects a font within the data:
90    ///
91    /// - SFNT: the font in a collection (`ttc` or `otc`). Data holding a
92    ///   single font accepts only zero.
93    /// - Standalone CFF: the font in the CFF font set.
94    /// - Type 1: ignored, since the data holds one font.
95    /// - [`TableFunction`]: ignored, since the function supplies the tables
96    ///   of one font.
97    ///
98    /// Returns `None` if the source holds no font in a supported format, or
99    /// none at `index`.
100    pub fn new(source: impl Into<Source>, index: u32) -> Option<Self> {
101        Some(Font(Repr::Default(SharedFont::new(source, index)?)))
102    }
103
104    /// Returns the font's variation axes, which are empty if the font is not
105    /// variable.
106    pub fn axes(&self) -> Axes<'_> {
107        Axes::new(self)
108    }
109
110    /// Returns the named instances defined by the font.
111    pub fn named_instances(&self) -> NamedInstances<'_> {
112        NamedInstances::new(self)
113    }
114
115    /// Returns a builder for another instance of the same font.
116    ///
117    /// The two share everything the location does not change, so deriving one
118    /// instance from another costs no more than reading the tables once.
119    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    /// Returns this font at its default location.
128    #[inline]
129    pub fn default_instance(&self) -> Font {
130        Font(Repr::Default(self.shared().clone()))
131    }
132
133    /// Returns the normalized variation coordinates for this font instance.
134    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    /// Returns the layout feature variations this instance selects.
142    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            // Only an SFNT font has layout tables to vary.
148            None => SelectedFeatureVariations::default(),
149        }
150    }
151
152    /// Returns the number of glyphs in the font.
153    ///
154    /// Fixed for the font: no location varies it.
155    #[inline]
156    pub fn num_glyphs(&self) -> u32 {
157        self.shared().num_glyphs()
158    }
159
160    /// Returns the size of the em square, in font units.
161    ///
162    /// Fixed for the font: no location varies it.
163    #[inline]
164    pub fn units_per_em(&self) -> u16 {
165        self.shared().units_per_em()
166    }
167
168    /// Returns the width, style and weight of the default instance.
169    ///
170    /// For an SFNT font, these come from the OS/2 table, with the `post`
171    /// italic angle for an oblique style. If there is no OS/2 table, they
172    /// come from `head`. These tables do not vary, so every instance of a
173    /// variable font returns the same values. To get the attributes at a
174    /// location, use [`Attributes::from_variations`].
175    ///
176    /// For a Type 1 or standalone CFF font, these come from the `Weight` name
177    /// and `ItalicAngle`.
178    pub fn default_attributes(&self) -> Attributes {
179        Attributes::from_font(self)
180    }
181
182    /// Returns `true` if the font needs its TrueType instructions to draw
183    /// correct contours.
184    ///
185    /// Such fonts are often called "tricky". They are recognized by family name
186    /// or by the checksums of the `cvt`, `fpgm` and `prep` tables, and only
187    /// fonts with `glyf` outlines qualify. The result is computed once and
188    /// cached. See [`is_hint_reliant`](crate::tables::glyf::is_hint_reliant).
189    pub fn is_hint_reliant(&self) -> bool {
190        use core::sync::atomic::Ordering;
191        const UNKNOWN: u8 = 0;
192        const KNOWN: u8 = 1;
193        // Only an SFNT font can have TrueType outlines.
194        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    /// Returns the font's names and other localized strings.
209    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    /// Returns the name of a glyph, synthesizing `gidNNN` if none is stored.
218    ///
219    /// Returns `None` for a glyph outside the font.
220    pub fn glyph_name(&self, glyph: GlyphId) -> Option<GlyphName> {
221        name::glyph_name(self, glyph)
222    }
223
224    /// Returns the identifier and name of every glyph in the font.
225    ///
226    /// Names come from `post`, then `CFF`, or from Type 1 charstrings. A
227    /// missing name is synthesized as `gidNNN`.
228    pub fn glyph_names(&self) -> impl Iterator<Item = (GlyphId, GlyphName)> + '_ {
229        name::glyph_names(self)
230    }
231
232    /// Returns the glyph named `name`.
233    ///
234    /// Names come from the same sources as [`glyph_names`](Self::glyph_names),
235    /// but a synthesized `gidNNN` name matches nothing. Where several glyphs
236    /// share a name, the first is returned.
237    ///
238    /// The first call sorts the font's names, and later calls search them.
239    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    /// Returns `true` if the font is a standalone CID-keyed CFF font, whose
248    /// glyphs are identified by CID.
249    ///
250    /// A `CFF` table in an SFNT font may be CID-keyed too, but its glyphs
251    /// are identified by glyph index, as in any SFNT font.
252    pub fn is_cid_keyed(&self) -> bool {
253        self.shared()
254            .standalone_cff()
255            .is_some_and(|cff| cff.is_cid())
256    }
257
258    /// Returns the largest CID that a CID-keyed font maps to a glyph.
259    ///
260    /// Glyphs in a CID-keyed font can be addressed by CID, so one more than
261    /// this value is the number of addressable glyph IDs. Returns `None` if the
262    /// font is not CID-keyed.
263    pub fn max_cid(&self) -> Option<u32> {
264        Some(self.shared().cid_glyphs()?.len() as u32 - 1)
265    }
266
267    /// Returns the glyph for `cid` in a CID-keyed font.
268    ///
269    /// Where several glyphs have the same CID, the first is returned. Returns
270    /// `None` if the font is not CID-keyed or no glyph has `cid`.
271    ///
272    /// The first call builds a table from each CID to its glyph.
273    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    /// Returns the font's character mappings.
279    pub fn charmap(&self) -> Charmap<'_> {
280        Charmap::new(self)
281    }
282
283    /// Returns the metrics describing the font as a whole, at this
284    /// instance's location.
285    pub fn metrics(&self) -> &Metrics {
286        match &self.0 {
287            // Nothing varies there, so the font already holds the answer.
288            Repr::Default(font) => font.metrics(),
289            // A Type 1 font's metrics come from dictionaries that no
290            // location changes.
291            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    /// Returns the font's style measurements at this instance's location.
302    ///
303    /// This reads `post` when requested; [`metrics`](Self::metrics) does not.
304    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    /// Returns measurements of individual glyphs, at this instance's
324    /// location.
325    #[inline]
326    pub fn glyph_metrics(&self) -> GlyphMetrics<'_> {
327        GlyphMetrics::new(self, self.metrics(), self.normalized_coords())
328    }
329
330    /// Returns the font's outlines, using the source order in
331    /// [`GlyphSourceOrder::DEFAULT`].
332    pub fn outline_glyphs(&self) -> OutlineGlyphs<'_> {
333        OutlineGlyphs::new(self, GlyphSourceOrder::DEFAULT)
334    }
335
336    /// Returns the font's outlines, using the source order in `order`.
337    ///
338    /// The first of `Glyf`, `Cff2`, `Cff` and `Type1` that the font has
339    /// becomes the base source for every glyph. If `Varc` comes before the
340    /// base source, it applies to the glyphs it covers, and their components
341    /// are drawn from the base source. Sources that are not outlines are
342    /// ignored.
343    pub fn outline_glyphs_with(&self, order: GlyphSourceOrder) -> OutlineGlyphs<'_> {
344        OutlineGlyphs::new(self, order)
345    }
346
347    /// Returns the font's bitmaps, from the first of `sbix`, `CBDT` and
348    /// `EBDT` that the font has.
349    pub fn bitmap_strikes(&self) -> BitmapStrikes<'_> {
350        BitmapStrikes::new(self, GlyphSourceOrder::BITMAPS)
351    }
352
353    /// Returns the font's bitmaps, using the source order in `order`.
354    ///
355    /// The bitmaps come from the first of `Sbix`, `Cbdt` and `Ebdt` in
356    /// `order` that the font has. Sources that are not bitmaps are ignored.
357    pub fn bitmap_strikes_with(&self, order: GlyphSourceOrder) -> BitmapStrikes<'_> {
358        BitmapStrikes::new(self, order)
359    }
360
361    /// Returns the font's color glyphs.
362    pub fn color_glyphs(&self) -> ColorGlyphs<'_> {
363        ColorGlyphs::new(self)
364    }
365
366    /// Returns the font's color palettes.
367    pub fn palettes(&self) -> Palettes<'_> {
368        Palettes::new(self)
369    }
370
371    /// Returns the underlying source of font data.
372    #[inline]
373    pub fn source(&self) -> &Source {
374        self.shared().source()
375    }
376
377    /// Returns the underlying kind of the font.
378    #[inline]
379    pub fn kind(&self) -> Kind<'_> {
380        self.shared().kind()
381    }
382
383    /// Returns an object that provides access to individual font tables.
384    ///
385    /// For non-SFNT fonts, this will return an empty set of tables.
386    #[inline]
387    pub fn tables(&self) -> &Tables {
388        self.shared().tables()
389    }
390}
391
392impl Font {
393    /// Returns the slot for data that Skrifa derives from the font at this
394    /// location.
395    ///
396    /// At the default location, the slot belongs to the shared font.
397    /// Elsewhere, it belongs to the instance.
398    pub(super) fn scaling_data(&self) -> &Once<Box<dyn Any + Send + Sync>> {
399        &self.location_caches().scaling_data
400    }
401
402    /// Returns the caches for this instance's location.
403    ///
404    /// At the default location, the caches belong to the shared font.
405    /// Elsewhere, they belong to the instance.
406    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    /// Returns the SFNT caches for this instance's location, or `None` if
414    /// the font is not an SFNT font.
415    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    /// The state every instance of this font shares.
426    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    /// Returns the axes of a Type 1 multiple master font, or `None` for any
434    /// other font.
435    pub(crate) fn type1_axes(&self) -> Option<&[VariationAxisRecord]> {
436        self.shared().multiple_master()?;
437        Some(self.shared().type1_axes())
438    }
439
440    /// Returns a Type 1 font drawn at this instance's location.
441    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    /// Returns the horizontal glyph metrics, from `HMTX` or `hmtx`.
455    #[inline]
456    pub(crate) fn h_metric_records(&self) -> &GlyphMetricRecords<'_> {
457        self.shared().h_metric_records()
458    }
459
460    /// Returns the vertical glyph metrics, from `VMTX` or `vmtx`.
461    #[inline]
462    pub(crate) fn v_metric_records(&self) -> &GlyphMetricRecords<'_> {
463        self.shared().v_metric_records()
464    }
465
466    /// Returns the outline tables.
467    #[inline]
468    pub(crate) fn selected_glyf_loca(&self) -> Option<&(Glyf<'_>, Loca<'_>)> {
469        self.shared().selected_glyf_loca()
470    }
471
472    /// Returns `HVAR`.
473    #[inline]
474    pub(crate) fn hvar(&self) -> Option<&Hvar<'_>> {
475        self.shared().hvar()
476    }
477
478    /// Returns `VVAR`.
479    #[inline]
480    pub(crate) fn vvar(&self) -> Option<&Vvar<'_>> {
481        self.shared().vvar()
482    }
483
484    /// Returns `GVAR`, or `gvar` without it.
485    #[inline]
486    pub(crate) fn selected_gvar(&self) -> Option<&SelectedGvar<'_>> {
487        self.shared().selected_gvar()
488    }
489
490    /// Returns the `gvar` shared tuple scalars at this location, computed
491    /// once.
492    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    /// Returns the `HVAR` region scalars at this location, computed once.
505    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    /// Returns the `VVAR` region scalars at this location, computed once.
519    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    /// Returns the `COLR` region scalars at this location, computed once.
533    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    /// Returns the scalars `cache` selects for this location, filling it
547    /// with `compute` the first time. The default location needs none.
548    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    /// Returns the `CFF` charstring outlines from SFNT tables.
563    #[inline]
564    pub(crate) fn cff(&self) -> Option<&CffFontRef<'_>> {
565        self.shared().cff()
566    }
567
568    /// Returns the charstrings for `source`.
569    ///
570    /// For an SFNT font, these come from the `CFF2` or `CFF` table. A
571    /// standalone CFF font provides its own charstrings as `CFF`.
572    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    /// Returns subfont `index` of the charstrings for `source`, parsed at
582    /// this instance's location.
583    ///
584    /// The first [`CACHED_SUBFONTS`] subfonts are parsed once and kept,
585    /// since nearly every glyph in a font shares one of them. A standalone
586    /// CFF font parses them when the font is read. An SFNT font parses each
587    /// once per location, when a glyph first needs it. A subfont past them
588    /// is parsed each time it is asked for, so a font with thousands of
589    /// subfonts costs no more memory than one with eight.
590    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    /// Returns `true` if the font has glyphs from `source`, one of the
615    /// bitmap tables, `COLR` or `VARC`.
616    ///
617    /// Each source is probed the first time it is asked about and the answer
618    /// kept, so asking loads no table that measuring with it would not.
619    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    /// Returns `true` if the font has glyphs from any bitmap table, `COLR`
649    /// or `VARC` that `order` lists.
650    ///
651    /// Reads the cached results once and probes only the sources not yet
652    /// checked.
653    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    /// Returns the probed state of the bitmap, color and composite sources.
680    #[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    /// Returns the variable composites, read once for the font.
689    #[inline]
690    pub(crate) fn varc_outlines(&self) -> Option<&varc::Outlines<'_>> {
691        self.shared().varc_outlines()
692    }
693
694    /// Returns `COLR` with its variation tables, read once for the font.
695    #[inline]
696    pub(crate) fn colr_tables(&self) -> Option<&ColrTables<'_>> {
697        self.shared().colr_tables()
698    }
699
700    /// Returns `CPAL`, read once for the font.
701    #[inline]
702    pub(crate) fn cpal(&self) -> Option<&Cpal<'_>> {
703        self.shared().cpal()
704    }
705
706    /// Returns `sbix`, read once for the font.
707    #[inline]
708    pub(crate) fn sbix(&self) -> Option<&Sbix<'_>> {
709        self.shared().sbix()
710    }
711
712    /// Returns `CBLC` and `CBDT`, read once for the font.
713    #[inline]
714    pub(crate) fn cbdt(&self) -> Option<&(Cblc<'_>, Cbdt<'_>)> {
715        self.shared().cbdt()
716    }
717
718    /// Returns `EBLC` and `EBDT`, read once for the font.
719    #[inline]
720    pub(crate) fn ebdt(&self) -> Option<&(Eblc<'_>, Ebdt<'_>)> {
721        self.shared().ebdt()
722    }
723
724    /// Returns `VORG`.
725    #[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/// The underlying type of a font.
805#[derive(Clone)]
806pub enum Kind<'a> {
807    /// An SFNT-based font represented by a set of tables and an index.
808    Sfnt(&'a Tables, u32),
809    /// An Adobe Type 1 font.
810    Type1(&'a Type1Font),
811    /// A standalone CFF font with an associated index.
812    Cff(CffFontRef<'a>, u32),
813}
814
815/// Builder for configuring a font instance.
816pub struct InstanceBuilder {
817    font: SharedFont,
818    coords: CoordStorage,
819    /// The blend coordinates of a Type 1 multiple master location, which
820    /// `coords` holds rounded to 2.14. The weights are computed from these.
821    type1_blend: SmallVec<[Fixed; 4]>,
822}
823
824impl InstanceBuilder {
825    /// Sets the variations for the font instance from an unordered sequence of
826    /// variations in user space.
827    ///
828    /// Omitted axes will be set to their default values. Unsupported axes are
829    /// ignored. If an axis is specified multiple times, the last value is used.
830    ///
831    /// This will overwrite any previous variation settings.
832    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    /// Sets the variations for the font instance from an ordered sequence
842    /// of normalized coordinates.
843    ///
844    /// If the number of provided coordinates is less than the number of axes,
845    /// the remaining axes will be set to their default values. If the number
846    /// of provided coordinates is greater than the number of axes, the extra
847    /// coordinates will be ignored.
848    ///
849    /// This will overwrite any previous variation settings.
850    pub fn normalized_coords(mut self, coords: impl IntoIterator<Item = NormalizedCoord>) -> Self {
851        self.set_coords(coords);
852        self
853    }
854
855    /// Sets the variations for the font instance from a named instance.
856    ///
857    /// If the given named instance index is invalid, then variation settings
858    /// will be reset to default.
859    ///
860    /// This will overwrite any previous variation settings.
861    pub fn named_instance(mut self, index: u32) -> Self {
862        self.set_named_instance(index);
863        self
864    }
865
866    /// Sets the variations for the font instance from a named instance, with
867    /// additional overrides.
868    ///
869    /// If the given named instance index is invalid, then it is ignored and
870    /// only overrides are applied.
871    ///
872    /// This will overwrite any previous variation settings.
873    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    /// Builds the font instance.
883    pub fn build(self) -> Font {
884        // An all-default location is just the font, so nothing needs owning.
885        // This is what keeps `VariedInstance::coords` non-empty.
886        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            // A standalone CFF font has no axes, so no coordinates reach
906            // here; it is at its default location.
907            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            // Design coordinates, starting from each axis's default.
926            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            // An axis no variation names keeps the default blend. Its
942            // default design coordinate doesn't always map back to it.
943            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            // Blend coordinates, with the default blend for any not given.
964            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    /// Sets the location of a Type 1 multiple master font.
989    ///
990    /// Every blend is kept, even all zeros, which is a master rather than
991    /// the font's default.
992    ///
993    /// The weights are computed from `blend` itself. The coordinates are
994    /// those the weights give back, which rounding in the weights can move
995    /// from `blend` by the smallest step, as FreeType reports them.
996    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        // A Type 1 multiple master font has no named instances, so only the
1029        // overrides apply.
1030        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
1049// Helper to extract an iterator of Variation from a named instance index.
1050fn 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
1068/// Helper for setting variations.
1069///
1070/// Pulled out into a separate function to avoid borrow checker issues.
1071fn 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/// The state every instance of a font shares.
1089///
1090/// Reference counted internally: cloning it costs no more than the count,
1091/// and it is thread safe.
1092#[derive(Clone)]
1093pub(super) struct SharedFont(Arc<SharedFontRepr>);
1094
1095impl SharedFont {
1096    /// Creates a new font from the given source and font index.
1097    ///
1098    /// See [`Font::new`] for how `index` is used.
1099    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                    // A subfont that cannot be read fails only its own
1119                    // glyphs, not the font.
1120                    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    /// Returns the underlying source of font data.
1149    fn source(&self) -> &Source {
1150        &self.0.source
1151    }
1152
1153    /// Returns the underlying kind of the font.
1154    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    /// Returns the design space of a Type 1 multiple master font.
1163    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    /// Returns the axes of a Type 1 multiple master font, which is empty
1171    /// for any other font.
1172    fn type1_axes(&self) -> &[VariationAxisRecord] {
1173        match &self.0.kind {
1174            KindRepr::Type1(type1) => &type1.axes,
1175            _ => &[],
1176        }
1177    }
1178
1179    /// Returns the metrics describing the font as a whole, at its default
1180    /// location.
1181    #[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    /// Returns the style measurements at the font's default location.
1194    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    /// Returns the number of glyphs in the font.
1209    ///
1210    /// Fixed for the font: no location varies it.
1211    #[inline]
1212    pub(super) fn num_glyphs(&self) -> u32 {
1213        self.metrics().num_glyphs
1214    }
1215
1216    /// Returns the size of the em square, in font units.
1217    ///
1218    /// Fixed for the font: no location varies it.
1219    #[inline]
1220    fn units_per_em(&self) -> u16 {
1221        self.metrics().units_per_em
1222    }
1223
1224    /// Returns the caches of an SFNT font, or `None` for a font of another
1225    /// kind.
1226    #[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    /// Returns the tables behind this font, for a cache that holds them.
1235    fn tables_arc(&self) -> Option<&Arc<Tables>> {
1236        self.sfnt().map(|sfnt| &sfnt.tables)
1237    }
1238
1239    /// Returns the selected character maps, parsed once for the font.
1240    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    /// Returns all selectable cmap subtables, parsed on first use.
1254    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    /// Returns the horizontal glyph metrics, parsed once for the font.
1297    #[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    /// Returns the vertical glyph metrics, parsed once for the font.
1314    ///
1315    /// Read on demand, unlike the horizontal metrics: most text is
1316    /// horizontal and never asks.
1317    #[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    /// Returns the outline tables, parsed once for the font.
1334    ///
1335    /// Outlines and metrics in both directions read these, so they are
1336    /// shared rather than parsed by each.
1337    #[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    /// Returns `HVAR`, parsed once for the font.
1348    #[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    /// Returns `VVAR`, parsed once for the font.
1361    #[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    /// Returns `GVAR`, or `gvar` without it, parsed once for the font.
1374    ///
1375    /// Typically about half a variable font, so nothing should ask for this
1376    /// that another table can answer.
1377    #[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    /// Returns `VORG`, parsed once for the font.
1390    #[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    /// Returns the `CFF2` charstring outlines, parsed once for the font.
1403    ///
1404    /// Reading one means running it, so nothing here is parsed per glyph
1405    /// beyond what a charstring itself needs.
1406    #[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    /// Returns the `CFF` charstring outlines, parsed once for the font.
1417    #[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    /// Returns the glyph for each CID of a standalone CID-keyed font, built
1435    /// once.
1436    ///
1437    /// As in FreeType, the table ends at the largest CID and the first glyph
1438    /// with a CID is kept.
1439    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    /// Returns the variable composites, read once for the font.
1470    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    /// Returns `COLR` with its variation tables, read once for the font.
1483    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    /// Returns `CPAL`, read once for the font.
1496    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    /// Returns `sbix`, read once for the font.
1510    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    /// Returns `CBLC` and `CBDT`, read once for the font.
1524    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    /// Returns `EBLC` and `EBDT`, read once for the font.
1538    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    /// Returns an object that provides access to individual font tables.
1552    ///
1553    /// For non-SFNT fonts, this will return an empty set of tables.
1554    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
1563/// Builds the table from each CID to its glyph, keeping the first glyph for
1564/// a CID.
1565///
1566/// `cids` yields each glyph and its CID, glyph zero first with CID zero.
1567fn 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    // Zero marks an empty slot. Glyph zero has CID zero, so that slot is
1571    // already right and no later glyph takes it.
1572    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    // Storage cell for lazily loaded HarfRust shaping data.
1584    shaping_data: Once<Box<dyn Any + Send + Sync>>,
1585    // The glyph names sorted for lookup by name.
1586    glyph_name_index: Once<name::GlyphNameIndex>,
1587    // The caches of the default location, shared by every default instance.
1588    // A varied instance keeps its own.
1589    default_location: LocationCaches,
1590}
1591
1592/// The underlying type of a font.
1593///
1594/// Every variant is stored inline. The SFNT caches are the largest variant,
1595/// so the other variants add no size. Inlining also avoids a pointer
1596/// indirection in the metric lookups for every advance.
1597#[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
1607/// A Type 1 font and the axes of its design space, if it has one.
1608struct Type1Data {
1609    font: Type1Font,
1610    axes: Box<[VariationAxisRecord]>,
1611}
1612
1613/// Caches for an SFNT font, with each table parsed once per font.
1614pub(super) struct SfntCaches {
1615    tables: Arc<Tables>,
1616    index: u32,
1617    // Whether the font needs its instructions for correct contours: zero
1618    // until asked, then one more than the answer.
1619    hint_reliant: core::sync::atomic::AtomicU8,
1620    // Which of the bitmap, color and composite sources the font has: two
1621    // bits per source, zero until asked, then absent or present. They are
1622    // probed one at a time, as something asks for each.
1623    glyph_sources: core::sync::atomic::AtomicU32,
1624    // The default location's own SFNT caches. A varied instance keeps its
1625    // own.
1626    default_location: SfntLocation,
1627    // The horizontal glyph metrics, parsed once for the font. Held beside
1628    // the tables they borrow, which is what lets them live here at all.
1629    h_metrics: Once<TableCache<MetricsTable<'static>>>,
1630    // The vertical glyph metrics, read only when something measures
1631    // vertically.
1632    v_metrics: Once<TableCache<MetricsTable<'static>>>,
1633    // The outlines, and the tables that vary metrics: `HVAR` and `VVAR` hold
1634    // metric deltas, and `gvar` holds outline deltas, with metric deltas in
1635    // its phantom points. None depends on a location to parse, so every
1636    // instance shares them. Each is read when something first needs it.
1637    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    // The charstrings of a `CFF2` or `CFF` font, which neither metrics nor
1642    // outlines read unless the font states them this way. Each table has a
1643    // cell of its own, so an order can prefer either of a font with both.
1644    cff2: Once<TableCache<Cff2Font<'static>>>,
1645    cff: Once<TableCache<CffFont<'static>>>,
1646    // Tables that draw glyphs in other ways, read only when something asks
1647    // for that way. Boxed, so a font without them pays a pointer each.
1648    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    // Where a glyph's vertical origin sits, read only by vertical text.
1655    vorg: Once<TableCache<VorgTable<'static>>>,
1656    // Selected Unicode cmap subtables and their encoding metadata.
1657    charmap: Once<TableCache<SfntUnicodeCharmap<'static>>>,
1658    // Only clients selecting a charmap pay for the dense record array.
1659    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    /// The first [`CACHED_SUBFONTS`] subfonts, parsed when the font is
1694    /// read; `None` where a private dict could not be.
1695    subfonts: Box<[Option<Subfont>]>,
1696    index: u32,
1697    unicode_charmap: Once<crate::ps::UnicodeCharmap>,
1698    /// The glyph for each CID of a CID-keyed font, zero where none has it.
1699    cid_glyphs: Once<Box<[u16]>>,
1700}
1701
1702/// Caches for one location of a font of any kind.
1703struct LocationCaches {
1704    /// Data that Skrifa derives from the font at this location.
1705    scaling_data: Once<Box<dyn Any + Send + Sync>>,
1706    // Metrics that describe the font as a whole, read once rather than per
1707    // query. Kept apart from the shaping and scaling slots, which each hold
1708    // one thing for one owner.
1709    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
1723/// Caches for one location of an SFNT font.
1724struct SfntLocation {
1725    // Feature variation selection. Zero coordinates can still satisfy
1726    // conditions, so the default location keeps one too.
1727    feature_vars: FeatureVarsStorage,
1728    // CFF private dicts blend at a location, so each location parses its
1729    // own, as glyphs ask.
1730    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
1744/// What a location away from the default carries.
1745struct VariedInstance {
1746    font: SharedFont,
1747    /// Never empty: an all-default location is [`Repr::Default`].
1748    coords: CoordStorage,
1749    location: LocationCaches,
1750    kind: VariedKind,
1751}
1752
1753/// Caches for a varied instance, by font kind.
1754///
1755/// SFNT fonts and Type 1 multiple master fonts have variation axes.
1756enum VariedKind {
1757    Sfnt(VariedSfnt),
1758    Type1(VariedType1),
1759}
1760
1761/// A Type 1 multiple master font at a location.
1762struct VariedType1 {
1763    /// The weight of each master.
1764    weights: SmallVec<[Fixed; 16]>,
1765}
1766
1767/// Caches for a varied SFNT instance.
1768struct VariedSfnt {
1769    location: SfntLocation,
1770    // The region scalars at the location, for the variation tables that
1771    // state deltas by region. The default location needs none.
1772    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
1780/// The kept answer for a glyph source the font does not have.
1781const SOURCE_ABSENT: u32 = 1;
1782/// The kept answer for a glyph source the font has.
1783const SOURCE_PRESENT: u32 = 2;
1784
1785/// Returns up to [`MAX_PRECOMPUTED_SCALARS`] scalars from `count`, filled by
1786/// `compute`, which returns how many it wrote.
1787fn 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
1794/// Returns the region scalars of `store` at `coords`, or none without a
1795/// store.
1796fn 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
1810/// Lazy atomic storage for feature variation selections.
1811///
1812/// We don't want to load the GSUB and GPOS tables unless explicitly requested.
1813struct FeatureVarsStorage {
1814    gsub: AtomicU32,
1815    gpos: AtomicU32,
1816    /// Written before the indices, so it is current once they are.
1817    has_lookup_variations: AtomicBool,
1818}
1819
1820impl Default for FeatureVarsStorage {
1821    fn default() -> Self {
1822        Self::new()
1823    }
1824}
1825
1826impl FeatureVarsStorage {
1827    // Both sentinels sit where no record index reaches: a feature variation
1828    // record is eight bytes, so indexing this high would take a font of tens
1829    // of gigabytes.
1830    /// Nothing has read the tables yet.
1831    const UNCHECKED: u32 = u32::MAX;
1832    /// The tables were read and selected nothing.
1833    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        // Two threads that race here select the same indices, so the second
1856        // store writes what the first did.
1857        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    /// Returns the index a stored word holds, if it holds one.
1868    fn decode(value: u32) -> Option<u32> {
1869        (value < Self::ABSENT).then_some(value)
1870    }
1871
1872    /// Returns the word that stores a selection.
1873    fn encode(index: Option<u32>) -> u32 {
1874        index.unwrap_or(Self::ABSENT)
1875    }
1876}
1877
1878/// A cache of CFF subfonts at one location, each parsed when a glyph first
1879/// needs it.
1880///
1881/// A `None` cell means the subfont could not be read.
1882type SubfontCache = Once<Box<[Once<Option<Subfont>>]>>;
1883
1884/// The number of CFF subfonts a font keeps once parsed. A subfont past them
1885/// is parsed again whenever a glyph needs it.
1886const CACHED_SUBFONTS: usize = 8;
1887
1888/// Maximum number of coordinates we store inline. Chosen to maximize the
1889/// number of coords without growing the storage: a `SmallVec` is a capacity
1890/// alongside a union of the inline array and the heap pointer, so twelve
1891/// two byte coords are the most that fit in 32 bytes.
1892const MAX_INLINE_COORDS: usize = 12;
1893
1894type CoordStorage = SmallVec<[NormalizedCoord; MAX_INLINE_COORDS]>;
1895
1896/// Empties `coords` if every coordinate is zero. This lets the default
1897/// instance skip variation processing behind a plain `is_empty` check.
1898fn 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    /// The storage behind a varied instance, for the cache tests.
1907    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    /// Returns the SFNT caches of the instance, for the cache tests.
1918    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    /// Returns where glyph 1 of the synthetic multiple master font starts
1935    /// drawing, which moves with the weight of its second master.
1936    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    // A Type 1 multiple master font has an axis per blend axis, and draws
1951    // and measures at the location an instance is built for.
1952    #[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        // The default is the font's own blend: a weight of 0.25 on the
1961        // second master, at design 300.
1962        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        // The normalized coordinates are the blend, from 0 to 1. The first
1972        // master is all zeros, and stays a location of its own.
1973        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    // A variation naming no axis leaves the default instance alone, even
1999    // where the default design coordinate doesn't map back to the default
2000    // blend.
2001    #[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    // Design maps spanning every design coordinate give axes whose values
2017    // saturate.
2018    #[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    // A Type 1 font's metrics don't vary, so an instance reports the
2036    // font's own.
2037    #[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    // Type 1 has no named instances, but the overrides still apply.
2061    #[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    // The index selects a font where the data can hold several, and is
2073    // ignored where it cannot.
2074    #[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    // A standalone CID-keyed font finds each glyph by the CID its charset
2097    // gives it.
2098    #[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    // A later glyph that claims a CID already taken, CID zero included,
2120    // leaves the first glyph in place.
2121    #[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            // A CID-keyed `CFF` table in an SFNT font.
2131            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        // A Subrs offset of -1, which no private dict can resolve.
2151        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        // Every glyph of the font is in that one subfont, so none draws or
2159        // measures.
2160        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        // A standalone CFF encoding names no record in either mapping table.
2234        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            // (named instance index, expected weight value)
2402            (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        // Out of bounds index should give us the default instance.
2421        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            // (fill, [GSUB feature variation index, GPOS feature variation index])
2487            (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                // GSUB 1.1 followed by one feature-variation record whose
2507                // axis-zero range matches exactly the default location.
2508                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    /// Twelve axes, and an `MVAR` that varies its heights.
2687    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        // Nothing varies there, so there is no second copy to compute.
2739        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        // The location is resolved once, not once per holder.
2753        assert!(core::ptr::eq(far.metrics(), shared.metrics()));
2754    }
2755
2756    #[test]
2757    fn the_fixed_numbers_do_not_resolve_a_location() {
2758        // Both are properties of the font, so reaching them through an
2759        // instance must not read `MVAR` the way its whole-font metrics do.
2760        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        // And the varied metrics still do.
2785        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        // Nothing about the default location needs owning, so building one
2792        // shares the font rather than allocating beside it.
2793        let font = Font::new(MVAR_FONT, 0).unwrap();
2794        for instance in [
2795            font.default_instance(),
2796            font.instance_builder().build(),
2797            // An all-default location collapses to no coordinates, so this
2798            // reaches the same place.
2799            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}