Skip to main content

read_fonts/model/glyph/outline/
varc.rs

1//! Outlines from variable composites in `VARC`.
2//!
3//! A variable composite glyph is a tree of components. Each component is a
4//! transformed glyph drawn at its own location in the font's variation space,
5//! and may itself be a composite. The leaves are glyphs from the font's base
6//! outline source, `glyf` or CFF. They are drawn by the [`OutlineGlyphs`]
7//! that produced the composite.
8//!
9//! The working memory a [`VarcGlyph`] needs depends on its nesting depth and
10//! its largest leaf. The tree is walked to measure them the first time the
11//! size is needed, and the result is kept. Drawing splits that memory into
12//! typed slices, as the `glyf` loader does, so a composite needs no memory
13//! beyond that buffer.
14
15use super::{
16    glyf::Precision, glyphs::OutlineGlyphs, DrawOptions, DrawnMetrics, PathSink, TransformSink,
17};
18use crate::model::Error;
19use crate::{
20    limits::{MAX_COMPOSITE_EDGES, MAX_RECURSION_DEPTH},
21    mem::take_slice,
22    tables::{
23        varc::{
24            Condition, CoverageTable, DecomposedTransform, MultiItemVariationStore,
25            SparseVariationRegionList, Varc, VarcComponent, VarcFlags,
26        },
27        variations::NO_VARIATION_INDEX,
28    },
29    types::{F2Dot14, GlyphId, Matrix},
30    ReadError,
31};
32use core::{
33    mem::{align_of, size_of},
34    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
35};
36use yoke::Yokeable;
37
38#[cfg(feature = "libm")]
39#[allow(unused_imports)]
40use core_maths::CoreFloat;
41
42/// Provides the data that [`Outlines::draw`] needs from outside the table.
43///
44/// This includes the font's location and the glyphs at the leaves of a
45/// composite.
46pub(crate) trait VarcContext {
47    /// Returns the normalized variation coordinates of the font's location.
48    ///
49    /// A component that resets unspecified axes uses these values.
50    fn coords(&self) -> &[F2Dot14];
51
52    /// Draws `glyph` into `sink` at the normalized coordinates `coords`.
53    ///
54    /// The `coords` may differ from [`Self::coords`]. `ppem` is the size of
55    /// the composite, or `None` for font units. `buf` is working memory of
56    /// at least [`Self::glyph_buffer_size`] bytes for the glyph.
57    fn draw_glyph(
58        &self,
59        glyph: GlyphId,
60        coords: &[F2Dot14],
61        ppem: Option<f32>,
62        buf: &mut [u8],
63        sink: &mut dyn PathSink,
64    ) -> Result<(), Error>;
65
66    /// Returns the size in bytes of the working memory needed to draw
67    /// `glyph`.
68    fn glyph_buffer_size(&self, glyph: GlyphId) -> usize;
69}
70
71/// A glyph in the `VARC` table, with its working memory size computed.
72/// A composite glyph, ready to draw.
73///
74/// The memory a draw needs depends on the whole tree, so the tree is
75/// measured the first time that is asked for, not when the outline is made.
76#[derive(Debug)]
77pub(crate) struct Outline {
78    glyph: GlyphId,
79    coverage_index: u32,
80    measured: Measured,
81}
82
83impl Outline {
84    /// Returns the glyph identifier.
85    pub(crate) fn glyph(&self) -> GlyphId {
86        self.glyph
87    }
88}
89
90impl Clone for Outline {
91    fn clone(&self) -> Self {
92        Self {
93            glyph: self.glyph,
94            coverage_index: self.coverage_index,
95            measured: self.measured.clone(),
96        }
97    }
98}
99
100/// What measuring a composite's tree found, kept in atomics so an outline
101/// stays cheap to clone and safe to share.
102///
103/// Two threads that measure at once store the same values.
104#[derive(Debug, Default)]
105struct Measured {
106    /// [`Measured::UNMEASURED`] until the tree is measured. Then one of the
107    /// other states in the low two bits, with the nesting depth above them.
108    state: AtomicU32,
109    /// The size in bytes of the largest leaf, once `state` says so.
110    leaf: AtomicUsize,
111}
112
113impl Measured {
114    const UNMEASURED: u32 = 0;
115    const MEASURED: u32 = 1;
116    const MALFORMED: u32 = 2;
117    const OVER_LIMIT: u32 = 3;
118    const STATE_BITS: u32 = 2;
119}
120
121impl Clone for Measured {
122    fn clone(&self) -> Self {
123        Self {
124            state: AtomicU32::new(self.state.load(Ordering::Acquire)),
125            leaf: AtomicUsize::new(self.leaf.load(Ordering::Relaxed)),
126        }
127    }
128}
129
130/// Provides access to the variable composite glyphs in a font.
131#[derive(Clone, Yokeable)]
132pub(crate) struct Outlines<'a> {
133    varc: Varc<'a>,
134    coverage: CoverageTable<'a>,
135    var_store: Option<MultiItemVariationStore<'a>>,
136    regions: Option<SparseVariationRegionList<'a>>,
137    units_per_em: u16,
138    axis_count: usize,
139    /// The length of the longest axis index list in the table.
140    ///
141    /// A list longer than `axis_count` repeats an axis, and the later value
142    /// wins.
143    index_count: usize,
144}
145
146impl<'a> Outlines<'a> {
147    /// Creates a new set of composite glyphs from `varc`.
148    ///
149    /// `axis_count` is the number of variation axes that components can set.
150    /// A font without `fvar` can still have axes in `gvar` or the CFF2
151    /// variation store.
152    pub(crate) fn new(
153        varc: Varc<'a>,
154        units_per_em: u16,
155        axis_count: usize,
156    ) -> Result<Self, ReadError> {
157        let coverage = varc.coverage()?;
158        let var_store = varc.multi_var_store().transpose()?;
159        let regions = var_store
160            .as_ref()
161            .map(|store| store.region_list())
162            .transpose()?;
163        let list_count = varc
164            .axis_indices_list()
165            .transpose()?
166            .map_or(0, |lists| lists.count() as usize);
167        let index_count = (0..list_count)
168            .filter_map(|nth| varc.axis_indices(nth).ok())
169            .map(|list| list.count_or_compute())
170            .max()
171            .unwrap_or(0);
172        Ok(Self {
173            varc,
174            coverage,
175            var_store,
176            regions,
177            units_per_em,
178            axis_count,
179            index_count,
180        })
181    }
182
183    /// Returns true if the table covers `glyph`.
184    pub(crate) fn covers(&self, glyph: GlyphId) -> bool {
185        self.coverage.get(glyph).is_some()
186    }
187
188    /// Returns the outline for `glyph`.
189    ///
190    /// Returns `None` if the table does not cover the glyph. The tree is read
191    /// only when the glyph is drawn or its memory size is requested.
192    pub(crate) fn outline(&self, glyph: GlyphId) -> Option<Outline> {
193        let coverage_index = self.coverage.get(glyph)?;
194        Some(Outline {
195            glyph,
196            coverage_index,
197            measured: Measured::default(),
198        })
199    }
200
201    /// Returns the memory layout for drawing `outline`, measuring its tree
202    /// the first time.
203    ///
204    /// Fails if the glyph data is malformed or the tree passes the recursion
205    /// or edge limits. Every component counts, whatever its condition, so the
206    /// answer is the same at every location.
207    fn layout_of(&self, outline: &Outline, context: &dyn VarcContext) -> Result<Layout, Error> {
208        let measured = &outline.measured;
209        let mut state = measured.state.load(Ordering::Acquire);
210        if state == Measured::UNMEASURED {
211            let mut walk = Walk::new();
212            let result = self.measure(outline.glyph, outline.coverage_index, context, &mut walk);
213            let (code, leaf) = match result {
214                Err(_) => (Measured::MALFORMED, 0),
215                Ok(_) if walk.truncated => (Measured::OVER_LIMIT, 0),
216                Ok(leaf) => (Measured::MEASURED, leaf),
217            };
218            state = code | (walk.depth as u32) << Measured::STATE_BITS;
219            measured.leaf.store(leaf, Ordering::Relaxed);
220            measured.state.store(state, Ordering::Release);
221        }
222        match state & ((1 << Measured::STATE_BITS) - 1) {
223            Measured::MEASURED => {
224                let depth = (state >> Measured::STATE_BITS) as usize;
225                Ok(self.layout(depth, measured.leaf.load(Ordering::Relaxed)))
226            }
227            Measured::OVER_LIMIT => Err(Error::LimitExceeded),
228            _ => Err(Error::Malformed),
229        }
230    }
231
232    /// Returns the size in bytes of the memory for drawing `outline`, or zero
233    /// if it cannot be drawn.
234    pub(crate) fn required_memory_size(
235        &self,
236        outline: &Outline,
237        context: &dyn VarcContext,
238    ) -> usize {
239        self.layout_of(outline, context)
240            .ok()
241            .and_then(|layout| layout.packed_len())
242            .unwrap_or(0)
243    }
244
245    /// Returns the memory layout for a composite.
246    ///
247    /// `depth` is the nesting depth and `leaf` is the size in bytes of the
248    /// largest leaf.
249    fn layout(&self, depth: usize, leaf: usize) -> Layout {
250        Layout {
251            depth: depth.max(1),
252            axis_count: self.axis_count,
253            index_count: self.index_count,
254            region_count: self.region_count(),
255            leaf,
256        }
257    }
258
259    /// Returns the number of variation regions, or zero if the table has no
260    /// variation store.
261    fn region_count(&self) -> usize {
262        self.regions
263            .as_ref()
264            .map_or(0, |regions| regions.region_count() as usize)
265    }
266
267    /// Measures the tree under a composite.
268    ///
269    /// Returns the size in bytes of the largest leaf and records the deepest
270    /// nesting in `walk`. Every component is counted regardless of its
271    /// condition, so the result is valid for any location.
272    fn measure(
273        &self,
274        glyph: GlyphId,
275        coverage_index: u32,
276        context: &dyn VarcContext,
277        walk: &mut Walk,
278    ) -> Result<usize, ReadError> {
279        // The caller filters out a glyph already on the stack, so this one
280        // starts no cycle. The limits end the walk, and the outline is then
281        // refused.
282        if walk.stack.len() >= MAX_RECURSION_DEPTH || walk.edges_left == 0 {
283            walk.truncated = true;
284            return Ok(0);
285        }
286        walk.edges_left -= 1;
287        walk.stack.push(glyph);
288        walk.depth = walk.depth.max(walk.stack.len());
289        let mut leaf = 0usize;
290        let record = self.varc.glyph(coverage_index as usize)?;
291        for component in record.components() {
292            let component_gid = component?.gid();
293            // A covered component nests, unless it is the glyph itself,
294            // which drawing takes from the base, or one already being drawn,
295            // which closes a cycle and draws nothing.
296            let covered = self
297                .coverage
298                .get(component_gid)
299                .filter(|_| component_gid != glyph);
300            let component_leaf = match covered {
301                Some(coverage_index) if !walk.stack.contains(component_gid) => {
302                    self.measure(component_gid, coverage_index, context, walk)?
303                }
304                // A leaf or a cycle is an edge of the tree too.
305                _ if walk.edges_left == 0 => {
306                    walk.truncated = true;
307                    break;
308                }
309                Some(_) => {
310                    walk.edges_left -= 1;
311                    0
312                }
313                None => {
314                    walk.edges_left -= 1;
315                    context.glyph_buffer_size(component_gid)
316                }
317            };
318            leaf = leaf.max(component_leaf);
319        }
320        walk.stack.pop();
321        Ok(leaf)
322    }
323
324    /// Draws `outline` into `sink` at the location provided by `context`.
325    ///
326    /// The glyphs at the leaves are drawn by `context`. `ppem` is the size of
327    /// the leaves, or `None` for font units. It also scales the component
328    /// offsets. `buf` must hold at least
329    /// [`required_memory_size`](Self::required_memory_size) bytes.
330    pub(crate) fn draw(
331        &self,
332        outline: &Outline,
333        context: &dyn VarcContext,
334        ppem: Option<f32>,
335        buf: &mut [u8],
336        sink: &mut dyn PathSink,
337    ) -> Result<(), Error> {
338        let layout = self.layout_of(outline, context)?;
339        let memory = layout.carve(buf).ok_or(Error::InsufficientMemory)?;
340        self.draw_tree(outline, &layout, context, ppem, memory, sink)
341    }
342
343    /// Implements [`draw`](Self::draw) with memory that is already split
344    /// into slices.
345    fn draw_tree(
346        &self,
347        outline: &Outline,
348        layout: &Layout,
349        context: &dyn VarcContext,
350        ppem: Option<f32>,
351        memory: Memory<'_>,
352        sink: &mut dyn PathSink,
353    ) -> Result<(), Error> {
354        let Memory {
355            font_coords,
356            coords,
357            scalars,
358            leaf,
359            mut scratch,
360        } = memory;
361        expand_coords(font_coords, context.coords());
362        let (root_scalars, level_scalars) =
363            split(scalars, layout.region_count).ok_or(Error::InsufficientMemory)?;
364        let mut scalar_cache = ScalarCache::new(root_scalars);
365        let levels = Levels {
366            coords,
367            scalars: level_scalars,
368            axis_count: layout.axis_count,
369            region_count: layout.region_count,
370        };
371        let draw = DrawContext {
372            font_coords,
373            scale: linear_scale(ppem, self.units_per_em),
374            ppem,
375            context,
376            store_regions: self.var_store.as_ref().zip(self.regions.as_ref()),
377        };
378        let mut stack = GlyphStack::new();
379        self.draw_glyph(
380            outline.glyph,
381            outline.coverage_index,
382            draw.font_coords,
383            Matrix::IDENTITY,
384            &draw,
385            &mut scalar_cache,
386            levels,
387            leaf,
388            sink,
389            &mut stack,
390            &mut scratch,
391        )
392    }
393
394    #[allow(clippy::too_many_arguments)]
395    fn draw_glyph(
396        &self,
397        glyph: GlyphId,
398        coverage_index: u32,
399        current_coords: &[F2Dot14],
400        parent_matrix: Matrix<f32>,
401        draw: &DrawContext<'a, '_>,
402        scalar_cache: &mut ScalarCache<'_>,
403        mut levels: Levels<'_>,
404        leaf: &mut [u8],
405        sink: &mut dyn PathSink,
406        stack: &mut GlyphStack,
407        scratch: &mut Scratch<'_>,
408    ) -> Result<(), Error> {
409        // Past a limit the whole glyph fails rather than leaving out what
410        // lies beyond it. The memory is sized by a walk that stops at the
411        // same limits, so nothing past them has room to draw.
412        if stack.len() >= MAX_RECURSION_DEPTH || scratch.edges_left == 0 {
413            return Err(Error::LimitExceeded);
414        }
415        scratch.edges_left -= 1;
416        let record = self.varc.glyph(coverage_index as usize)?;
417        // This level's run of coordinates and scalars, for components drawn
418        // at another location.
419        let mut level = levels.next().ok_or(Error::InsufficientMemory)?;
420        stack.push(glyph);
421        let store_regions = draw.store_regions;
422        for component in record.components() {
423            let component = component?;
424            if !self.component_condition_met(
425                &component,
426                current_coords,
427                scalar_cache,
428                scratch,
429                store_regions,
430            )? {
431                continue;
432            }
433            let component_gid = component.gid();
434            let flags = component.flags();
435
436            let coords_the_same = !flags.contains(VarcFlags::HAVE_AXES)
437                && !flags.contains(VarcFlags::RESET_UNSPECIFIED_AXES);
438
439            let component_coords: &[F2Dot14] = if coords_the_same {
440                current_coords
441            } else {
442                self.component_coords(
443                    &component,
444                    current_coords,
445                    level.coords,
446                    scalar_cache,
447                    scratch,
448                    draw.font_coords,
449                    store_regions,
450                )?;
451                level.coords
452            };
453
454            let mut transform = *component.transform();
455            apply_transform_variations(
456                &component,
457                current_coords,
458                &mut transform,
459                scalar_cache,
460                scratch,
461                store_regions,
462            )?;
463            let matrix = parent_matrix * scale_matrix(transform.matrix(), draw.scale);
464            if component_gid != glyph {
465                if let Some(coverage_index) = self.coverage.get(component_gid) {
466                    if !stack.contains(component_gid) {
467                        if coords_the_same {
468                            // The scalars are for these coordinates, so the
469                            // child shares them.
470                            self.draw_glyph(
471                                component_gid,
472                                coverage_index,
473                                current_coords,
474                                matrix,
475                                draw,
476                                scalar_cache,
477                                level.rest.reborrow(),
478                                leaf,
479                                sink,
480                                stack,
481                                scratch,
482                            )?;
483                        } else {
484                            let mut child_cache = ScalarCache::new(level.scalars);
485                            self.draw_glyph(
486                                component_gid,
487                                coverage_index,
488                                component_coords,
489                                matrix,
490                                draw,
491                                &mut child_cache,
492                                level.rest.reborrow(),
493                                leaf,
494                                sink,
495                                stack,
496                                scratch,
497                            )?;
498                        }
499                        continue;
500                    }
501                    // The component leads back to a glyph being drawn. The
502                    // cycle draws nothing, but it is an edge of the tree.
503                    if scratch.edges_left == 0 {
504                        return Err(Error::LimitExceeded);
505                    }
506                    scratch.edges_left -= 1;
507                    continue;
508                }
509            }
510            // A leaf is an edge of the tree too.
511            if scratch.edges_left == 0 {
512                return Err(Error::LimitExceeded);
513            }
514            scratch.edges_left -= 1;
515            draw.context.draw_glyph(
516                component_gid,
517                component_coords,
518                draw.ppem,
519                leaf,
520                &mut TransformSink::new(&mut *sink, matrix),
521            )?;
522        }
523        stack.pop();
524        Ok(())
525    }
526
527    /// Writes the coordinates for drawing `component` to `coords`.
528    ///
529    /// `coords` holds one value per axis.
530    #[allow(clippy::too_many_arguments)]
531    fn component_coords(
532        &self,
533        component: &VarcComponent<'_>,
534        current_coords: &[F2Dot14],
535        coords: &mut [F2Dot14],
536        scalar_cache: &mut ScalarCache<'_>,
537        scratch: &mut Scratch<'_>,
538        font_coords: &[F2Dot14],
539        store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
540    ) -> Result<(), Error> {
541        let flags = component.flags();
542        if flags.contains(VarcFlags::RESET_UNSPECIFIED_AXES) {
543            expand_coords(coords, font_coords);
544        } else {
545            expand_coords(coords, current_coords);
546        }
547
548        if !flags.contains(VarcFlags::HAVE_AXES) {
549            return Ok(());
550        }
551
552        let axis_indices_index = component.axis_indices_index().ok_or(Error::Malformed)?;
553        let num_axes = self.axis_indices(axis_indices_index as usize, scratch.axis_indices)?;
554        let axis_indices = &scratch.axis_indices[..num_axes];
555        let values = scratch
556            .axis_values
557            .get_mut(..num_axes)
558            .ok_or(Error::Malformed)?;
559        let stated = axis_values(component, values);
560        let values = &mut values[..stated];
561        if let Some(var_idx) = component.axis_values_var_index() {
562            if var_idx != NO_VARIATION_INDEX && !values.is_empty() {
563                let (store, regions) = store_regions.ok_or(Error::Malformed)?;
564                let deltas = scratch
565                    .deltas
566                    .get_mut(..values.len())
567                    .ok_or(Error::Malformed)?;
568                compute_tuple_deltas(
569                    store,
570                    regions,
571                    var_idx,
572                    current_coords,
573                    scalar_cache,
574                    deltas,
575                )?;
576                for (value, delta) in values.iter_mut().zip(deltas.iter()) {
577                    *value += *delta;
578                }
579            }
580        }
581
582        for (axis_index, value) in axis_indices.iter().zip(values.iter().copied()) {
583            let Some(slot) = coords.get_mut(*axis_index as usize) else {
584                return Err(Error::Malformed);
585            };
586            let raw = value.round().clamp(i16::MIN as f64, i16::MAX as f64) as i16;
587            *slot = F2Dot14::from_bits(raw);
588        }
589        Ok(())
590    }
591
592    /// Reads the `nth` list of axis indices into `out` and returns the
593    /// number of indices.
594    fn axis_indices(&self, nth: usize, out: &mut [u16]) -> Result<usize, ReadError> {
595        let packed = self.varc.axis_indices(nth)?;
596        let mut count = 0;
597        for value in packed.iter() {
598            // `out` holds the longest list in the table.
599            let slot = out.get_mut(count).ok_or(ReadError::OutOfBounds)?;
600            *slot = u16::try_from(value).map_err(|_| ReadError::OutOfBounds)?;
601            count += 1;
602        }
603        Ok(count)
604    }
605
606    fn component_condition_met(
607        &self,
608        component: &VarcComponent<'_>,
609        coords: &[F2Dot14],
610        scalar_cache: &mut ScalarCache<'_>,
611        scratch: &mut Scratch<'_>,
612        store_regions: Option<(&MultiItemVariationStore<'a>, &SparseVariationRegionList<'a>)>,
613    ) -> Result<bool, Error> {
614        let Some(condition_index) = component.condition_index() else {
615            return Ok(true);
616        };
617        let condition_list = self.varc.condition_list().ok_or(Error::Malformed)??;
618        let offset = condition_list
619            .condition_offsets()
620            .get(condition_index as usize)
621            .ok_or(Error::Malformed)?;
622        // A null condition is unconditional; it is not a format-0 table.
623        if offset.get().is_null() {
624            return Ok(true);
625        }
626        let condition = condition_list.conditions().get(condition_index as usize)?;
627        eval_condition(&condition, coords, store_regions, scalar_cache, scratch)
628    }
629}
630
631/// Data shared by every glyph in one draw.
632struct DrawContext<'a, 'b> {
633    font_coords: &'b [F2Dot14],
634    /// The factor that scales component offsets to the size of the leaves.
635    scale: f32,
636    ppem: Option<f32>,
637    context: &'b dyn VarcContext,
638    store_regions: Option<(
639        &'b MultiItemVariationStore<'a>,
640        &'b SparseVariationRegionList<'a>,
641    )>,
642}
643
644/// The maximum number of fields that one transform can vary.
645///
646/// Together with the axis count, this bounds the length of a delta tuple.
647const TRANSFORM_FIELD_COUNT: usize = 9;
648
649/// The size of a composite's working memory and how it is split.
650///
651/// A single buffer from the caller holds every slice that the walk uses.
652/// The slices are taken in descending order of alignment so that only the
653/// first one can need padding. The memory for drawing leaves comes last.
654#[derive(Clone, Copy, Debug, PartialEq, Eq)]
655struct Layout {
656    /// The nesting depth of the composites, at least one.
657    depth: usize,
658    axis_count: usize,
659    /// The length of the longest axis index list, which sizes the axis
660    /// indices, their values and their deltas.
661    index_count: usize,
662    region_count: usize,
663    /// The size in bytes of the memory needed by the largest leaf.
664    leaf: usize,
665}
666
667impl Layout {
668    /// Returns the length of the longest delta tuple a component reads.
669    fn tuple_len(&self) -> usize {
670        self.index_count.max(TRANSFORM_FIELD_COUNT)
671    }
672
673    /// Returns the size in bytes of the buffer needed for
674    /// [`carve`](Self::carve) to succeed.
675    ///
676    /// Returns `None` if the table has more regions than memory can hold.
677    fn packed_len(&self) -> Option<usize> {
678        let doubles = self.tuple_len().checked_add(self.index_count)?;
679        let scalars = (self.depth + 1).checked_mul(self.region_count)?;
680        // The font's coordinates, one run per level, and the axis indices.
681        let halves = (self.depth + 1)
682            .checked_mul(self.axis_count)?
683            .checked_add(self.index_count)?;
684        doubles
685            .checked_mul(size_of::<f64>())?
686            .checked_add(scalars.checked_mul(size_of::<f32>())?)?
687            .checked_add(halves.checked_mul(size_of::<u16>())?)?
688            .checked_add(self.leaf)?
689            .checked_add(align_of::<f64>())
690    }
691
692    /// Splits `buf` into the slices used by a draw, or returns `None` if it
693    /// is too short.
694    fn carve<'m>(&self, buf: &'m mut [u8]) -> Option<Memory<'m>> {
695        let (deltas, rest) = take_slice::<f64>(buf, self.tuple_len())?;
696        let (axis_values, rest) = take_slice::<f64>(rest, self.index_count)?;
697        let (scalars, rest) = take_slice::<f32>(rest, (self.depth + 1) * self.region_count)?;
698        let (font_coords, rest) = take_slice::<F2Dot14>(rest, self.axis_count)?;
699        let (coords, rest) = take_slice::<F2Dot14>(rest, self.depth * self.axis_count)?;
700        let (axis_indices, leaf) = take_slice::<u16>(rest, self.index_count)?;
701        (leaf.len() >= self.leaf).then_some(Memory {
702            font_coords,
703            coords,
704            scalars,
705            leaf,
706            scratch: Scratch {
707                deltas,
708                axis_values,
709                axis_indices,
710                edges_left: MAX_COMPOSITE_EDGES,
711            },
712        })
713    }
714}
715
716/// The working memory for one draw, split by a [`Layout`].
717struct Memory<'m> {
718    /// The font's coordinates, one per axis.
719    font_coords: &'m mut [F2Dot14],
720    /// One run of coordinates per level of nesting.
721    coords: &'m mut [F2Dot14],
722    /// One run of region scalars for the root and one per level.
723    scalars: &'m mut [f32],
724    /// The working memory for drawing leaves.
725    leaf: &'m mut [u8],
726    scratch: Scratch<'m>,
727}
728
729/// Slices that each level uses in turn, and the number of edges left to
730/// walk.
731struct Scratch<'m> {
732    deltas: &'m mut [f64],
733    axis_values: &'m mut [f64],
734    axis_indices: &'m mut [u16],
735    edges_left: usize,
736}
737
738/// The unused runs of coordinates and scalars.
739///
740/// There is one of each per level of nesting below the current one.
741struct Levels<'m> {
742    coords: &'m mut [F2Dot14],
743    scalars: &'m mut [f32],
744    axis_count: usize,
745    region_count: usize,
746}
747
748/// The runs for one level, with the remaining runs for the levels below it.
749struct Level<'m> {
750    coords: &'m mut [F2Dot14],
751    scalars: &'m mut [f32],
752    rest: Levels<'m>,
753}
754
755impl Levels<'_> {
756    /// Reborrows the runs for one child.
757    fn reborrow(&mut self) -> Levels<'_> {
758        Levels {
759            coords: &mut *self.coords,
760            scalars: &mut *self.scalars,
761            axis_count: self.axis_count,
762            region_count: self.region_count,
763        }
764    }
765
766    /// Takes the runs for one level, or returns `None` if none are left.
767    fn next(&mut self) -> Option<Level<'_>> {
768        let (coords, rest_coords) = split(&mut *self.coords, self.axis_count)?;
769        let (scalars, rest_scalars) = split(&mut *self.scalars, self.region_count)?;
770        Some(Level {
771            coords,
772            scalars,
773            rest: Levels {
774                coords: rest_coords,
775                scalars: rest_scalars,
776                axis_count: self.axis_count,
777                region_count: self.region_count,
778            },
779        })
780    }
781}
782
783/// Splits `len` elements off the front of `slice`, or `None` if it is too
784/// short.
785fn split<T>(slice: &mut [T], len: usize) -> Option<(&mut [T], &mut [T])> {
786    (len <= slice.len()).then(|| slice.split_at_mut(len))
787}
788
789/// The composites being walked, outermost first, used to detect cycles.
790struct GlyphStack {
791    ids: [GlyphId; MAX_RECURSION_DEPTH],
792    len: usize,
793}
794
795impl GlyphStack {
796    fn new() -> Self {
797        Self {
798            ids: [GlyphId::new(0); MAX_RECURSION_DEPTH],
799            len: 0,
800        }
801    }
802
803    fn len(&self) -> usize {
804        self.len
805    }
806
807    fn contains(&self, glyph: GlyphId) -> bool {
808        self.ids[..self.len].contains(&glyph)
809    }
810
811    /// Pushes `glyph`.
812    ///
813    /// Callers keep the depth below the limit, so there is always room.
814    fn push(&mut self, glyph: GlyphId) {
815        if let Some(slot) = self.ids.get_mut(self.len) {
816            *slot = glyph;
817            self.len += 1;
818        }
819    }
820
821    fn pop(&mut self) {
822        self.len = self.len.saturating_sub(1);
823    }
824}
825
826/// The state of a sizing walk.
827struct Walk {
828    stack: GlyphStack,
829    edges_left: usize,
830    /// The deepest nesting seen.
831    depth: usize,
832    /// True if a limit stopped the walk early.
833    truncated: bool,
834}
835
836impl Walk {
837    fn new() -> Self {
838        Self {
839            stack: GlyphStack::new(),
840            edges_left: MAX_COMPOSITE_EDGES,
841            depth: 0,
842            truncated: false,
843        }
844    }
845}
846
847/// The scalar for each region at one location, computed on first use.
848struct ScalarCache<'m> {
849    values: &'m mut [f32],
850}
851
852impl<'m> ScalarCache<'m> {
853    /// Marks a scalar that has not been computed.
854    ///
855    /// Scalars are in `[0, 1]`, so this value is never valid.
856    const INVALID: f32 = 2.0;
857
858    fn new(values: &'m mut [f32]) -> Self {
859        values.fill(Self::INVALID);
860        Self { values }
861    }
862
863    fn get(&self, index: usize) -> f32 {
864        self.values.get(index).copied().unwrap_or(Self::INVALID)
865    }
866
867    fn set(&mut self, index: usize, value: f32) {
868        if let Some(slot) = self.values.get_mut(index) {
869            *slot = value;
870        }
871    }
872}
873
874/// Writes the axis values of `component` to `out`, one per axis index.
875///
876/// Returns the number of slots written. If the component has axis values,
877/// every slot is written and any missing values are set to zero. If it has
878/// none, this returns zero.
879fn axis_values(component: &VarcComponent<'_>, out: &mut [f64]) -> usize {
880    let Some(packed) = component.axis_values() else {
881        return 0;
882    };
883    out.fill(0.0);
884    for (slot, value) in out.iter_mut().zip(packed.iter()) {
885        *slot = value as f64;
886    }
887    out.len()
888}
889
890fn apply_transform_variations(
891    component: &VarcComponent<'_>,
892    coords: &[F2Dot14],
893    transform: &mut DecomposedTransform,
894    scalar_cache: &mut ScalarCache<'_>,
895    scratch: &mut Scratch<'_>,
896    store_regions: Option<(&MultiItemVariationStore<'_>, &SparseVariationRegionList<'_>)>,
897) -> Result<(), Error> {
898    let Some(var_idx) = component.transform_var_index() else {
899        return Ok(());
900    };
901
902    let flags = component.flags();
903
904    const TRANSFORM_MASK: VarcFlags = VarcFlags::from_bits_truncate(
905        VarcFlags::HAVE_TRANSLATE_X.bits()
906            | VarcFlags::HAVE_TRANSLATE_Y.bits()
907            | VarcFlags::HAVE_ROTATION.bits()
908            | VarcFlags::HAVE_SCALE_X.bits()
909            | VarcFlags::HAVE_SCALE_Y.bits()
910            | VarcFlags::HAVE_SKEW_X.bits()
911            | VarcFlags::HAVE_SKEW_Y.bits()
912            | VarcFlags::HAVE_TCENTER_X.bits()
913            | VarcFlags::HAVE_TCENTER_Y.bits(),
914    );
915    let field_count = (flags.bits() & TRANSFORM_MASK.bits()).count_ones() as usize;
916    if field_count == 0 || var_idx == NO_VARIATION_INDEX {
917        return Ok(());
918    }
919
920    let (store, regions) = store_regions.ok_or(Error::Malformed)?;
921    let deltas = scratch
922        .deltas
923        .get_mut(..field_count)
924        .ok_or(Error::Malformed)?;
925    compute_tuple_deltas(store, regions, var_idx, coords, scalar_cache, deltas)?;
926
927    let mut delta_iter = deltas.iter().map(|&delta| delta as f32);
928
929    if flags.contains(VarcFlags::HAVE_TRANSLATE_X) {
930        let delta = delta_iter.next().unwrap_or(0.0);
931        transform.set_translate_x(transform.translate_x() + delta);
932    }
933    if flags.contains(VarcFlags::HAVE_TRANSLATE_Y) {
934        let delta = delta_iter.next().unwrap_or(0.0);
935        transform.set_translate_y(transform.translate_y() + delta);
936    }
937    if flags.contains(VarcFlags::HAVE_ROTATION) {
938        let delta = delta_iter.next().unwrap_or(0.0);
939        transform.set_rotation(transform.rotation() + delta / 4096.0);
940    }
941    if flags.contains(VarcFlags::HAVE_SCALE_X) {
942        let delta = delta_iter.next().unwrap_or(0.0);
943        transform.set_scale_x(transform.scale_x() + delta / 1024.0);
944    }
945    if flags.contains(VarcFlags::HAVE_SCALE_Y) {
946        let delta = delta_iter.next().unwrap_or(0.0);
947        transform.set_scale_y(transform.scale_y() + delta / 1024.0);
948    }
949    const SKEW_OR_CENTER: VarcFlags = VarcFlags::from_bits_truncate(
950        VarcFlags::HAVE_SKEW_X.bits()
951            | VarcFlags::HAVE_SKEW_Y.bits()
952            | VarcFlags::HAVE_TCENTER_X.bits()
953            | VarcFlags::HAVE_TCENTER_Y.bits(),
954    );
955    if flags.intersects(SKEW_OR_CENTER) {
956        if flags.contains(VarcFlags::HAVE_SKEW_X) {
957            let delta = delta_iter.next().unwrap_or(0.0);
958            transform.set_skew_x(transform.skew_x() + delta / 4096.0);
959        }
960        if flags.contains(VarcFlags::HAVE_SKEW_Y) {
961            let delta = delta_iter.next().unwrap_or(0.0);
962            transform.set_skew_y(transform.skew_y() + delta / 4096.0);
963        }
964        if flags.contains(VarcFlags::HAVE_TCENTER_X) {
965            let delta = delta_iter.next().unwrap_or(0.0);
966            transform.set_center_x(transform.center_x() + delta);
967        }
968        if flags.contains(VarcFlags::HAVE_TCENTER_Y) {
969            let delta = delta_iter.next().unwrap_or(0.0);
970            transform.set_center_y(transform.center_y() + delta);
971        }
972    }
973
974    if !flags.contains(VarcFlags::HAVE_SCALE_Y) {
975        transform.set_scale_y(transform.scale_x());
976    }
977    Ok(())
978}
979
980fn eval_condition(
981    condition: &Condition<'_>,
982    coords: &[F2Dot14],
983    store_regions: Option<(&MultiItemVariationStore<'_>, &SparseVariationRegionList<'_>)>,
984    scalar_cache: &mut ScalarCache<'_>,
985    scratch: &mut Scratch<'_>,
986) -> Result<bool, Error> {
987    condition.evaluate(coords, |var_idx| {
988        let (var_store, regions) = store_regions.ok_or(Error::Malformed)?;
989        let delta = scratch.deltas.get_mut(..1).ok_or(Error::Malformed)?;
990        compute_tuple_deltas(var_store, regions, var_idx, coords, scalar_cache, delta)?;
991        Ok(delta[0])
992    })
993}
994
995/// Returns the factor that scales font units to `ppem`.
996fn linear_scale(ppem: Option<f32>, units_per_em: u16) -> f32 {
997    match ppem {
998        Some(ppem) if units_per_em != 0 => ppem / units_per_em as f32,
999        _ => 1.0,
1000    }
1001}
1002
1003/// Copies `coords` into `out`, which holds one value per axis.
1004///
1005/// Axes missing from `coords` are set to their default.
1006fn expand_coords(out: &mut [F2Dot14], coords: &[F2Dot14]) {
1007    out.fill(F2Dot14::ZERO);
1008    for (slot, value) in out.iter_mut().zip(coords.iter().copied()) {
1009        *slot = value;
1010    }
1011}
1012
1013/// Sums the deltas at `coords` for the tuple identified by `var_idx` into
1014/// `out`.
1015///
1016/// The length of `out` is the length of the tuple.
1017fn compute_tuple_deltas(
1018    store: &MultiItemVariationStore,
1019    regions: &SparseVariationRegionList,
1020    var_idx: u32,
1021    coords: &[F2Dot14],
1022    cache: &mut ScalarCache<'_>,
1023    out: &mut [f64],
1024) -> Result<(), ReadError> {
1025    // The slice is reused across calls, so it starts at zero rather than at
1026    // the previous call's values.
1027    out.fill(0.0);
1028    if out.is_empty() || var_idx == NO_VARIATION_INDEX {
1029        return Ok(());
1030    }
1031    let outer = (var_idx >> 16) as usize;
1032    let inner = (var_idx & 0xFFFF) as usize;
1033    let data = store.variation_data().get(outer)?;
1034    let region_indices = data.region_indices();
1035    let mut deltas = data.delta_set(inner)?.fetcher();
1036    let regions = regions.regions();
1037
1038    let mut skip = 0;
1039    for region_index in region_indices.iter() {
1040        let region_idx = region_index.get() as usize;
1041        let mut scalar = cache.get(region_idx);
1042        if scalar >= 2.0 {
1043            scalar = regions.get(region_idx)?.compute_scalar_f32(coords);
1044            cache.set(region_idx, scalar);
1045        }
1046        // Regions with a zero scalar are skipped in one seek.
1047        if scalar == 0.0 {
1048            skip += out.len();
1049        } else {
1050            if skip != 0 {
1051                deltas.skip(skip)?;
1052                skip = 0;
1053            }
1054            deltas.add_to_f64_scaled(out, scalar as f64)?;
1055        }
1056    }
1057
1058    Ok(())
1059}
1060
1061#[inline(always)]
1062fn scale_matrix(mut m: Matrix<f32>, s: f32) -> Matrix<f32> {
1063    m.dx *= s;
1064    m.dy *= s;
1065    m
1066}
1067
1068/// A variable composite glyph from a [`Font`](crate::model::Font).
1069///
1070/// This includes the base outlines used to draw its components.
1071#[derive(Clone)]
1072pub struct VarcGlyph<'a> {
1073    pub(super) outlines: &'a Outlines<'a>,
1074    pub(super) outline: Outline,
1075    /// Draws the leaves from the base source selected by the source order.
1076    pub(super) base: OutlineGlyphs<'a>,
1077}
1078
1079impl<'a> VarcGlyph<'a> {
1080    /// Returns the glyph identifier.
1081    pub fn glyph(&self) -> GlyphId {
1082        self.outline.glyph()
1083    }
1084
1085    /// Returns the size in bytes of the working memory needed for drawing.
1086    ///
1087    /// The size depends on the nesting depth, the largest leaf, and the
1088    /// table's axes and regions. It is computed the first time it is needed
1089    /// and kept. Returns zero for a glyph that cannot be drawn, which a draw
1090    /// reports as an error.
1091    pub fn required_memory_size(&self) -> usize {
1092        let leaves = Leaves::new(self.base, DrawOptions::default(), Precision::Fixed);
1093        self.outlines.required_memory_size(&self.outline, &leaves)
1094    }
1095
1096    pub(super) fn draw(
1097        &self,
1098        options: &DrawOptions,
1099        precision: Precision,
1100        buf: &mut [u8],
1101        sink: &mut dyn PathSink,
1102    ) -> Result<DrawnMetrics, Error> {
1103        let leaves = Leaves::new(self.base, *options, precision);
1104        self.outlines
1105            .draw(&self.outline, &leaves, options.ppem, buf, sink)?;
1106        Ok(DrawnMetrics::default())
1107    }
1108}
1109
1110/// Draws the leaves of a composite from the base source.
1111///
1112/// The leaves use the draw options of the composite.
1113pub(super) struct Leaves<'a> {
1114    base: OutlineGlyphs<'a>,
1115    options: DrawOptions,
1116    precision: Precision,
1117}
1118
1119impl<'a> Leaves<'a> {
1120    pub(super) fn new(base: OutlineGlyphs<'a>, options: DrawOptions, precision: Precision) -> Self {
1121        Self {
1122            base,
1123            options,
1124            precision,
1125        }
1126    }
1127}
1128
1129impl VarcContext for Leaves<'_> {
1130    fn coords(&self) -> &[F2Dot14] {
1131        self.base.font().normalized_coords()
1132    }
1133
1134    fn draw_glyph(
1135        &self,
1136        glyph: GlyphId,
1137        coords: &[F2Dot14],
1138        ppem: Option<f32>,
1139        buf: &mut [u8],
1140        sink: &mut dyn PathSink,
1141    ) -> Result<(), Error> {
1142        let options = DrawOptions {
1143            ppem,
1144            ..self.options
1145        };
1146        self.base
1147            .draw_base(glyph, coords, &options, self.precision, buf, sink)
1148    }
1149
1150    fn glyph_buffer_size(&self, glyph: GlyphId) -> usize {
1151        self.base.base_buffer_size(glyph)
1152    }
1153}
1154
1155#[cfg(test)]
1156mod tests {
1157    use super::*;
1158    use crate::{model::glyph::outline::PathElement, FontRef, TableProvider};
1159
1160    fn coord(value: f32) -> F2Dot14 {
1161        F2Dot14::from_f32(value)
1162    }
1163
1164    fn test_outlines<'a>(font: &FontRef<'a>) -> Outlines<'a> {
1165        let axis_count = font
1166            .fvar()
1167            .map(|fvar| fvar.axis_count() as usize)
1168            .unwrap_or_default();
1169        let upem = font.head().unwrap().units_per_em();
1170        Outlines::new(font.varc().unwrap(), upem, axis_count).unwrap()
1171    }
1172
1173    /// Returns working memory for the helpers.
1174    ///
1175    /// The size is computed for `outlines` with no leaves and at least eight
1176    /// axes, regardless of the fixture.
1177    fn test_memory(outlines: &Outlines<'_>) -> alloc::vec::Vec<u8> {
1178        alloc::vec![0u8; test_layout(outlines).packed_len().unwrap()]
1179    }
1180
1181    fn test_layout(outlines: &Outlines<'_>) -> Layout {
1182        Layout {
1183            depth: 1,
1184            axis_count: outlines.axis_count.max(8),
1185            index_count: outlines.index_count.max(8),
1186            region_count: outlines.region_count(),
1187            leaf: 0,
1188        }
1189    }
1190
1191    /// A context that draws nothing and needs no memory.
1192    struct NullContext;
1193
1194    impl VarcContext for NullContext {
1195        fn coords(&self) -> &[F2Dot14] {
1196            &[]
1197        }
1198
1199        fn draw_glyph(
1200            &self,
1201            _glyph: GlyphId,
1202            _coords: &[F2Dot14],
1203            _ppem: Option<f32>,
1204            _buf: &mut [u8],
1205            _pen: &mut dyn PathSink,
1206        ) -> Result<(), Error> {
1207            Ok(())
1208        }
1209
1210        fn glyph_buffer_size(&self, _glyph: GlyphId) -> usize {
1211            0
1212        }
1213    }
1214
1215    fn assert_close(actual: f32, expected: f32) {
1216        let diff = (actual - expected).abs();
1217        assert!(
1218            diff <= 1e-6,
1219            "expected {expected}, got {actual}, diff {diff}"
1220        );
1221    }
1222
1223    #[test]
1224    fn expand_coords_pads_and_truncates() {
1225        let mut out = [F2Dot14::ZERO; 4];
1226        expand_coords(&mut out, &[coord(0.25), coord(-0.5)]);
1227        assert_eq!(
1228            out,
1229            [coord(0.25), coord(-0.5), F2Dot14::ZERO, F2Dot14::ZERO]
1230        );
1231
1232        let mut out = [F2Dot14::ZERO; 1];
1233        expand_coords(&mut out, &[coord(0.25), coord(-0.5)]);
1234        assert_eq!(out, [coord(0.25)]);
1235    }
1236
1237    #[test]
1238    fn scale_matrix_only_scales_translation() {
1239        let matrix = Matrix::from_elements([1.0, 2.0, 3.0, 4.0, 5.0, -6.0]);
1240        assert_eq!(
1241            scale_matrix(matrix, 10.0).elements(),
1242            [1.0, 2.0, 3.0, 4.0, 50.0, -60.0]
1243        );
1244    }
1245
1246    #[test]
1247    fn compute_tuple_deltas_no_variation_index_zeroes_the_tuple() {
1248        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1249        let varc = font.varc().unwrap();
1250        let store = varc.multi_var_store().unwrap().unwrap();
1251        let regions = store.region_list().unwrap();
1252        let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1253        let mut cache = ScalarCache::new(&mut values);
1254        let mut out = [42.0f64; 3];
1255
1256        compute_tuple_deltas(
1257            &store,
1258            &regions,
1259            NO_VARIATION_INDEX,
1260            &[coord(0.0)],
1261            &mut cache,
1262            &mut out,
1263        )
1264        .unwrap();
1265        assert_eq!(out, [0.0, 0.0, 0.0]);
1266
1267        compute_tuple_deltas(
1268            &store,
1269            &regions,
1270            NO_VARIATION_INDEX,
1271            &[coord(0.0)],
1272            &mut cache,
1273            &mut [],
1274        )
1275        .unwrap();
1276    }
1277
1278    #[test]
1279    fn compute_tuple_deltas_invalid_outer_index_errors() {
1280        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1281        let varc = font.varc().unwrap();
1282        let store = varc.multi_var_store().unwrap().unwrap();
1283        let regions = store.region_list().unwrap();
1284        let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1285        let mut cache = ScalarCache::new(&mut values);
1286        let mut out = [0.0f64; 1];
1287
1288        // an outer index past the end of the variation data is refused,
1289        // rather than silently contributing zero deltas
1290        assert!(
1291            compute_tuple_deltas(&store, &regions, 0xFFFF_0000, &[], &mut cache, &mut out).is_err()
1292        );
1293    }
1294
1295    #[test]
1296    fn compute_tuple_deltas_matches_manual_decode() {
1297        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1298        let varc = font.varc().unwrap();
1299        let store = varc.multi_var_store().unwrap().unwrap();
1300        let regions = store.region_list().unwrap();
1301        let region_list = regions.regions();
1302        let coords = [coord(0.5); 8];
1303
1304        let mut tried = 0usize;
1305        for (outer, data) in store.variation_data().iter().enumerate() {
1306            let data = data.unwrap();
1307            let region_count = data.region_indices().len();
1308            if region_count == 0 {
1309                continue;
1310            }
1311            let delta_set_count = data.delta_sets().unwrap().count() as usize;
1312            for inner in 0..delta_set_count.min(3) {
1313                let decoded = data
1314                    .delta_set(inner)
1315                    .unwrap()
1316                    .iter()
1317                    .collect::<alloc::vec::Vec<_>>();
1318                if decoded.is_empty() || decoded.len() % region_count != 0 {
1319                    continue;
1320                }
1321                let tuple_len = decoded.len() / region_count;
1322                let var_idx = ((outer as u32) << 16) | inner as u32;
1323
1324                let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1325                let mut cache = ScalarCache::new(&mut values);
1326                let mut actual = alloc::vec![0.0f64; tuple_len];
1327                compute_tuple_deltas(&store, &regions, var_idx, &coords, &mut cache, &mut actual)
1328                    .unwrap();
1329                let first = actual.clone();
1330
1331                // Same cache after population should not alter results.
1332                compute_tuple_deltas(&store, &regions, var_idx, &coords, &mut cache, &mut actual)
1333                    .unwrap();
1334                assert_eq!(actual, first);
1335
1336                let mut expected = alloc::vec![0.0f64; tuple_len];
1337                for (region_order, region_idx) in data.region_indices().iter().enumerate() {
1338                    let scalar = region_list
1339                        .get(region_idx.get() as usize)
1340                        .unwrap()
1341                        .compute_scalar_f32(&coords);
1342                    if scalar == 0.0 {
1343                        continue;
1344                    }
1345                    let base = region_order * tuple_len;
1346                    for (i, slot) in expected.iter_mut().enumerate() {
1347                        *slot += decoded[base + i] as f64 * scalar as f64;
1348                    }
1349                }
1350                assert_eq!(actual.len(), expected.len());
1351                for (a, e) in actual.iter().zip(expected.iter()) {
1352                    assert_eq!(*a, *e);
1353                }
1354                tried += 1;
1355            }
1356        }
1357        assert!(tried > 0, "expected at least one tuple to be exercised");
1358    }
1359
1360    fn apply_transform_variations_reference(
1361        component: &VarcComponent<'_>,
1362        coords: &[F2Dot14],
1363        transform: &mut DecomposedTransform,
1364        var_store: Option<&MultiItemVariationStore<'_>>,
1365        regions: Option<&SparseVariationRegionList<'_>>,
1366        scalar_cache: &mut ScalarCache<'_>,
1367        deltas: &mut [f64],
1368    ) -> Result<(), Error> {
1369        let Some(var_idx) = component.transform_var_index() else {
1370            return Ok(());
1371        };
1372        let flags = component.flags();
1373        const TRANSFORM_MASK: VarcFlags = VarcFlags::from_bits_truncate(
1374            VarcFlags::HAVE_TRANSLATE_X.bits()
1375                | VarcFlags::HAVE_TRANSLATE_Y.bits()
1376                | VarcFlags::HAVE_ROTATION.bits()
1377                | VarcFlags::HAVE_SCALE_X.bits()
1378                | VarcFlags::HAVE_SCALE_Y.bits()
1379                | VarcFlags::HAVE_SKEW_X.bits()
1380                | VarcFlags::HAVE_SKEW_Y.bits()
1381                | VarcFlags::HAVE_TCENTER_X.bits()
1382                | VarcFlags::HAVE_TCENTER_Y.bits(),
1383        );
1384        let field_count = (flags.bits() & TRANSFORM_MASK.bits()).count_ones() as usize;
1385        if field_count == 0 {
1386            return Ok(());
1387        }
1388
1389        let store = var_store.ok_or(Error::Malformed)?;
1390        let regions = regions.ok_or(Error::Malformed)?;
1391        let deltas = &mut deltas[..field_count];
1392        compute_tuple_deltas(store, regions, var_idx, coords, scalar_cache, deltas)?;
1393
1394        let mut delta_iter = deltas.iter().map(|&delta| delta as f32);
1395        if flags.contains(VarcFlags::HAVE_TRANSLATE_X) {
1396            transform.set_translate_x(transform.translate_x() + delta_iter.next().unwrap_or(0.0));
1397        }
1398        if flags.contains(VarcFlags::HAVE_TRANSLATE_Y) {
1399            transform.set_translate_y(transform.translate_y() + delta_iter.next().unwrap_or(0.0));
1400        }
1401        if flags.contains(VarcFlags::HAVE_ROTATION) {
1402            transform
1403                .set_rotation(transform.rotation() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1404        }
1405        if flags.contains(VarcFlags::HAVE_SCALE_X) {
1406            transform.set_scale_x(transform.scale_x() + delta_iter.next().unwrap_or(0.0) / 1024.0);
1407        }
1408        if flags.contains(VarcFlags::HAVE_SCALE_Y) {
1409            transform.set_scale_y(transform.scale_y() + delta_iter.next().unwrap_or(0.0) / 1024.0);
1410        }
1411        const SKEW_OR_CENTER: VarcFlags = VarcFlags::from_bits_truncate(
1412            VarcFlags::HAVE_SKEW_X.bits()
1413                | VarcFlags::HAVE_SKEW_Y.bits()
1414                | VarcFlags::HAVE_TCENTER_X.bits()
1415                | VarcFlags::HAVE_TCENTER_Y.bits(),
1416        );
1417        if flags.intersects(SKEW_OR_CENTER) {
1418            if flags.contains(VarcFlags::HAVE_SKEW_X) {
1419                transform
1420                    .set_skew_x(transform.skew_x() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1421            }
1422            if flags.contains(VarcFlags::HAVE_SKEW_Y) {
1423                transform
1424                    .set_skew_y(transform.skew_y() + delta_iter.next().unwrap_or(0.0) / 4096.0);
1425            }
1426            if flags.contains(VarcFlags::HAVE_TCENTER_X) {
1427                transform.set_center_x(transform.center_x() + delta_iter.next().unwrap_or(0.0));
1428            }
1429            if flags.contains(VarcFlags::HAVE_TCENTER_Y) {
1430                transform.set_center_y(transform.center_y() + delta_iter.next().unwrap_or(0.0));
1431            }
1432        }
1433        if !flags.contains(VarcFlags::HAVE_SCALE_Y) {
1434            transform.set_scale_y(transform.scale_x());
1435        }
1436        Ok(())
1437    }
1438
1439    #[test]
1440    fn apply_transform_variations_matches_reference_path() {
1441        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1442        let outlines = test_outlines(&font);
1443        let coverage = outlines.varc.coverage().unwrap();
1444        let var_store = outlines.varc.multi_var_store().transpose().unwrap();
1445        let regions = var_store
1446            .as_ref()
1447            .map(|s| s.region_list())
1448            .transpose()
1449            .unwrap();
1450        let region_count = regions
1451            .as_ref()
1452            .map(|r| r.region_count() as usize)
1453            .unwrap_or(0);
1454
1455        let mut coords = alloc::vec![F2Dot14::ZERO; outlines.axis_count];
1456        for (i, c) in coords.iter_mut().enumerate() {
1457            *c = match i % 4 {
1458                0 => coord(0.5),
1459                1 => coord(-0.5),
1460                2 => coord(0.25),
1461                _ => coord(-0.25),
1462            };
1463        }
1464
1465        let mut tested = 0usize;
1466        for gid in coverage.iter() {
1467            let coverage_index = coverage.get(gid).unwrap() as usize;
1468            let glyph = outlines.varc.glyph(coverage_index).unwrap();
1469            for component in glyph.components() {
1470                let component = component.unwrap();
1471                if component.transform_var_index().is_none() {
1472                    continue;
1473                }
1474
1475                let mut transform_new = *component.transform();
1476                let mut transform_ref = *component.transform();
1477                let mut values_new = alloc::vec![0.0f32; region_count];
1478                let mut values_ref = alloc::vec![0.0f32; region_count];
1479                let mut cache_new = ScalarCache::new(&mut values_new);
1480                let mut cache_ref = ScalarCache::new(&mut values_ref);
1481                let mut deltas_ref = [0.0f64; TRANSFORM_FIELD_COUNT];
1482                let mut buf = test_memory(&outlines);
1483                let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1484                let store_regions = var_store.as_ref().zip(regions.as_ref());
1485
1486                apply_transform_variations(
1487                    &component,
1488                    &coords,
1489                    &mut transform_new,
1490                    &mut cache_new,
1491                    &mut scratch,
1492                    store_regions,
1493                )
1494                .unwrap();
1495                apply_transform_variations_reference(
1496                    &component,
1497                    &coords,
1498                    &mut transform_ref,
1499                    var_store.as_ref(),
1500                    regions.as_ref(),
1501                    &mut cache_ref,
1502                    &mut deltas_ref,
1503                )
1504                .unwrap();
1505
1506                assert_close(transform_new.translate_x(), transform_ref.translate_x());
1507                assert_close(transform_new.translate_y(), transform_ref.translate_y());
1508                assert_close(transform_new.rotation(), transform_ref.rotation());
1509                assert_close(transform_new.scale_x(), transform_ref.scale_x());
1510                assert_close(transform_new.scale_y(), transform_ref.scale_y());
1511                assert_close(transform_new.skew_x(), transform_ref.skew_x());
1512                assert_close(transform_new.skew_y(), transform_ref.skew_y());
1513                assert_close(transform_new.center_x(), transform_ref.center_x());
1514                assert_close(transform_new.center_y(), transform_ref.center_y());
1515                tested += 1;
1516                if tested >= 32 {
1517                    break;
1518                }
1519            }
1520            if tested >= 32 {
1521                break;
1522            }
1523        }
1524        assert!(tested > 0, "expected at least one transformed component");
1525    }
1526
1527    // Build the store/regions needed by `eval_condition`'s signature. The
1528    // conditions exercised below never touch the variation store, so any valid
1529    // VARC store works here.
1530    fn condition_eval_env() -> (
1531        FontRef<'static>,
1532        MultiItemVariationStore<'static>,
1533        SparseVariationRegionList<'static>,
1534    ) {
1535        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1536        let varc = font.varc().unwrap();
1537        let store = varc.multi_var_store().unwrap().unwrap();
1538        let regions = store.region_list().unwrap();
1539        (font, store, regions)
1540    }
1541
1542    #[test]
1543    fn component_conditions_without_variations_do_not_require_a_store() {
1544        use crate::{FontData, FontRead};
1545
1546        let leaf = alloc::vec![0, 1, 0, 0, 0xE0, 0, 0x20, 0]; // axis 0: [-0.5, 0.5]
1547        let mut cases = alloc::vec![(leaf.clone(), false)];
1548        for default in [-1i16, 0, 1] {
1549            let mut bytes = alloc::vec![0, 2];
1550            bytes.extend(default.to_be_bytes());
1551            bytes.extend(NO_VARIATION_INDEX.to_be_bytes());
1552            cases.push((bytes, default > 0));
1553        }
1554        for format in [3, 4] {
1555            let mut bytes = alloc::vec![0, format, 1, 0, 0, 6];
1556            bytes.extend_from_slice(&leaf);
1557            cases.push((bytes, false));
1558        }
1559        let mut negate = alloc::vec![0, 5, 0, 0, 5];
1560        negate.extend_from_slice(&leaf);
1561        cases.push((negate, true));
1562
1563        for (condition, expected) in cases {
1564            let mut bytes = alloc::vec![0u8; 24]; // VARC header
1565            bytes[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1566            bytes[12..16].copy_from_slice(&24u32.to_be_bytes()); // condition list
1567            bytes.extend(1u32.to_be_bytes()); // one condition
1568            bytes.extend(8u32.to_be_bytes()); // condition offset
1569            bytes.extend_from_slice(&condition);
1570            let glyphs_offset = bytes.len() as u32;
1571            bytes[20..24].copy_from_slice(&glyphs_offset.to_be_bytes());
1572            // One CFF2 INDEX entry: component with condition 0, gid 1.
1573            bytes.extend([0, 0, 0, 1, 1, 1, 6, 0x80, 0x80, 0, 1, 0]);
1574            let varc = Varc::read(FontData::new(&bytes)).unwrap();
1575            let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1576            let mut outlines = test_outlines(&font);
1577            outlines.varc = varc;
1578            let glyph = outlines.varc.glyph(0).unwrap();
1579            let component = glyph.components().next().unwrap().unwrap();
1580            assert_eq!(component.condition_index(), Some(0));
1581            let mut buf = test_memory(&outlines);
1582            let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1583            let result = outlines.component_condition_met(
1584                &component,
1585                &[coord(0.75)],
1586                &mut ScalarCache::new(&mut []),
1587                &mut scratch,
1588                None,
1589            );
1590            assert_eq!(result.unwrap(), expected, "condition bytes {condition:?}");
1591        }
1592    }
1593
1594    #[test]
1595    fn component_zero_deltas_do_not_require_a_store() {
1596        use crate::{FontData, FontRead};
1597
1598        for (component_bytes, axis_indices, expected_coord, expected_translation) in [
1599            // One explicit axis value with NO_VARIATION_INDEX.
1600            (
1601                alloc::vec![6, 0, 1, 0, 0x40, 0x10, 0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF],
1602                alloc::vec![0, 0], // packed axis index 0
1603                0.25,
1604                0.0,
1605            ),
1606            // An empty axis tuple consumes no deltas, even with a real index.
1607            (alloc::vec![6, 0, 1, 0, 0], alloc::vec![], 0.5, 0.0),
1608            // A translated component with NO_VARIATION_INDEX.
1609            (
1610                alloc::vec![0x18, 0, 1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0, 7],
1611                alloc::vec![],
1612                0.5,
1613                7.0,
1614            ),
1615        ] {
1616            let mut bytes = alloc::vec![0u8; 24];
1617            bytes[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1618            bytes[16..20].copy_from_slice(&24u32.to_be_bytes());
1619            bytes.extend([0, 0, 0, 1, 1, 1, axis_indices.len() as u8 + 1]);
1620            bytes.extend_from_slice(&axis_indices);
1621            let glyphs_offset = bytes.len() as u32;
1622            bytes[20..24].copy_from_slice(&glyphs_offset.to_be_bytes());
1623            bytes.extend([0, 0, 0, 1, 1, 1, component_bytes.len() as u8 + 1]);
1624            bytes.extend_from_slice(&component_bytes);
1625            let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1626            let mut outlines = test_outlines(&font);
1627            outlines.varc = Varc::read(FontData::new(&bytes)).unwrap();
1628            let glyph = outlines.varc.glyph(0).unwrap();
1629            let component = glyph.components().next().unwrap().unwrap();
1630            let mut coords = [F2Dot14::ZERO; 1];
1631            let mut cache = ScalarCache::new(&mut []);
1632            let mut buf = test_memory(&outlines);
1633            let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1634            // A stale scratch delta must not leak into the zero-delta case.
1635            scratch.deltas.fill(100.0);
1636            outlines
1637                .component_coords(
1638                    &component,
1639                    &[coord(0.5)],
1640                    &mut coords,
1641                    &mut cache,
1642                    &mut scratch,
1643                    &[coord(0.5)],
1644                    None,
1645                )
1646                .unwrap();
1647            assert_eq!(coords, [coord(expected_coord)]);
1648            let mut transform = *component.transform();
1649            apply_transform_variations(
1650                &component,
1651                &[coord(0.5)],
1652                &mut transform,
1653                &mut cache,
1654                &mut scratch,
1655                None,
1656            )
1657            .unwrap();
1658            assert_eq!(transform.translate_x(), expected_translation);
1659        }
1660    }
1661
1662    #[test]
1663    fn null_condition_offsets_are_unconditional() {
1664        use crate::{FontData, FontRead};
1665
1666        let leaf = [0, 1, 0, 0, 0xE0, 0, 0x20, 0]; // false at 0.75
1667        let mut cases = alloc::vec![
1668            (None, true),
1669            (Some(alloc::vec![0, 3, 1, 0, 0, 0]), true),
1670            (Some(alloc::vec![0, 4, 1, 0, 0, 0]), true),
1671            (Some(alloc::vec![0, 5, 0, 0, 0]), false),
1672        ];
1673        for format in [3, 4] {
1674            for null_first in [false, true] {
1675                let mut bytes = alloc::vec![0, format, 2];
1676                bytes.extend_from_slice(if null_first {
1677                    &[0, 0, 0, 0, 0, 9]
1678                } else {
1679                    &[0, 0, 9, 0, 0, 0]
1680                });
1681                bytes.extend_from_slice(&leaf);
1682                cases.push((Some(bytes), format == 4));
1683            }
1684        }
1685        for (condition, expected) in cases {
1686            let mut bytes = alloc::vec![0u8; 24];
1687            bytes[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1688            bytes[12..16].copy_from_slice(&24u32.to_be_bytes());
1689            bytes.extend(1u32.to_be_bytes());
1690            bytes.extend(if condition.is_some() { 8u32 } else { 0 }.to_be_bytes());
1691            if let Some(condition) = condition.as_ref() {
1692                bytes.extend_from_slice(condition);
1693            }
1694            let glyphs_offset = bytes.len() as u32;
1695            bytes[20..24].copy_from_slice(&glyphs_offset.to_be_bytes());
1696            bytes.extend([0, 0, 0, 1, 1, 1, 6, 0x80, 0x80, 0, 1, 0]);
1697            let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1698            let mut outlines = test_outlines(&font);
1699            outlines.varc = Varc::read(FontData::new(&bytes)).unwrap();
1700            let glyph = outlines.varc.glyph(0).unwrap();
1701            let component = glyph.components().next().unwrap().unwrap();
1702            let mut buf = test_memory(&outlines);
1703            let mut scratch = test_layout(&outlines).carve(&mut buf).unwrap().scratch;
1704            let result = outlines.component_condition_met(
1705                &component,
1706                &[coord(0.75)],
1707                &mut ScalarCache::new(&mut []),
1708                &mut scratch,
1709                None,
1710            );
1711            assert_eq!(result.unwrap(), expected, "condition {condition:?}");
1712        }
1713    }
1714
1715    // A deeply nested condition tree must return an error instead of overflowing
1716    // the stack. Regression test for the unbounded recursion in `eval_condition`.
1717    #[test]
1718    fn eval_condition_bounds_recursion_depth() {
1719        use crate::{FontData, FontRead};
1720        // Linear chain of Format5Negate tables: each is `format(u16 BE = 5)` +
1721        // `condition_offset(Offset24 BE = 5)`, so every table points 5 bytes
1722        // forward to the next -> an arbitrarily deep condition tree. `CHAIN_LEN`
1723        // is far larger than the limit, proving evaluation bails out early rather
1724        // than walking (and recursing into) the whole chain.
1725        const CHAIN_LEN: usize = 4096;
1726        let mut bytes = alloc::vec::Vec::with_capacity(CHAIN_LEN * 5);
1727        for _ in 0..CHAIN_LEN {
1728            bytes.extend_from_slice(&[0x00, 0x05, 0x00, 0x00, 0x05]);
1729        }
1730        let cond = Condition::read(FontData::new(&bytes)).unwrap();
1731
1732        let (_font, store, regions) = condition_eval_env();
1733        let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1734        let mut cache = ScalarCache::new(&mut values);
1735        let mut deltas = [0.0f64; 1];
1736        let mut scratch = Scratch {
1737            deltas: &mut deltas,
1738            axis_values: &mut [],
1739            axis_indices: &mut [],
1740            edges_left: MAX_COMPOSITE_EDGES,
1741        };
1742
1743        let result = eval_condition(
1744            &cond,
1745            &[],
1746            Some((&store, &regions)),
1747            &mut cache,
1748            &mut scratch,
1749        );
1750        assert!(
1751            matches!(result, Err(Error::LimitExceeded)),
1752            "expected RecursionLimitExceeded, got {result:?}"
1753        );
1754    }
1755
1756    // A normal (shallow) condition tree must still evaluate correctly.
1757    #[test]
1758    fn eval_condition_shallow_is_correct() {
1759        use crate::{FontData, FontRead};
1760        let (_font, store, regions) = condition_eval_env();
1761        let mut values = alloc::vec![0.0f32; regions.region_count() as usize];
1762        let mut cache = ScalarCache::new(&mut values);
1763        let mut deltas = [0.0f64; 1];
1764        let mut scratch = Scratch {
1765            deltas: &mut deltas,
1766            axis_values: &mut [],
1767            axis_indices: &mut [],
1768            edges_left: MAX_COMPOSITE_EDGES,
1769        };
1770
1771        // ConditionFormat1: axis 0, filter range [-1.0, 1.0].
1772        let leaf: [u8; 8] = [0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x40, 0x00];
1773        let cond = Condition::read(FontData::new(&leaf)).unwrap();
1774        assert!(eval_condition(
1775            &cond,
1776            &[coord(0.5)],
1777            Some((&store, &regions)),
1778            &mut cache,
1779            &mut scratch,
1780        )
1781        .unwrap());
1782        assert!(!eval_condition(
1783            &cond,
1784            &[coord(1.5)],
1785            Some((&store, &regions)),
1786            &mut cache,
1787            &mut scratch,
1788        )
1789        .unwrap());
1790
1791        // Format5Negate wrapping the same leaf must negate the result, proving a
1792        // shallow nested tree still evaluates through the recursion guard.
1793        let mut negate = alloc::vec![0x00, 0x05, 0x00, 0x00, 0x05];
1794        negate.extend_from_slice(&leaf);
1795        let cond = Condition::read(FontData::new(&negate)).unwrap();
1796        assert!(!eval_condition(
1797            &cond,
1798            &[coord(0.5)],
1799            Some((&store, &regions)),
1800            &mut cache,
1801            &mut scratch,
1802        )
1803        .unwrap());
1804    }
1805
1806    fn first_nested_varc_edge(outlines: &Outlines<'_>) -> Option<(GlyphId, u32)> {
1807        for gid in outlines.coverage.iter() {
1808            let coverage_index = outlines.coverage.get(gid)?;
1809            let glyph = outlines.varc.glyph(coverage_index as usize).ok()?;
1810            for component in glyph.components() {
1811                let component = component.ok()?;
1812                // Pick an unconditional child edge so the traversal always attempts
1813                // to recurse for this component.
1814                if component.condition_index().is_none()
1815                    && component.gid() != gid
1816                    && outlines.coverage.get(component.gid()).is_some()
1817                {
1818                    return Some((gid, coverage_index));
1819                }
1820            }
1821        }
1822        None
1823    }
1824
1825    #[test]
1826    fn draw_glyph_respects_total_edge_budget() {
1827        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1828        let outlines = test_outlines(&font);
1829        let (root_gid, _) = first_nested_varc_edge(&outlines)
1830            .expect("expected at least one VARC->VARC component edge in fixture font");
1831        let outline = outlines.outline(root_gid).unwrap();
1832        let layout = outlines.layout_of(&outline, &NullContext).unwrap();
1833        let mut buf = alloc::vec![0u8; layout.packed_len().unwrap()];
1834        let mut memory = layout.carve(&mut buf).unwrap();
1835        // Budget of one edge allows entering the root glyph only; any recursive
1836        // component edge must be rejected as over budget.
1837        memory.scratch.edges_left = 1;
1838        let mut pen = alloc::vec::Vec::<PathElement>::new();
1839        let result = outlines.draw_tree(&outline, &layout, &NullContext, None, memory, &mut pen);
1840        assert!(
1841            matches!(result, Err(Error::LimitExceeded)),
1842            "expected RecursionLimitExceeded when edge budget is exhausted, got {result:?}"
1843        );
1844    }
1845
1846    #[test]
1847    fn measuring_respects_total_edge_budget() {
1848        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1849        let outlines = test_outlines(&font);
1850        let (root_gid, root_cov_idx) = first_nested_varc_edge(&outlines)
1851            .expect("expected at least one VARC->VARC component edge in fixture font");
1852        let mut walk = Walk::new();
1853        walk.edges_left = 0;
1854        let leaf = outlines
1855            .measure(root_gid, root_cov_idx, &NullContext, &mut walk)
1856            .unwrap();
1857        assert_eq!(leaf, 0);
1858        assert_eq!(walk.edges_left, 0);
1859        assert!(walk.truncated);
1860    }
1861
1862    // The index scratch holds the longest list in the table, which may be
1863    // longer than the axis count when a list repeats an axis.
1864    #[test]
1865    fn axis_index_scratch_holds_the_longest_list() {
1866        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1867        let outlines = test_outlines(&font);
1868        let varc = font.varc().unwrap();
1869        let lists = varc.axis_indices_list().unwrap().unwrap().count() as usize;
1870        let longest = (0..lists)
1871            .map(|nth| varc.axis_indices(nth).unwrap().iter().count())
1872            .max()
1873            .unwrap();
1874        assert_eq!(outlines.index_count, longest);
1875        // A layout whose lists are longer than its axes carves slices of the
1876        // list length for indices and values, and of the axis count for
1877        // coordinates.
1878        let layout = Layout {
1879            depth: 2,
1880            axis_count: 2,
1881            index_count: 5,
1882            region_count: 3,
1883            leaf: 0,
1884        };
1885        let mut buf = alloc::vec![0u8; layout.packed_len().unwrap()];
1886        let memory = layout.carve(&mut buf).unwrap();
1887        assert_eq!(memory.scratch.axis_indices.len(), 5);
1888        assert_eq!(memory.scratch.axis_values.len(), 5);
1889        assert_eq!(memory.scratch.deltas.len(), TRANSFORM_FIELD_COUNT);
1890        assert_eq!(memory.font_coords.len(), 2);
1891        assert_eq!(memory.coords.len(), 4);
1892    }
1893
1894    // Making an outline reads only the coverage. The tree is measured the
1895    // first time its memory is needed, once, and a clone keeps the result.
1896    #[test]
1897    fn the_tree_is_measured_on_first_use() {
1898        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1899        let outlines = test_outlines(&font);
1900        let (root_gid, _) = first_nested_varc_edge(&outlines).unwrap();
1901        let outline = outlines.outline(root_gid).unwrap();
1902        assert_eq!(
1903            outline.measured.state.load(Ordering::Relaxed),
1904            Measured::UNMEASURED
1905        );
1906        let size = outlines.required_memory_size(&outline, &NullContext);
1907        assert!(size > 0);
1908        let state = outline.measured.state.load(Ordering::Relaxed);
1909        assert_eq!(state & 3, Measured::MEASURED);
1910        let copy = outline.clone();
1911        assert_eq!(copy.measured.state.load(Ordering::Relaxed), state);
1912        assert_eq!(outlines.required_memory_size(&copy, &NullContext), size);
1913        // A glyph the table does not cover has no outline.
1914        let uncovered = (0..font.maxp().unwrap().num_glyphs() as u32)
1915            .map(GlyphId::new)
1916            .find(|gid| !outlines.covers(*gid))
1917            .unwrap();
1918        assert!(outlines.outline(uncovered).is_none());
1919    }
1920
1921    #[test]
1922    fn the_layout_counts_the_nesting_and_the_largest_leaf() {
1923        /// Gives each leaf a size in bytes equal to its glyph id, so the
1924        /// largest is known.
1925        struct LeafSizes;
1926
1927        impl VarcContext for LeafSizes {
1928            fn coords(&self) -> &[F2Dot14] {
1929                &[]
1930            }
1931
1932            fn draw_glyph(
1933                &self,
1934                _glyph: GlyphId,
1935                _coords: &[F2Dot14],
1936                _ppem: Option<f32>,
1937                _buf: &mut [u8],
1938                _pen: &mut dyn PathSink,
1939            ) -> Result<(), Error> {
1940                Ok(())
1941            }
1942
1943            fn glyph_buffer_size(&self, glyph: GlyphId) -> usize {
1944                glyph.to_u32() as usize
1945            }
1946        }
1947
1948        let font = FontRef::new(font_test_data::varc::CJK_6868).unwrap();
1949        let outlines = test_outlines(&font);
1950        let (root_gid, _) = first_nested_varc_edge(&outlines).unwrap();
1951        let outline = outlines.outline(root_gid).unwrap();
1952        let layout = outlines.layout_of(&outline, &LeafSizes).unwrap();
1953        assert!(layout.depth >= 2, "a nested composite is two levels deep");
1954        assert_eq!(layout.axis_count, outlines.axis_count);
1955        assert_eq!(layout.index_count, outlines.index_count);
1956        assert_eq!(layout.region_count, outlines.region_count());
1957        assert!(layout.leaf > 0);
1958        // The block carves at exactly its stated size and not one byte short
1959        // of the leaf.
1960        let packed_len = layout.packed_len().unwrap();
1961        let mut buf = alloc::vec![0u8; packed_len];
1962        assert!(layout.carve(&mut buf).is_some());
1963        let mut short = alloc::vec![0u8; packed_len - layout.leaf - 1];
1964        assert!(layout.carve(&mut short).is_none());
1965        // More regions than memory can hold is refused rather than wrapped.
1966        let huge = Layout {
1967            region_count: usize::MAX / 2,
1968            ..layout
1969        };
1970        assert!(huge.packed_len().is_none());
1971    }
1972
1973    // Every component reference counts against the edge limit, leaves
1974    // included, so a flat composite with too many components is refused
1975    // before anything is drawn.
1976    #[test]
1977    fn leaf_components_count_against_the_edge_limit() {
1978        use crate::model::{
1979            glyph::outline::{DrawMemory, DrawOptions, OutlineSource},
1980            Blob, Font,
1981        };
1982        use alloc::{sync::Arc, vec, vec::Vec};
1983        use types::Tag;
1984        let count = MAX_COMPOSITE_EDGES + 1;
1985        let mut varc = vec![0u8; 24];
1986        varc[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
1987        varc[4..8].copy_from_slice(&24u32.to_be_bytes());
1988        // Coverage of glyph 0 alone.
1989        varc.extend_from_slice(&[0, 1, 0, 1, 0, 0]);
1990        let offset = varc.len() as u32;
1991        varc[20..24].copy_from_slice(&offset.to_be_bytes());
1992        // One CFF2 INDEX entry with 32 bit offsets.
1993        varc.extend_from_slice(&1u32.to_be_bytes());
1994        varc.push(4);
1995        varc.extend_from_slice(&1u32.to_be_bytes());
1996        varc.extend_from_slice(&((count * 3 + 1) as u32).to_be_bytes());
1997        for _ in 0..count {
1998            // No flags, glyph 1.
1999            varc.extend_from_slice(&[0, 0, 1]);
2000        }
2001        let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag| {
2002            if tag == Tag::new(b"VARC") {
2003                return Some(Blob::from(varc.clone()));
2004            }
2005            let base = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).ok()?;
2006            base.table_data(tag)
2007                .map(|data| Blob::from(data.as_bytes().to_vec()))
2008        });
2009        let font = Font::new(source, 0).unwrap();
2010        let outline = font.outline_glyphs().get(GlyphId::new(0)).unwrap();
2011        assert_eq!(outline.source(), OutlineSource::Varc);
2012        assert_eq!(outline.required_memory_size(), 0);
2013        let mut path = Vec::<PathElement>::new();
2014        let result = outline.draw(&DrawOptions::default(), DrawMemory::Scratch, &mut path);
2015        assert_eq!(result.err(), Some(Error::LimitExceeded));
2016        assert!(path.is_empty());
2017    }
2018
2019    // A component that leads back to a glyph being drawn closes a cycle and
2020    // draws nothing; the glyph's other components still draw.
2021    #[test]
2022    fn a_cycle_draws_nothing() {
2023        use crate::model::{
2024            glyph::outline::{DrawMemory, DrawOptions},
2025            Blob, Font,
2026        };
2027        use alloc::{sync::Arc, vec, vec::Vec};
2028        use types::Tag;
2029        const LEAF: u8 = 2;
2030        let mut varc = vec![0u8; 24];
2031        varc[..4].copy_from_slice(&0x00010000u32.to_be_bytes());
2032        varc[4..8].copy_from_slice(&24u32.to_be_bytes());
2033        // Coverage of glyphs 1 and 3, which have outlines of their own.
2034        varc.extend_from_slice(&[0, 1, 0, 2, 0, 1, 0, 3]);
2035        let offset = varc.len() as u32;
2036        varc[20..24].copy_from_slice(&offset.to_be_bytes());
2037        // Glyph 1 uses glyph 3, which uses glyph 1 and the leaf. Each
2038        // component has no flags and a 16 bit glyph.
2039        let records: [&[u8]; 2] = [&[0, 0, 3], &[0, 0, 1, 0, 0, LEAF]];
2040        varc.extend_from_slice(&2u32.to_be_bytes());
2041        varc.push(4);
2042        let mut end = 1u32;
2043        varc.extend_from_slice(&end.to_be_bytes());
2044        for record in records {
2045            end += record.len() as u32;
2046            varc.extend_from_slice(&end.to_be_bytes());
2047        }
2048        records
2049            .iter()
2050            .for_each(|record| varc.extend_from_slice(record));
2051        let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag| {
2052            if tag == Tag::new(b"VARC") {
2053                return Some(Blob::from(varc.clone()));
2054            }
2055            let base = FontRef::new(font_test_data::MATERIAL_SYMBOLS_SUBSET).ok()?;
2056            base.table_data(tag)
2057                .map(|data| Blob::from(data.as_bytes().to_vec()))
2058        });
2059        let draw = |font: &Font, glyph: u8| {
2060            let mut path = Vec::<PathElement>::new();
2061            let outline = font
2062                .outline_glyphs()
2063                .get(GlyphId::new(glyph as u32))
2064                .unwrap();
2065            outline
2066                .draw(&DrawOptions::default(), DrawMemory::Scratch, &mut path)
2067                .unwrap();
2068            path
2069        };
2070        let base = Font::new(font_test_data::MATERIAL_SYMBOLS_SUBSET, 0).unwrap();
2071        let leaf = draw(&base, LEAF);
2072        assert!(!leaf.is_empty());
2073        assert!(!draw(&base, 1).is_empty());
2074        assert_eq!(draw(&Font::new(source, 0).unwrap(), 1), leaf);
2075    }
2076}