allsorts_subset_browser/tables/
glyf.rs

1//! Parsing and writing of the `glyf` table.
2//!
3//! > This table contains information that describes the glyphs in the font in the TrueType outline
4//! > format. Information regarding the rasterizer (scaler) refers to the TrueType rasterizer.
5//!
6//! — <https://docs.microsoft.com/en-us/typography/opentype/spec/glyf>
7
8#[cfg(feature = "outline")]
9mod outline;
10mod subset;
11mod variation;
12
13use std::convert::{TryFrom, TryInto};
14use std::{iter, mem};
15
16use bitflags::bitflags;
17use itertools::Itertools;
18use log::warn;
19use pathfinder_geometry::transform2d::Matrix2x2F;
20use pathfinder_geometry::vector::Vector2F;
21
22use crate::binary::read::{
23    ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope, ReadUnchecked,
24};
25use crate::binary::write::{WriteBinary, WriteBinaryDep, WriteContext};
26use crate::binary::{word_align, I16Be, U16Be, I8, U8};
27use crate::error::{ParseError, WriteError};
28use crate::tables::loca::{owned, LocaTable};
29use crate::tables::os2::Os2;
30use crate::tables::{F2Dot14, HheaTable, HmtxTable, IndexToLocFormat};
31
32pub use subset::SubsetGlyph;
33
34/// Recursion limit for nested composite glyphs
35///
36/// "There is no minimum nesting depth that must be supported" so we use the same value as Harfbuzz.
37#[allow(unused)]
38const COMPOSITE_GLYPH_RECURSION_LIMIT: u8 = 6;
39
40bitflags! {
41    #[rustfmt::skip]
42    pub struct SimpleGlyphFlag: u8 {
43        const ON_CURVE_POINT                       = 0b00000001;
44        const X_SHORT_VECTOR                       = 0b00000010;
45        const Y_SHORT_VECTOR                       = 0b00000100;
46        const REPEAT_FLAG                          = 0b00001000;
47        const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 0b00010000;
48        const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 0b00100000;
49    }
50}
51
52bitflags! {
53    pub struct CompositeGlyphFlag: u16 {
54        /// Bit 0: If this is set, the arguments are 16-bit (uint16 or int16); otherwise, they are
55        /// bytes (uint8 or int8).
56        const ARG_1_AND_2_ARE_WORDS = 0x0001;
57        /// Bit 1: If this is set, the arguments are signed xy values; otherwise, they are unsigned
58        /// point numbers.
59        const ARGS_ARE_XY_VALUES = 0x0002;
60        /// Bit 2: For the xy values if the preceding is true.
61        const ROUND_XY_TO_GRID = 0x0004;
62        /// Bit 3: This indicates that there is a simple scale for the component.
63        ///
64        /// Otherwise, scale = 1.0.
65        const WE_HAVE_A_SCALE = 0x0008;
66        /// Bit 4: Reserved, set to 0
67        /// Bit 5: Indicates at least one more glyph after this one.
68        const MORE_COMPONENTS = 0x0020;
69        /// Bit 6: The x direction will use a different scale from the y direction.
70        const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
71        /// Bit 7: There is a 2 by 2 transformation that will be used to scale the component.
72        const WE_HAVE_A_TWO_BY_TWO = 0x0080;
73        /// Bit 8: Following the last component are instructions for the composite character.
74        const WE_HAVE_INSTRUCTIONS = 0x0100;
75        /// Bit 9: If set, this forces the aw and lsb (and rsb) for the composite to be equal to
76        /// those from this original glyph. This works for hinted and unhinted characters.
77        const USE_MY_METRICS = 0x0200;
78        /// Bit 10: If set, the components of the compound glyph overlap.
79        ///
80        /// Use of this flag is not required in OpenType — that is, it is valid to have components
81        /// overlap without having this flag set. It may affect behaviors in some platforms,
82        /// however. (See Apple’s specification for details regarding behavior in Apple platforms.)
83        /// When used, it must be set on the flag word for the first component. See additional
84        /// remarks, above, for the similar OVERLAP_SIMPLE flag used in simple-glyph descriptions.
85        const OVERLAP_COMPOUND = 0x0400;
86        /// Bit 11: The composite is designed to have the component offset scaled.
87        const SCALED_COMPONENT_OFFSET = 0x0800;
88        /// Bit 12: The composite is designed not to have the component offset scaled.
89        const UNSCALED_COMPONENT_OFFSET = 0x1000;
90        // 0xE010 	Reserved 	Bits 4, 13, 14 and 15 are reserved: set to 0.
91    }
92}
93
94/// `glyf` table
95///
96/// <https://docs.microsoft.com/en-us/typography/opentype/spec/glyf>
97#[derive(Debug, PartialEq)]
98pub struct GlyfTable<'a> {
99    records: Vec<GlyfRecord<'a>>,
100}
101
102#[derive(Debug, PartialEq, Clone)]
103pub enum GlyfRecord<'a> {
104    Present {
105        number_of_contours: i16,
106        scope: ReadScope<'a>,
107    },
108    Parsed(Glyph<'a>),
109}
110
111pub type PhantomPoints = [Point; 4];
112
113#[derive(Debug, PartialEq, Clone)]
114pub enum Glyph<'a> {
115    Empty(EmptyGlyph),
116    Simple(SimpleGlyph<'a>),
117    Composite(CompositeGlyph<'a>),
118}
119
120#[derive(Debug, PartialEq, Clone)]
121pub struct EmptyGlyph {
122    pub phantom_points: Option<PhantomPoints>,
123}
124
125#[derive(Debug, PartialEq, Clone)]
126pub struct SimpleGlyph<'a> {
127    pub bounding_box: BoundingBox,
128    pub end_pts_of_contours: Vec<u16>,
129    pub instructions: &'a [u8],
130    pub coordinates: Vec<(SimpleGlyphFlag, Point)>,
131    /// Phantom points, only populated when applying glyph variation deltas
132    pub phantom_points: Option<Box<PhantomPoints>>,
133}
134
135#[derive(Debug, PartialEq, Clone)]
136pub struct CompositeGlyph<'a> {
137    pub bounding_box: BoundingBox,
138    pub glyphs: Vec<CompositeGlyphComponent>,
139    pub instructions: &'a [u8],
140    /// Phantom points, only populated when applying glyph variation deltas
141    pub phantom_points: Option<Box<PhantomPoints>>,
142}
143
144#[derive(Debug, PartialEq, Clone)]
145pub struct CompositeGlyphComponent {
146    pub flags: CompositeGlyphFlag,
147    pub glyph_index: u16,
148    pub argument1: CompositeGlyphArgument,
149    pub argument2: CompositeGlyphArgument,
150    pub scale: Option<CompositeGlyphScale>,
151}
152
153#[derive(Debug, PartialEq, Copy, Clone)]
154pub enum CompositeGlyphArgument {
155    U8(u8),
156    I8(i8),
157    U16(u16),
158    I16(i16),
159}
160
161#[derive(Debug, PartialEq, Copy, Clone)]
162pub enum CompositeGlyphScale {
163    Scale(F2Dot14),
164    XY { x_scale: F2Dot14, y_scale: F2Dot14 },
165    Matrix([[F2Dot14; 2]; 2]),
166}
167
168pub struct CompositeGlyphs {
169    pub glyphs: Vec<CompositeGlyphComponent>,
170    pub have_instructions: bool,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174pub struct Point(pub i16, pub i16);
175
176#[derive(Debug, PartialEq, Copy, Clone)]
177pub struct BoundingBox {
178    pub x_min: i16,
179    pub x_max: i16,
180    pub y_min: i16,
181    pub y_max: i16,
182}
183
184impl<'b> ReadBinaryDep for GlyfTable<'b> {
185    type Args<'a> = &'a LocaTable<'a>;
186    type HostType<'a> = GlyfTable<'a>;
187
188    fn read_dep<'a>(
189        ctxt: &mut ReadCtxt<'a>,
190        loca: Self::Args<'a>,
191    ) -> Result<Self::HostType<'a>, ParseError> {
192        if loca.offsets.len() < 2 {
193            return Err(ParseError::BadIndex);
194        }
195
196        let glyph_records = loca
197            .offsets
198            .iter()
199            .tuple_windows()
200            .map(|(start, end)| match end.checked_sub(start) {
201                Some(0) => Ok(GlyfRecord::empty()),
202                Some(length) => {
203                    let offset = usize::try_from(start)?;
204                    let glyph_scope = ctxt.scope().offset_length(offset, usize::try_from(length)?);
205                    match glyph_scope {
206                        Ok(scope) => {
207                            let number_of_contours = scope.read::<I16Be>()?;
208                            Ok(GlyfRecord::Present {
209                                number_of_contours,
210                                scope,
211                            })
212                        }
213                        Err(ParseError::BadEof) => {
214                            // The length specified by `loca` is beyond the end of the `glyf`
215                            // table. Try parsing the glyph without a length limit to see if it's
216                            // valid. This is a workaround for a font where the last `loca` offset
217                            // was incorrectly 1 byte beyond the end of the `glyf` table but the
218                            // actual glyph data was valid.
219                            warn!("glyph length out of bounds, trying to parse");
220                            let scope = ctxt.scope().offset(offset);
221                            scope.read::<Glyph<'_>>().map(GlyfRecord::Parsed)
222                        }
223                        Err(err) => Err(err),
224                    }
225                }
226                None => Err(ParseError::BadOffset),
227            })
228            .collect::<Result<Vec<_>, _>>()?;
229
230        Ok(GlyfTable {
231            records: glyph_records,
232        })
233    }
234}
235
236impl<'a> WriteBinaryDep<Self> for GlyfTable<'a> {
237    type Output = owned::LocaTable;
238    type Args = IndexToLocFormat;
239
240    /// Write this glyf table into `ctxt`.
241    ///
242    /// ## A Note About Padding
243    ///
244    /// On the [loca table documentation](https://docs.microsoft.com/en-us/typography/opentype/spec/loca#long-version)
245    /// at the bottom it states:
246    ///
247    /// > Note that the local offsets should be 32-bit aligned. Offsets which are not 32-bit
248    /// > aligned may seriously degrade performance of some processors.
249    ///
250    /// On the [Recommendations for OpenType Fonts](https://docs.microsoft.com/en-us/typography/opentype/spec/recom#loca-table)
251    /// page it states:
252    ///
253    /// > We recommend that local offsets should be 16-bit aligned, in both the short and long
254    /// > formats of this table.
255    ///
256    /// On [Apple's loca documentation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6loca.html)
257    /// it says:
258    ///
259    /// > The glyph data is always word aligned.
260    ///
261    /// Elsewhere in the Apple docs they refer to long as 32-bits, so assuming word here means
262    /// 16-bits.
263    ///
264    /// [An issue](https://github.com/MicrosoftDocs/typography-issues/issues/241) was raised against
265    /// Microsoft's docs regarding this.
266    /// Behdad Esfahbod [commented](https://github.com/MicrosoftDocs/typography-issues/issues/241#issuecomment-495265379):
267    ///
268    /// > All the requirements should be removed since 2019.
269    /// >
270    /// > In reality, in the short format, you are forced to do 16-bit alignment because of how
271    /// > offsets are stored. In the long format, use alignment 1. We've been doing that in
272    /// > fonttools for years and never ever heard a complaint whatsoever.
273    ///
274    /// So with this in mind we implement 16-bit alignment when `index_to_loc_format` is 0,
275    /// and no alignment/padding otherwise.
276    fn write_dep<C: WriteContext>(
277        ctxt: &mut C,
278        table: GlyfTable<'a>,
279        index_to_loc_format: IndexToLocFormat,
280    ) -> Result<Self::Output, WriteError> {
281        let mut offsets: Vec<u32> = Vec::with_capacity(table.records.len() + 1);
282
283        let start = ctxt.bytes_written();
284        for record in table.records {
285            let offset = ctxt.bytes_written();
286
287            offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
288
289            match record {
290                GlyfRecord::Present { scope, .. } => ReadScope::write(ctxt, scope)?,
291                GlyfRecord::Parsed(glyph) => Glyph::write(ctxt, glyph)?,
292            }
293
294            if index_to_loc_format == IndexToLocFormat::Short {
295                let length = ctxt.bytes_written() - offset;
296                let padded_length = word_align(length);
297                ctxt.write_zeros(padded_length - length)?;
298            }
299        }
300
301        // Add the final loca entry
302        offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
303
304        Ok(owned::LocaTable { offsets })
305    }
306}
307
308impl Glyph<'_> {
309    pub fn number_of_contours(&self) -> i16 {
310        match self {
311            Glyph::Empty(_) => 0,
312            Glyph::Simple(simple) => simple.number_of_contours(),
313            Glyph::Composite(_) => -1,
314        }
315    }
316
317    /// Returns the bounding box of the glyph.
318    ///
319    /// Returns `None` if the glyph is an [EmptyGlyph].
320    pub fn bounding_box(&self) -> Option<BoundingBox> {
321        match self {
322            Glyph::Empty(_) => None,
323            Glyph::Simple(simple) => Some(simple.bounding_box),
324            Glyph::Composite(composite) => Some(composite.bounding_box),
325        }
326    }
327
328    /// Returns the phantom points of the glyph.
329    ///
330    /// Returns `None` if the phantom points have not been assigned through glyph variation.
331    pub fn phantom_points(&self) -> Option<PhantomPoints> {
332        match self {
333            Glyph::Empty(empty) => empty.phantom_points,
334            Glyph::Simple(SimpleGlyph { phantom_points, .. })
335            | Glyph::Composite(CompositeGlyph { phantom_points, .. }) => {
336                phantom_points.as_deref().copied()
337            }
338        }
339    }
340
341    /// The number of delta adjustable points in this glyph excluding phantom points.
342    fn number_of_points(&self) -> Result<u16, ParseError> {
343        match self {
344            Glyph::Empty(_) => Ok(0),
345            Glyph::Simple(glyph) => Ok(glyph.coordinates.len().try_into()?),
346            Glyph::Composite(composite) => Ok(composite.glyphs.len().try_into()?),
347        }
348    }
349}
350
351/// Calculate the phantom points from the glyph.
352///
353/// Requires that the bounding box of the glyph is accurate/up-to-date.
354pub(crate) fn calculate_phantom_points(
355    glyph_id: u16,
356    bounding_box: Option<BoundingBox>,
357    hmtx: &HmtxTable<'_>,
358    vmtx: Option<&HmtxTable<'_>>,
359    os2: Option<&Os2>,
360    hhea: &HheaTable,
361) -> Result<[Point; 4], ParseError> {
362    // In a font with TrueType outlines, xMin and xMax values for each glyph are given in the
363    // 'glyf' table. The advance width (“aw”) and left side bearing (“lsb”) can be derived from
364    // the glyph “phantom points”.
365    //
366    // If pp1 and pp2 are TrueType phantom points used to control lsb and rsb, their initial
367    // position in the X-direction is calculated as follows:
368    //
369    // pp1 = xMin - lsb
370    // pp2 = pp1 + aw
371    //
372    // If a glyph has no contours, xMax/xMin are not defined. The left side bearing indicated
373    // in the 'hmtx' table for such glyphs should be zero.
374    //
375    // https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx
376    //
377    // See also notes in FreeType:
378    // https://gitlab.freedesktop.org/freetype/freetype/-/blob/7d45cf2c8f219263c5b9d84763a9a101138b0ed1/src/truetype/ttgload.c#L1280-1363
379    let horizonal_metrics = hmtx.metric(glyph_id)?;
380    let x_min = bounding_box.map(|bbox| bbox.x_min).unwrap_or(0);
381    let y_max = bounding_box.map(|bbox| bbox.y_max).unwrap_or(0);
382    let pp1 = Point(x_min - horizonal_metrics.lsb, 0);
383    let pp2 = Point(pp1.0 + i16::try_from(horizonal_metrics.advance_width)?, 0);
384
385    let (advance_height, tsb) = match vmtx {
386        Some(vmtx) => vmtx.metric(glyph_id).and_then(|metric| {
387            i16::try_from(metric.advance_width)
388                .map(|aw| (aw, metric.lsb))
389                .map_err(|_| ParseError::LimitExceeded)
390        })?,
391        // Fall back on OS/2 table if vmtx table is not present
392        None => {
393            let (default_ascender, default_descender) =
394                match os2.and_then(|os2| os2.version0.as_ref()) {
395                    Some(os2) => (os2.s_typo_ascender, os2.s_typo_descender),
396                    None => (hhea.ascender, hhea.descender),
397                };
398            let advance_height = default_ascender - default_descender;
399            let tsb = default_ascender - y_max;
400            (advance_height, tsb)
401        }
402    };
403
404    let x = 0;
405    let pp3 = Point(x, y_max + tsb);
406    let pp4 = Point(x, pp3.1 - advance_height);
407
408    Ok([pp1, pp2, pp3, pp4])
409}
410
411impl<'b> ReadBinary for Glyph<'b> {
412    type HostType<'a> = Glyph<'a>;
413
414    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
415        let number_of_contours = ctxt.read_i16be()?;
416
417        if number_of_contours >= 0 {
418            // Simple glyph
419            // Cast is safe as we've checked value is positive above
420            let glyph = ctxt.read_dep::<SimpleGlyph<'_>>(number_of_contours as u16)?;
421            Ok(Glyph::Simple(glyph))
422        } else {
423            // Composite glyph
424            let glyph = ctxt.read::<CompositeGlyph<'_>>()?;
425            Ok(Glyph::Composite(glyph))
426        }
427    }
428}
429
430impl<'a> WriteBinary for Glyph<'a> {
431    type Output = ();
432
433    fn write<C: WriteContext>(ctxt: &mut C, glyph: Glyph<'a>) -> Result<(), WriteError> {
434        match glyph {
435            Glyph::Empty(_) => Ok(()),
436            Glyph::Simple(simple_glyph) => SimpleGlyph::write(ctxt, simple_glyph),
437            Glyph::Composite(composite) => CompositeGlyph::write(ctxt, composite),
438        }
439    }
440}
441
442impl<'a> SimpleGlyph<'a> {
443    pub fn number_of_contours(&self) -> i16 {
444        // TODO: Revisit this to see how we might enforce its validity
445        // In theory there could be more than i16::MAX items in end_pts_of_contours
446        self.end_pts_of_contours.len() as i16
447    }
448
449    pub fn contours(&self) -> impl Iterator<Item = &[(SimpleGlyphFlag, Point)]> {
450        self.end_pts_of_contours.iter().scan(0, move |i, &end| {
451            let start = *i;
452            let end = usize::from(end);
453            *i = end + 1;
454            self.coordinates.get(start..=end)
455        })
456    }
457
458    pub fn bounding_box(&self) -> BoundingBox {
459        BoundingBox::from_points(self.coordinates.iter().copied().map(|(_flag, point)| point))
460    }
461}
462
463impl<'b> ReadBinaryDep for SimpleGlyph<'b> {
464    type Args<'a> = u16;
465    type HostType<'a> = SimpleGlyph<'a>;
466
467    fn read_dep<'a>(
468        ctxt: &mut ReadCtxt<'a>,
469        number_of_contours: Self::Args<'_>,
470    ) -> Result<Self::HostType<'a>, ParseError> {
471        let number_of_contours = usize::from(number_of_contours);
472        let bounding_box = ctxt.read::<BoundingBox>()?;
473        let end_pts_of_contours = ctxt.read_array::<U16Be>(number_of_contours)?.to_vec();
474        let instruction_length = ctxt.read::<U16Be>()?;
475        let instructions = ctxt.read_slice(usize::from(instruction_length))?;
476        // end_pts_of_contours stores the index of the end points.
477        // Therefore the number of coordinates is the last index + 1
478        let number_of_coordinates = end_pts_of_contours
479            .last()
480            .map_or(0, |&last| usize::from(last) + 1);
481
482        // Read all the flags
483        let mut coordinates = Vec::with_capacity(number_of_coordinates);
484        while coordinates.len() < number_of_coordinates {
485            let flag = ctxt.read::<SimpleGlyphFlag>()?;
486            if flag.is_repeated() {
487                let count = usize::from(ctxt.read::<U8>()?) + 1; // + 1 to include the current entry
488                let repeat = iter::repeat((flag, Point::zero())).take(count);
489                coordinates.extend(repeat)
490            } else {
491                coordinates.push((flag, Point::zero()));
492            }
493        }
494
495        // Read the x coordinates
496        for (flag, Point(x, _y)) in coordinates.iter_mut() {
497            *x = if flag.x_is_short() {
498                ctxt.read::<U8>()
499                    .map(|val| i16::from(val) * flag.x_short_sign())?
500            } else if flag.x_is_same_or_positive() {
501                0
502            } else {
503                ctxt.read::<I16Be>()?
504            }
505        }
506
507        // Read y coordinates, updating the Points in `coordinates`
508        let mut prev_point = Point::zero();
509        for (flag, point) in coordinates.iter_mut() {
510            let y = if flag.y_is_short() {
511                ctxt.read::<U8>()
512                    .map(|val| i16::from(val) * flag.y_short_sign())?
513            } else if flag.y_is_same_or_positive() {
514                0
515            } else {
516                ctxt.read::<I16Be>()?
517            };
518
519            // The x and y coordinates are stored as deltas against the previous point, with the
520            // first one being implicitly against (0, 0). Here we resolve these deltas into
521            // absolute (x, y) values.
522            prev_point = Point(prev_point.0 + point.0, prev_point.1 + y);
523            *point = prev_point
524        }
525
526        Ok(SimpleGlyph {
527            bounding_box,
528            end_pts_of_contours,
529            instructions,
530            coordinates,
531            phantom_points: None,
532        })
533    }
534}
535
536impl<'a> WriteBinary for SimpleGlyph<'a> {
537    type Output = ();
538
539    fn write<C: WriteContext>(ctxt: &mut C, glyph: SimpleGlyph<'_>) -> Result<(), WriteError> {
540        I16Be::write(ctxt, glyph.number_of_contours())?;
541        BoundingBox::write(ctxt, glyph.bounding_box)?;
542        ctxt.write_vec::<U16Be, _>(glyph.end_pts_of_contours)?;
543        U16Be::write(ctxt, u16::try_from(glyph.instructions.len())?)?;
544        ctxt.write_bytes(glyph.instructions)?;
545
546        // Flags and coordinates are written without any attempt to compact them using
547        // smaller representation, use of REPEAT, or X/Y_IS_SAME.
548        // TODO: try to compact the values written
549
550        // flags
551        let mask = SimpleGlyphFlag::ON_CURVE_POINT; // ON_CURVE_POINT is the only flag that needs to carry through
552        for flag in glyph.coordinates.iter().map(|(flag, _)| *flag) {
553            U8::write(ctxt, (flag & mask).bits())?;
554        }
555
556        // x coordinates
557        let mut prev_x = 0;
558        for (_, Point(x, _)) in &glyph.coordinates {
559            let delta_x = x - prev_x;
560            I16Be::write(ctxt, delta_x)?;
561            prev_x = *x;
562        }
563
564        // y coordinates
565        let mut prev_y = 0;
566        for (_, Point(_, y)) in &glyph.coordinates {
567            let delta_y = y - prev_y;
568            I16Be::write(ctxt, delta_y)?;
569            prev_y = *y;
570        }
571
572        Ok(())
573    }
574}
575
576impl ReadFrom for SimpleGlyphFlag {
577    type ReadType = U8;
578
579    fn read_from(flag: u8) -> Self {
580        SimpleGlyphFlag::from_bits_truncate(flag)
581    }
582}
583
584impl ReadBinary for CompositeGlyphs {
585    type HostType<'a> = Self;
586
587    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
588        let mut have_instructions = false;
589        let mut glyphs = Vec::new();
590        loop {
591            let flags = ctxt.read::<CompositeGlyphFlag>()?;
592            let data = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
593
594            if flags.we_have_instructions() {
595                have_instructions = true;
596            }
597
598            glyphs.push(data);
599
600            if !flags.more_components() {
601                break;
602            }
603        }
604
605        Ok(CompositeGlyphs {
606            glyphs,
607            have_instructions,
608        })
609    }
610}
611
612impl ReadBinary for CompositeGlyph<'_> {
613    type HostType<'a> = CompositeGlyph<'a>;
614
615    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
616        let bounding_box = ctxt.read::<BoundingBox>()?;
617        let glyphs = ctxt.read::<CompositeGlyphs>()?;
618
619        let instruction_length = if glyphs.have_instructions {
620            usize::from(ctxt.read::<U16Be>()?)
621        } else {
622            0
623        };
624        let instructions = ctxt.read_slice(instruction_length)?;
625
626        Ok(CompositeGlyph {
627            bounding_box,
628            glyphs: glyphs.glyphs,
629            instructions,
630            phantom_points: None,
631        })
632    }
633}
634
635impl WriteBinary for CompositeGlyph<'_> {
636    type Output = ();
637
638    fn write<C: WriteContext>(ctxt: &mut C, composite: Self) -> Result<Self::Output, WriteError> {
639        I16Be::write(ctxt, -1_i16)?; // number_of_contours
640        BoundingBox::write(ctxt, composite.bounding_box)?;
641        let mut has_instructions = false;
642        for glyph in composite.glyphs {
643            has_instructions |= glyph.flags.we_have_instructions();
644            CompositeGlyphComponent::write(ctxt, glyph)?;
645        }
646        if has_instructions {
647            U16Be::write(ctxt, u16::try_from(composite.instructions.len())?)?;
648            ctxt.write_bytes(composite.instructions)?;
649        }
650        Ok(())
651    }
652}
653
654impl SimpleGlyphFlag {
655    pub fn is_on_curve(self) -> bool {
656        self & Self::ON_CURVE_POINT == Self::ON_CURVE_POINT
657    }
658
659    pub fn x_is_short(self) -> bool {
660        self & Self::X_SHORT_VECTOR == Self::X_SHORT_VECTOR
661    }
662
663    pub fn y_is_short(self) -> bool {
664        self & Self::Y_SHORT_VECTOR == Self::Y_SHORT_VECTOR
665    }
666
667    pub fn is_repeated(self) -> bool {
668        self & Self::REPEAT_FLAG == Self::REPEAT_FLAG
669    }
670
671    pub fn x_short_sign(self) -> i16 {
672        if self.x_is_same_or_positive() {
673            1
674        } else {
675            -1
676        }
677    }
678
679    pub fn y_short_sign(self) -> i16 {
680        if self.y_is_same_or_positive() {
681            1
682        } else {
683            -1
684        }
685    }
686
687    pub fn x_is_same_or_positive(self) -> bool {
688        self & Self::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
689            == Self::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
690    }
691
692    pub fn y_is_same_or_positive(self) -> bool {
693        self & Self::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR
694            == Self::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR
695    }
696}
697
698impl ReadFrom for CompositeGlyphFlag {
699    type ReadType = U16Be;
700
701    fn read_from(flag: u16) -> Self {
702        CompositeGlyphFlag::from_bits_truncate(flag)
703    }
704}
705
706impl ReadBinaryDep for CompositeGlyphArgument {
707    type Args<'a> = CompositeGlyphFlag;
708    type HostType<'a> = Self;
709
710    fn read_dep<'a>(ctxt: &mut ReadCtxt<'a>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
711        let arg = match (flags.arg_1_and_2_are_words(), flags.args_are_xy_values()) {
712            (true, true) => CompositeGlyphArgument::I16(ctxt.read_i16be()?),
713            (true, false) => CompositeGlyphArgument::U16(ctxt.read_u16be()?),
714            (false, true) => CompositeGlyphArgument::I8(ctxt.read_i8()?),
715            (false, false) => CompositeGlyphArgument::U8(ctxt.read_u8()?),
716        };
717
718        Ok(arg)
719    }
720}
721
722impl WriteBinary for CompositeGlyphArgument {
723    type Output = ();
724
725    fn write<C: WriteContext>(ctxt: &mut C, arg: CompositeGlyphArgument) -> Result<(), WriteError> {
726        match arg {
727            CompositeGlyphArgument::U8(val) => U8::write(ctxt, val),
728            CompositeGlyphArgument::I8(val) => I8::write(ctxt, val),
729            CompositeGlyphArgument::U16(val) => U16Be::write(ctxt, val),
730            CompositeGlyphArgument::I16(val) => I16Be::write(ctxt, val),
731        }
732    }
733}
734
735impl ReadBinaryDep for CompositeGlyphComponent {
736    type Args<'a> = CompositeGlyphFlag;
737    type HostType<'a> = Self;
738
739    fn read_dep<'a>(ctxt: &mut ReadCtxt<'a>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
740        let glyph_index = ctxt.read_u16be()?;
741        let argument1 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
742        let argument2 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
743
744        let scale = if flags.we_have_a_scale() {
745            Some(CompositeGlyphScale::Scale(ctxt.read::<F2Dot14>()?))
746        } else if flags.we_have_an_x_and_y_scale() {
747            Some(CompositeGlyphScale::XY {
748                x_scale: ctxt.read::<F2Dot14>()?,
749                y_scale: ctxt.read::<F2Dot14>()?,
750            })
751        } else if flags.we_have_a_two_by_two() {
752            Some(CompositeGlyphScale::Matrix([
753                [ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
754                [ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
755            ]))
756        } else {
757            None
758        };
759
760        Ok(CompositeGlyphComponent {
761            flags,
762            glyph_index,
763            argument1,
764            argument2,
765            scale,
766        })
767    }
768}
769
770impl WriteBinary for CompositeGlyphComponent {
771    type Output = ();
772
773    fn write<C: WriteContext>(
774        ctxt: &mut C,
775        glyph: CompositeGlyphComponent,
776    ) -> Result<(), WriteError> {
777        U16Be::write(ctxt, glyph.flags.bits())?;
778        U16Be::write(ctxt, glyph.glyph_index)?;
779        CompositeGlyphArgument::write(ctxt, glyph.argument1)?;
780        CompositeGlyphArgument::write(ctxt, glyph.argument2)?;
781        if let Some(scale) = glyph.scale {
782            CompositeGlyphScale::write(ctxt, scale)?;
783        }
784        Ok(())
785    }
786}
787
788impl WriteBinary for CompositeGlyphScale {
789    type Output = ();
790
791    fn write<C: WriteContext>(ctxt: &mut C, scale: CompositeGlyphScale) -> Result<(), WriteError> {
792        match scale {
793            CompositeGlyphScale::Scale(scale) => F2Dot14::write(ctxt, scale)?,
794            CompositeGlyphScale::XY { x_scale, y_scale } => {
795                F2Dot14::write(ctxt, x_scale)?;
796                F2Dot14::write(ctxt, y_scale)?;
797            }
798            CompositeGlyphScale::Matrix(matrix) => {
799                F2Dot14::write(ctxt, matrix[0][0])?;
800                F2Dot14::write(ctxt, matrix[0][1])?;
801                F2Dot14::write(ctxt, matrix[1][0])?;
802                F2Dot14::write(ctxt, matrix[1][1])?;
803            }
804        }
805
806        Ok(())
807    }
808}
809
810impl ReadFrom for BoundingBox {
811    type ReadType = ((I16Be, I16Be), (I16Be, I16Be));
812
813    fn read_from(((x_min, y_min), (x_max, y_max)): ((i16, i16), (i16, i16))) -> Self {
814        BoundingBox {
815            x_min,
816            y_min,
817            x_max,
818            y_max,
819        }
820    }
821}
822
823impl WriteBinary for BoundingBox {
824    type Output = ();
825
826    fn write<C: WriteContext>(ctxt: &mut C, bbox: BoundingBox) -> Result<(), WriteError> {
827        I16Be::write(ctxt, bbox.x_min)?;
828        I16Be::write(ctxt, bbox.y_min)?;
829        I16Be::write(ctxt, bbox.x_max)?;
830        I16Be::write(ctxt, bbox.y_max)?;
831        Ok(())
832    }
833}
834
835impl<'a> GlyfTable<'a> {
836    pub fn new(records: Vec<GlyfRecord<'a>>) -> Result<Self, ParseError> {
837        if records.len() > usize::from(u16::MAX) {
838            return Err(ParseError::LimitExceeded);
839        }
840        Ok(GlyfTable { records })
841    }
842
843    /// Returns the number of glyphs in this `glyf` table.
844    ///
845    /// Returns a `u16` for convenience of interacting with other parts of the code.
846    pub fn num_glyphs(&self) -> u16 {
847        // NOTE(cast): Safe as we check records length in `new` and `push`.
848        self.records.len() as u16
849    }
850
851    pub fn records(&self) -> &[GlyfRecord<'a>] {
852        &self.records
853    }
854
855    pub fn records_mut(&mut self) -> &mut [GlyfRecord<'a>] {
856        &mut self.records
857    }
858
859    pub fn push(&mut self, record: GlyfRecord<'a>) -> Result<(), ParseError> {
860        if self.num_glyphs() < u16::MAX {
861            self.records.push(record);
862            Ok(())
863        } else {
864            Err(ParseError::LimitExceeded)
865        }
866    }
867
868    /// Returns a parsed glyph, converting [GlyfRecord::Present] into [GlyfRecord::Parsed] if
869    /// necessary.
870    pub fn get_parsed_glyph(&mut self, glyph_index: u16) -> Result<&Glyph<'a>, ParseError> {
871        let record = self
872            .records
873            .get_mut(usize::from(glyph_index))
874            .ok_or(ParseError::BadIndex)?;
875        record.parse()?;
876        match record {
877            GlyfRecord::Parsed(glyph) => Ok(glyph),
878            GlyfRecord::Present { .. } => unreachable!("glyph should be parsed"),
879        }
880    }
881
882    /// Takes the glyph at `glyph_index` out of the table replacing it with `GlyphRecord::Empty`
883    /// and returns it.
884    ///
885    /// Returns `None` if the index is out-of-bounds.
886    pub(crate) fn take(&mut self, glyph_index: u16) -> Option<GlyfRecord<'a>> {
887        let target = self.records.get_mut(usize::from(glyph_index))?;
888        Some(mem::replace(target, GlyfRecord::empty()))
889    }
890
891    /// Replaces the glyph at `glyph_index` with the supplied `GlyfRecord`.
892    ///
893    /// Returns an error if the index is out-of-bounds.
894    pub(crate) fn replace(
895        &mut self,
896        glyph_index: u16,
897        record: GlyfRecord<'a>,
898    ) -> Result<(), ParseError> {
899        let target = self
900            .records
901            .get_mut(usize::from(glyph_index))
902            .ok_or(ParseError::BadIndex)?;
903        *target = record;
904        Ok(())
905    }
906}
907
908impl<'a> GlyfRecord<'a> {
909    /// Construct an empty glyph record
910    pub fn empty() -> Self {
911        GlyfRecord::Parsed(Glyph::Empty(EmptyGlyph::new()))
912    }
913
914    pub fn number_of_contours(&self) -> i16 {
915        match self {
916            GlyfRecord::Present {
917                number_of_contours, ..
918            } => *number_of_contours,
919            GlyfRecord::Parsed(glyph) => glyph.number_of_contours(),
920        }
921    }
922
923    /// The number of delta adjustable points in this glyph record excluding phantom points.
924    pub fn number_of_points(&self) -> Result<u16, ParseError> {
925        // The `maxp` table contains fields:
926        //
927        // * maxPoints            Maximum points in a non-composite glyph.
928        // * maxCompositePoints   Maximum points in a composite glyph.
929        //
930        // Both of which are u16 so that's what we return here.
931        match self {
932            GlyfRecord::Present {
933                scope,
934                number_of_contours,
935            } => {
936                let mut ctxt = scope.ctxt();
937                // skip glyph header: number_of_contours and the bounding box
938                let _skip = ctxt.read_slice(U16Be::SIZE + BoundingBox::SIZE)?;
939                if *number_of_contours >= 0 {
940                    // Simple glyph
941                    let end_pts_of_contours =
942                        ctxt.read_array::<U16Be>(*number_of_contours as usize)?;
943                    // end_pts_of_contours stores the index of the end points.
944                    // Therefore the number of coordinates is the last index + 1
945                    match end_pts_of_contours.last() {
946                        Some(last) => last.checked_add(1).ok_or(ParseError::LimitExceeded),
947                        None => Ok(0),
948                    }
949                } else {
950                    // Composite glyph
951                    let mut count = 0;
952                    loop {
953                        let flags = ctxt.read::<CompositeGlyphFlag>()?;
954                        let _composite_glyph = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
955                        count += 1;
956                        if !flags.more_components() {
957                            break;
958                        }
959                    }
960                    Ok(count)
961                }
962            }
963            GlyfRecord::Parsed(glyph) => glyph.number_of_points(),
964        }
965    }
966
967    pub fn is_composite(&self) -> bool {
968        self.number_of_contours() < 0
969    }
970
971    /// Turn self from GlyfRecord::Present into GlyfRecord::Parsed
972    pub fn parse(&mut self) -> Result<(), ParseError> {
973        if let GlyfRecord::Present { scope, .. } = self {
974            *self = scope.read::<Glyph<'_>>().map(GlyfRecord::Parsed)?;
975        }
976        Ok(())
977    }
978}
979
980impl<'a> From<SimpleGlyph<'a>> for GlyfRecord<'a> {
981    fn from(glyph: SimpleGlyph<'a>) -> GlyfRecord<'a> {
982        GlyfRecord::Parsed(Glyph::Simple(glyph))
983    }
984}
985
986impl<'a> From<CompositeGlyph<'a>> for GlyfRecord<'a> {
987    fn from(glyph: CompositeGlyph<'a>) -> GlyfRecord<'a> {
988        GlyfRecord::Parsed(Glyph::Composite(glyph))
989    }
990}
991
992impl EmptyGlyph {
993    pub fn new() -> Self {
994        EmptyGlyph {
995            phantom_points: None,
996        }
997    }
998}
999
1000impl CompositeGlyphFlag {
1001    pub fn arg_1_and_2_are_words(self) -> bool {
1002        self & Self::ARG_1_AND_2_ARE_WORDS == Self::ARG_1_AND_2_ARE_WORDS
1003    }
1004
1005    pub fn args_are_xy_values(self) -> bool {
1006        self & Self::ARGS_ARE_XY_VALUES == Self::ARGS_ARE_XY_VALUES
1007    }
1008
1009    pub fn we_have_a_scale(self) -> bool {
1010        self & Self::WE_HAVE_A_SCALE == Self::WE_HAVE_A_SCALE
1011    }
1012
1013    pub fn we_have_an_x_and_y_scale(self) -> bool {
1014        self & Self::WE_HAVE_AN_X_AND_Y_SCALE == Self::WE_HAVE_AN_X_AND_Y_SCALE
1015    }
1016
1017    pub fn we_have_a_two_by_two(self) -> bool {
1018        self & Self::WE_HAVE_A_TWO_BY_TWO == Self::WE_HAVE_A_TWO_BY_TWO
1019    }
1020
1021    pub fn more_components(self) -> bool {
1022        self & Self::MORE_COMPONENTS == Self::MORE_COMPONENTS
1023    }
1024
1025    pub fn we_have_instructions(self) -> bool {
1026        self & Self::WE_HAVE_INSTRUCTIONS == Self::WE_HAVE_INSTRUCTIONS
1027    }
1028
1029    pub fn component_offsets(self) -> ComponentOffsets {
1030        // The SCALED_COMPONENT_OFFSET and UNSCALED_COMPONENT_OFFSET flags are used to determine
1031        // how x and y offset values are to be interpreted when the component glyph is scaled. If
1032        // the SCALED_COMPONENT_OFFSET flag is set, then the x and y offset values are deemed to be
1033        // in the component glyph’s coordinate system, and the scale transformation is applied to
1034        // both values.
1035        //
1036        // If the UNSCALED_COMPONENT_OFFSET flag is set, then the x and y offset values are deemed
1037        // to be in the current glyph’s coordinate system, and the scale transformation is not
1038        // applied to either value.
1039        //
1040        // If neither flag is set, then the rasterizer will apply a default behavior. On Microsoft
1041        // and Apple platforms, the default behavior is the same as when the
1042        // UNSCALED_COMPONENT_OFFSET flag is set; this behavior is recommended for all rasterizer
1043        // implementations. If a font has both flags set, this is invalid; the rasterizer should use
1044        // its default behavior for this case.
1045        let scaled = self & Self::SCALED_COMPONENT_OFFSET == Self::SCALED_COMPONENT_OFFSET;
1046        let unscaled = self & Self::UNSCALED_COMPONENT_OFFSET == Self::UNSCALED_COMPONENT_OFFSET;
1047        match (scaled, unscaled) {
1048            (true, false) => ComponentOffsets::Scaled,
1049            (false, true) => ComponentOffsets::Unscaled,
1050            // Default for neither or both set
1051            (true, true) | (false, false) => ComponentOffsets::Unscaled,
1052        }
1053    }
1054}
1055
1056#[derive(Copy, Clone, Eq, PartialEq)]
1057pub enum ComponentOffsets {
1058    Scaled,
1059    Unscaled,
1060}
1061
1062impl Point {
1063    pub fn zero() -> Self {
1064        Point(0, 0)
1065    }
1066}
1067
1068impl BoundingBox {
1069    pub fn empty() -> Self {
1070        BoundingBox {
1071            x_min: 0,
1072            x_max: 0,
1073            y_min: 0,
1074            y_max: 0,
1075        }
1076    }
1077
1078    /// Calculate xMin, xMax and yMin, yMax from a collection of `Points`
1079    ///
1080    /// Panics if `points` is empty.
1081    pub fn from_points(points: impl ExactSizeIterator<Item = Point>) -> Self {
1082        assert!(points.len() > 0);
1083        let mut points = points.peekable();
1084
1085        // NOTE(unwrap): Safe as length is at least 1
1086        let &Point(initial_x, initial_y) = points.peek().unwrap();
1087        let initial = BoundingBox {
1088            x_min: initial_x,
1089            x_max: initial_x,
1090            y_min: initial_y,
1091            y_max: initial_y,
1092        };
1093
1094        points.fold(initial, |mut bounding_box, point| {
1095            bounding_box.add(point);
1096            bounding_box
1097        })
1098    }
1099
1100    /// Update this bounding box to contain `point`.
1101    pub fn add(&mut self, Point(x, y): Point) {
1102        self.x_min = i16::min(x, self.x_min);
1103        self.x_max = i16::max(x, self.x_max);
1104        self.y_min = i16::min(y, self.y_min);
1105        self.y_max = i16::max(y, self.y_max);
1106    }
1107}
1108
1109impl std::ops::Add for Point {
1110    type Output = Self;
1111
1112    fn add(self, Point(x1, y1): Point) -> Self::Output {
1113        let Point(x, y) = self;
1114        Point(x + x1, y + y1)
1115    }
1116}
1117
1118impl From<CompositeGlyphArgument> for i32 {
1119    fn from(arg: CompositeGlyphArgument) -> Self {
1120        match arg {
1121            CompositeGlyphArgument::U8(value) => i32::from(value),
1122            CompositeGlyphArgument::I8(value) => i32::from(value),
1123            CompositeGlyphArgument::U16(value) => i32::from(value),
1124            CompositeGlyphArgument::I16(value) => i32::from(value),
1125        }
1126    }
1127}
1128
1129impl TryFrom<CompositeGlyphArgument> for u16 {
1130    type Error = std::num::TryFromIntError;
1131
1132    fn try_from(arg: CompositeGlyphArgument) -> Result<Self, Self::Error> {
1133        match arg {
1134            CompositeGlyphArgument::U8(value) => Ok(u16::from(value)),
1135            CompositeGlyphArgument::I8(value) => u16::try_from(value),
1136            CompositeGlyphArgument::U16(value) => Ok(value),
1137            CompositeGlyphArgument::I16(value) => u16::try_from(value),
1138        }
1139    }
1140}
1141
1142impl From<CompositeGlyphScale> for Matrix2x2F {
1143    fn from(scale: CompositeGlyphScale) -> Self {
1144        match scale {
1145            CompositeGlyphScale::Scale(scale) => {
1146                let scale = f32::from(scale);
1147                Matrix2x2F::from_scale(scale)
1148            }
1149            CompositeGlyphScale::XY { x_scale, y_scale } => {
1150                let scale = Vector2F::new(f32::from(x_scale), f32::from(y_scale));
1151                Matrix2x2F::from_scale(scale)
1152            }
1153            CompositeGlyphScale::Matrix(matrix) => Matrix2x2F::row_major(
1154                f32::from(matrix[0][0]),
1155                f32::from(matrix[0][1]),
1156                f32::from(matrix[1][0]),
1157                f32::from(matrix[1][1]),
1158            ),
1159        }
1160    }
1161}
1162
1163#[cfg(test)]
1164mod tests {
1165    use super::*;
1166    use crate::binary::write::WriteBuffer;
1167    use crate::error::ReadWriteError;
1168
1169    pub(super) fn simple_glyph_fixture() -> SimpleGlyph<'static> {
1170        SimpleGlyph {
1171            bounding_box: BoundingBox {
1172                x_min: 60,
1173                x_max: 915,
1174                y_min: -105,
1175                y_max: 702,
1176            },
1177            end_pts_of_contours: vec![8],
1178            instructions: &[],
1179            coordinates: vec![
1180                (
1181                    SimpleGlyphFlag::ON_CURVE_POINT
1182                        | SimpleGlyphFlag::Y_SHORT_VECTOR
1183                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
1184                    Point(433, 77),
1185                ),
1186                (
1187                    SimpleGlyphFlag::X_SHORT_VECTOR
1188                        | SimpleGlyphFlag::Y_SHORT_VECTOR
1189                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1190                    Point(499, 30),
1191                ),
1192                (
1193                    SimpleGlyphFlag::ON_CURVE_POINT
1194                        | SimpleGlyphFlag::X_SHORT_VECTOR
1195                        | SimpleGlyphFlag::Y_SHORT_VECTOR
1196                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1197                    Point(625, 2),
1198                ),
1199                (
1200                    SimpleGlyphFlag::X_SHORT_VECTOR
1201                        | SimpleGlyphFlag::Y_SHORT_VECTOR
1202                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1203                    Point(756, -27),
1204                ),
1205                (
1206                    SimpleGlyphFlag::ON_CURVE_POINT
1207                        | SimpleGlyphFlag::X_SHORT_VECTOR
1208                        | SimpleGlyphFlag::Y_SHORT_VECTOR
1209                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1210                    Point(915, -31),
1211                ),
1212                (
1213                    SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
1214                    Point(891, -47),
1215                ),
1216                (
1217                    SimpleGlyphFlag::ON_CURVE_POINT
1218                        | SimpleGlyphFlag::X_SHORT_VECTOR
1219                        | SimpleGlyphFlag::Y_SHORT_VECTOR,
1220                    Point(862, -60),
1221                ),
1222                (
1223                    SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
1224                    Point(832, -73),
1225                ),
1226                (
1227                    SimpleGlyphFlag::ON_CURVE_POINT
1228                        | SimpleGlyphFlag::X_SHORT_VECTOR
1229                        | SimpleGlyphFlag::Y_SHORT_VECTOR,
1230                    Point(819, -103),
1231                ),
1232            ],
1233            phantom_points: None,
1234        }
1235    }
1236
1237    pub(super) fn composite_glyph_fixture(instructions: &'static [u8]) -> CompositeGlyph<'static> {
1238        CompositeGlyph {
1239            bounding_box: BoundingBox {
1240                x_min: 205,
1241                x_max: 4514,
1242                y_min: 0,
1243                y_max: 1434,
1244            },
1245            glyphs: vec![
1246                CompositeGlyphComponent {
1247                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1248                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1249                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
1250                        | CompositeGlyphFlag::MORE_COMPONENTS
1251                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
1252                    glyph_index: 5,
1253                    argument1: CompositeGlyphArgument::I16(3453),
1254                    argument2: CompositeGlyphArgument::I16(0),
1255                    scale: None,
1256                },
1257                CompositeGlyphComponent {
1258                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1259                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1260                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
1261                        | CompositeGlyphFlag::MORE_COMPONENTS
1262                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
1263                    glyph_index: 4,
1264                    argument1: CompositeGlyphArgument::I16(2773),
1265                    argument2: CompositeGlyphArgument::I16(0),
1266                    scale: None,
1267                },
1268                CompositeGlyphComponent {
1269                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1270                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1271                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
1272                        | CompositeGlyphFlag::MORE_COMPONENTS
1273                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
1274                    glyph_index: 3,
1275                    argument1: CompositeGlyphArgument::I16(1182),
1276                    argument2: CompositeGlyphArgument::I16(0),
1277                    scale: None,
1278                },
1279                CompositeGlyphComponent {
1280                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1281                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1282                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
1283                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET
1284                        | CompositeGlyphFlag::WE_HAVE_INSTRUCTIONS,
1285                    glyph_index: 2,
1286                    argument1: CompositeGlyphArgument::I16(205),
1287                    argument2: CompositeGlyphArgument::I16(0),
1288                    scale: None,
1289                },
1290            ],
1291            instructions,
1292            phantom_points: None,
1293        }
1294    }
1295
1296    #[test]
1297    fn test_point_bounding_box() {
1298        let points = [Point(1761, 565), Point(2007, 565), Point(1884, 1032)];
1299
1300        let expected = BoundingBox {
1301            x_min: 1761,
1302            y_min: 565,
1303            x_max: 2007,
1304            y_max: 1032,
1305        };
1306
1307        assert_eq!(BoundingBox::from_points(points.iter().copied()), expected);
1308    }
1309
1310    #[test]
1311    fn write_glyf_table_loca_sanity_check() {
1312        let glyf = GlyfTable {
1313            records: vec![GlyfRecord::empty(), GlyfRecord::empty()],
1314        };
1315        let num_glyphs = glyf.records.len();
1316        let mut buffer = WriteBuffer::new();
1317        let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
1318        assert_eq!(loca.offsets.len(), num_glyphs + 1);
1319    }
1320
1321    #[test]
1322    fn write_composite_glyf_instructions() {
1323        let glyph = Glyph::Composite(composite_glyph_fixture(&[1, 2, 3, 4]));
1324
1325        let mut buffer = WriteBuffer::new();
1326        Glyph::write(&mut buffer, glyph).unwrap();
1327
1328        // Read it back and check the instructions are intact
1329        match ReadScope::new(buffer.bytes()).read::<Glyph<'_>>() {
1330            Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
1331                assert_eq!(instructions, vec![1, 2, 3, 4].as_slice())
1332            }
1333            _ => panic!("did not read back expected instructions"),
1334        }
1335    }
1336
1337    #[test]
1338    fn read_glyph_offsets_correctly() {
1339        // Test for a bug in which only the length relative to current ReadCtxt offset was used
1340        // to read a glyph out of the `glyf` table. It should have been using `start` and `end`
1341        // offsets read from `loca`. The bug was discovered when reading the Baekmuk Batang font
1342        // in which the glyph data starts at offset 366.
1343        let glyph = simple_glyph_fixture();
1344
1345        // Write the glyph out
1346        let mut buffer = WriteBuffer::new();
1347        buffer.write_zeros(4).unwrap(); // Add some unused data at the start
1348        SimpleGlyph::write(&mut buffer, glyph).unwrap();
1349        let glyph_data = buffer.into_inner();
1350
1351        let mut buffer = WriteBuffer::new();
1352        let loca = owned::LocaTable {
1353            offsets: vec![4, 4, glyph_data.len() as u32 - 4],
1354        };
1355        owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
1356            .expect("unable to generate loca");
1357        let loca_data = buffer.into_inner();
1358
1359        // Parse and verify
1360        let num_glyphs = 2;
1361        let loca = ReadScope::new(&loca_data)
1362            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
1363            .expect("unable to read loca");
1364        let glyf = ReadScope::new(&glyph_data)
1365            .read_dep::<GlyfTable<'_>>(&loca)
1366            .expect("unable to read glyf");
1367        assert_eq!(glyf.records.len(), 2);
1368        assert_eq!(&glyf.records[0], &GlyfRecord::empty());
1369        let glyph = &glyf.records[1];
1370
1371        // Before the fix num_contours was read as 0
1372        assert_eq!(glyph.number_of_contours(), 1);
1373    }
1374
1375    // Regarding simple glyphs the OpenType spec says:
1376    // This is the table information needed if numberOfContours is greater than or equal to zero
1377    // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description
1378    //
1379    // We previously rejected glyphs with zero contours.
1380    #[test]
1381    fn simple_glyph_with_zero_contours() {
1382        let glyph_data = &[
1383            0, 0, 0, 0, 0, 0, 0, 0, // bounding box
1384            0, 0, // instruction length
1385        ];
1386        let expected = SimpleGlyph {
1387            bounding_box: BoundingBox::empty(),
1388            end_pts_of_contours: vec![],
1389            instructions: &[],
1390            coordinates: vec![],
1391            phantom_points: None,
1392        };
1393
1394        let glyph = ReadScope::new(glyph_data)
1395            .read_dep::<SimpleGlyph<'_>>(0)
1396            .unwrap();
1397        assert_eq!(glyph, expected);
1398    }
1399
1400    #[test]
1401    fn write_simple_glyph_with_zero_contours() {
1402        let glyph = SimpleGlyph {
1403            bounding_box: BoundingBox::empty(),
1404            end_pts_of_contours: vec![],
1405            instructions: &[],
1406            coordinates: vec![],
1407            phantom_points: None,
1408        };
1409
1410        let mut buffer = WriteBuffer::new();
1411        assert!(SimpleGlyph::write(&mut buffer, glyph).is_ok());
1412    }
1413
1414    #[test]
1415    fn read_glyph_with_incorrect_loca_length() {
1416        // Write the glyph out
1417        let glyph = simple_glyph_fixture();
1418        let mut buffer = WriteBuffer::new();
1419        Glyph::write(&mut buffer, Glyph::Simple(glyph)).unwrap();
1420        let glyph_data = buffer.into_inner();
1421
1422        let mut buffer = WriteBuffer::new();
1423        let loca = owned::LocaTable {
1424            offsets: vec![0, 0, glyph_data.len() as u32 + 1], // + 1 to go past end of glyf
1425        };
1426        owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
1427            .expect("unable to generate loca");
1428        let loca_data = buffer.into_inner();
1429
1430        // Parse and verify
1431        let num_glyphs = 2;
1432        let loca = ReadScope::new(&loca_data)
1433            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
1434            .expect("unable to read loca");
1435        assert!(ReadScope::new(&glyph_data)
1436            .read_dep::<GlyfTable<'_>>(&loca)
1437            .is_ok())
1438    }
1439
1440    // This is a test for a bug in which a composite glyph read with has_instructions = yes, but
1441    // instruction length 0 would be written without an instruction length field. This resulting
1442    // font was invalid as parsers would see the has_instructions flag and attempt to read the
1443    // non-existent instruction length.
1444    #[test]
1445    fn write_composite_glyph_with_empty_instructions() {
1446        let glyph = composite_glyph_fixture(&[]);
1447
1448        let mut buffer = WriteBuffer::new();
1449        Glyph::write(&mut buffer, Glyph::Composite(glyph)).unwrap();
1450
1451        // Ensure we can read it back. Before this fix this failed.
1452        match ReadScope::new(buffer.bytes()).read::<Glyph<'_>>() {
1453            Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
1454                assert_eq!(instructions, &[])
1455            }
1456            Ok(_) => panic!("did not read back expected glyph"),
1457            Err(_) => panic!("unable to read back glyph"),
1458        }
1459    }
1460
1461    #[test]
1462    fn test_number_of_points_empty() {
1463        let glyph = GlyfRecord::empty();
1464        assert_eq!(glyph.number_of_points().unwrap(), 0);
1465    }
1466
1467    #[test]
1468    fn test_number_of_points_simple_parsed() {
1469        let glyph = GlyfRecord::from(simple_glyph_fixture());
1470        assert_eq!(glyph.number_of_points().unwrap(), 9);
1471    }
1472
1473    #[test]
1474    fn test_number_of_points_simple_present() -> Result<(), ReadWriteError> {
1475        // Serialize
1476        let glyph = GlyfRecord::from(simple_glyph_fixture());
1477        let glyf = GlyfTable {
1478            records: vec![GlyfRecord::empty(), glyph],
1479        };
1480        let num_glyphs = glyf.records.len();
1481        let mut buffer = WriteBuffer::new();
1482        let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
1483        let mut loca_buffer = WriteBuffer::new();
1484        owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
1485        let loca_data = loca_buffer.into_inner();
1486        let loca = ReadScope::new(&loca_data)
1487            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
1488
1489        // Read back
1490        let glyf = ReadScope::new(&buffer.bytes())
1491            .read_dep::<GlyfTable<'_>>(&loca)
1492            .unwrap();
1493        let glyph = &glyf.records[1];
1494        assert!(matches!(glyph, GlyfRecord::Present { .. }));
1495        assert_eq!(glyph.number_of_points().unwrap(), 9);
1496        Ok(())
1497    }
1498
1499    #[test]
1500    fn test_number_of_points_composite_parsed() {
1501        // Test parsed
1502        let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
1503        assert_eq!(glyph.number_of_points().unwrap(), 4);
1504    }
1505
1506    #[test]
1507    fn test_number_of_points_composite_present() -> Result<(), ReadWriteError> {
1508        // Serialize
1509        let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
1510        let glyf = GlyfTable {
1511            records: vec![GlyfRecord::empty(), glyph],
1512        };
1513        let num_glyphs = glyf.records.len();
1514        let mut buffer = WriteBuffer::new();
1515        let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
1516        let mut loca_buffer = WriteBuffer::new();
1517        owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
1518        let loca_data = loca_buffer.into_inner();
1519        let loca = ReadScope::new(&loca_data)
1520            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
1521
1522        // Read back
1523        let glyf = ReadScope::new(&buffer.bytes())
1524            .read_dep::<GlyfTable<'_>>(&loca)
1525            .unwrap();
1526        let glyph = &glyf.records[1];
1527        assert!(matches!(glyph, GlyfRecord::Present { .. }));
1528        assert_eq!(glyph.number_of_points().unwrap(), 4);
1529        Ok(())
1530    }
1531}