1
#![deny(missing_docs)]
2

            
3
//! Parsing and writing of the `glyf` table.
4
//!
5
//! > This table contains information that describes the glyphs in the font in the TrueType outline
6
//! > format. Information regarding the rasterizer (scaler) refers to the TrueType rasterizer.
7
//!
8
//! — <https://docs.microsoft.com/en-us/typography/opentype/spec/glyf>
9

            
10
mod outline;
11
mod subset;
12
mod variation;
13

            
14
use std::mem;
15
use std::sync::Arc;
16

            
17
use log::warn;
18
use pathfinder_geometry::transform2d::Matrix2x2F;
19
use pathfinder_geometry::vector::Vector2F;
20
use rustc_hash::FxHashMap;
21

            
22
use crate::binary::read::{
23
    ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope, ReadUnchecked,
24
};
25
use crate::binary::write::{WriteBinary, WriteBinaryDep, WriteContext};
26
use crate::binary::{word_align, I16Be, U16Be, I8, U8};
27
use crate::error::{ParseError, WriteError};
28
use crate::tables::loca::{owned, LocaTable};
29
use crate::tables::os2::Os2;
30
use crate::tables::{
31
    read_and_box_table, F2Dot14, FontTableProvider, HeadTable, HheaTable, HmtxTable,
32
    IndexToLocFormat, MaxpTable,
33
};
34
use crate::{tag, SafeFrom};
35

            
36
pub use outline::{GlyfVisitorContext, VariableGlyfContext, VariableGlyfContextStore};
37
pub use subset::SubsetGlyph;
38

            
39
/// Recursion limit for nested composite glyphs
40
///
41
/// "There is no minimum nesting depth that must be supported" so we use the same value as Harfbuzz.
42
#[allow(unused)]
43
const COMPOSITE_GLYPH_RECURSION_LIMIT: u8 = 6;
44

            
45
/// Flags for [simple glyphs](https://learn.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description)
46
#[enumflags2::bitflags]
47
#[repr(u8)]
48
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
49
#[allow(non_camel_case_types)]
50
pub enum SimpleGlyphFlag {
51
    /// Bit 0: If set, the point is on the curve; otherwise, it is off the curve.
52
    ON_CURVE_POINT = 0b00000001,
53
    /// Bit 1: If set, the corresponding x-coordinate is 1 byte long, and the sign is determined
54
    /// by the `X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR` flag.
55
    X_SHORT_VECTOR = 0b00000010,
56
    /// Bit 2: If set, the corresponding y-coordinate is 1 byte long, and the sign is determined
57
    /// by the `Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR` flag.
58
    Y_SHORT_VECTOR = 0b00000100,
59
    /// Bit 3: If set, the next byte (or bytes) specifies the number of additional times this
60
    /// flag byte is to be repeated — the number of additional logical flags inserted after this flag.
61
    REPEAT_FLAG = 0b00001000,
62
    /// Bit 4: This flag has two meanings, depending on the `X_SHORT_VECTOR` flag.
63
    X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 0b00010000,
64
    /// Bit 5: This flag has two meanings, depending on the `Y_SHORT_VECTOR` flag.
65
    Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 0b00100000,
66
}
67

            
68
/// A set of `SimpleGlyphFlag` flags.
69
pub type SimpleGlyphFlags = enumflags2::BitFlags<SimpleGlyphFlag>;
70

            
71
/// Flags for [composite glyphs](https://learn.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description)
72
#[enumflags2::bitflags]
73
#[repr(u16)]
74
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
75
#[allow(non_camel_case_types)]
76
pub enum CompositeGlyphFlag {
77
    /// Bit 0: If this is set, the arguments are 16-bit (uint16 or int16); otherwise, they are
78
    /// bytes (uint8 or int8).
79
    ARG_1_AND_2_ARE_WORDS = 0x0001,
80
    /// Bit 1: If this is set, the arguments are signed xy values; otherwise, they are unsigned
81
    /// point numbers.
82
    ARGS_ARE_XY_VALUES = 0x0002,
83
    /// Bit 2: For the xy values if the preceding is true.
84
    ROUND_XY_TO_GRID = 0x0004,
85
    /// Bit 3: This indicates that there is a simple scale for the component.
86
    ///
87
    /// Otherwise, scale = 1.0.
88
    WE_HAVE_A_SCALE = 0x0008,
89
    /// Bit 4: Reserved, set to 0
90
    /// Bit 5: Indicates at least one more glyph after this one.
91
    MORE_COMPONENTS = 0x0020,
92
    /// Bit 6: The x direction will use a different scale from the y direction.
93
    WE_HAVE_AN_X_AND_Y_SCALE = 0x0040,
94
    /// Bit 7: There is a 2 by 2 transformation that will be used to scale the component.
95
    WE_HAVE_A_TWO_BY_TWO = 0x0080,
96
    /// Bit 8: Following the last component are instructions for the composite character.
97
    WE_HAVE_INSTRUCTIONS = 0x0100,
98
    /// Bit 9: If set, this forces the aw and lsb (and rsb) for the composite to be equal to
99
    /// those from this original glyph. This works for hinted and unhinted characters.
100
    USE_MY_METRICS = 0x0200,
101
    /// Bit 10: If set, the components of the compound glyph overlap.
102
    ///
103
    /// Use of this flag is not required in OpenType — that is, it is valid to have components
104
    /// overlap without having this flag set. It may affect behaviors in some platforms,
105
    /// however. (See Apple’s specification for details regarding behavior in Apple platforms.)
106
    /// When used, it must be set on the flag word for the first component. See additional
107
    /// remarks, above, for the similar OVERLAP_SIMPLE flag used in simple-glyph descriptions.
108
    OVERLAP_COMPOUND = 0x0400,
109
    /// Bit 11: The composite is designed to have the component offset scaled.
110
    SCALED_COMPONENT_OFFSET = 0x0800,
111
    /// Bit 12: The composite is designed not to have the component offset scaled.
112
    UNSCALED_COMPONENT_OFFSET = 0x1000,
113
    // 0xE010 	Reserved 	Bits 4, 13, 14 and 15 are reserved: set to 0.
114
}
115

            
116
/// A set of `CompositeGlyphFlag` flags.
117
pub type CompositeGlyphFlags = enumflags2::BitFlags<CompositeGlyphFlag>;
118

            
119
/// `glyf` table
120
///
121
/// This table contains glyph outlines. Functionality is provided for reading glyphs,
122
/// serializing to a `glyf` table, and subsetting.
123
///
124
/// **See also:** [LocaGlyf].<br>
125
/// **Reference:** <https://docs.microsoft.com/en-us/typography/opentype/spec/glyf>
126
#[derive(Debug, PartialEq)]
127
pub struct GlyfTable<'a> {
128
    records: Vec<GlyfRecord<'a>>,
129
}
130

            
131
/// Alternate representation of `glyf` table
132
///
133
/// This is an alternate structure for the `glyf` table that combines `glyf` and
134
/// `loca` data together. This makes it easier to access glyphs. `LocaGlyph` also
135
/// contains a glyph cache so repeated calls to [glyph][Self::glyph] will only
136
/// fetch and parse the glyph once.
137
///
138
/// `LocaGlyf` also implements [OutlineBuilder][crate::outline::OutlineBuilder],
139
/// which allows the outline of the glyph to be visited.
140
///
141
/// **Reference:** <https://docs.microsoft.com/en-us/typography/opentype/spec/glyf>
142
pub struct LocaGlyf {
143
    /// Flag that indicates whether this structure has been loaded.
144
    ///
145
    /// This allows and empty version of the table to be created, removing the need
146
    /// to wrap an unloaded table in Option or similar.
147
    loaded: bool,
148
    /// Data from `loca` table.
149
    loca: owned::LocaTable,
150
    /// Raw `glyf` table data (owned copy, or a zero-copy view into shared
151
    /// already-resident font bytes — see [`GlyfBytes`]).
152
    glyf: GlyfBytes,
153
    /// Cache of parsed glyphs indexed by glyph ID.
154
    cache: FxHashMap<u16, Arc<Glyph>>,
155
}
156

            
157
/// Backing storage for the `glyf` table.
158
///
159
/// The classic path copies the whole `glyf` table onto the heap
160
/// ([`GlyfBytes::Owned`]) — for a large font (e.g. a CJK or macOS system
161
/// `.ttc`) that is a ~20-40 MB allocation kept for the font's lifetime, even
162
/// though the source bytes are usually already resident (mmap'd) elsewhere.
163
/// [`GlyfBytes::Shared`] instead keeps an `Arc` to those source bytes plus the
164
/// `glyf` table's `(offset, len)`, so no copy is made. See
165
/// [`LocaGlyf::load_shared`].
166
pub enum GlyfBytes {
167
    /// Owned heap copy of the `glyf` table.
168
    Owned(Box<[u8]>),
169
    /// Zero-copy view: `owner.as_ref()[offset..offset + len]` is the `glyf`
170
    /// table. `owner` keeps the whole font buffer alive.
171
    Shared {
172
        /// The font buffer the `glyf` table lives inside; kept alive so the
173
        /// view stays valid.
174
        owner: Arc<dyn AsRef<[u8]> + Send + Sync>,
175
        /// Byte offset of the `glyf` table within `owner`.
176
        offset: usize,
177
        /// Length of the `glyf` table in bytes.
178
        len: usize,
179
    },
180
}
181

            
182
impl GlyfBytes {
183
    /// The `glyf` table bytes.
184
    #[inline]
185
143640
    pub fn as_bytes(&self) -> &[u8] {
186
143640
        match self {
187
26568
            GlyfBytes::Owned(b) => b,
188
117072
            GlyfBytes::Shared { owner, offset, len } => {
189
                // Bounds were validated at construction (`load_shared`); clamp
190
                // defensively so a bad range degrades to a short read (parse
191
                // error) rather than a panic.
192
117072
                let all = (**owner).as_ref();
193
117072
                let end = offset.saturating_add(*len).min(all.len());
194
117072
                all.get(*offset..end).unwrap_or(&[])
195
            }
196
        }
197
143640
    }
198
}
199

            
200
impl From<Box<[u8]>> for GlyfBytes {
201
    fn from(b: Box<[u8]>) -> Self {
202
        GlyfBytes::Owned(b)
203
    }
204
}
205

            
206
/// If `table` is a sub-slice that lives inside `owner`'s allocation, return its
207
/// `(offset, len)` within `owner`; otherwise `None` (the provider returned an
208
/// owned/relocated copy, so a zero-copy view is impossible). Uses address-range
209
/// containment — valid because a `Cow::Borrowed` from a table provider points
210
/// directly into the font buffer `owner` wraps.
211
6750
fn glyf_within(table: &[u8], owner: &[u8]) -> Option<(usize, usize)> {
212
6750
    let (base, obytes) = (owner.as_ptr() as usize, owner.len());
213
6750
    let (tstart, tlen) = (table.as_ptr() as usize, table.len());
214
6750
    let offset = tstart.checked_sub(base)?;
215
    // Must lie fully within owner. (A zero-length table has no address to
216
    // anchor and isn't worth sharing — fall back to Owned.)
217
6750
    if tlen == 0 || offset.checked_add(tlen)? > obytes {
218
        return None;
219
6750
    }
220
6750
    Some((offset, tlen))
221
6750
}
222

            
223
/// A record from the `glyf` table that maybe parsed
224
#[derive(Debug, PartialEq, Clone)]
225
pub enum GlyfRecord<'a> {
226
    /// An unparsed glyph
227
    Present {
228
        /// The number of contours of this glyph
229
        ///
230
        /// - Zero for empty glyphs
231
        /// - Negative for composite glyphs
232
        number_of_contours: i16,
233
        /// A scope for parsing the glyph
234
        scope: ReadScope<'a>,
235
    },
236
    /// A parsed glyph
237
    Parsed(Glyph),
238
}
239

            
240
/// Storage for [phantom points](https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructing_glyphs#phantoms)
241
pub type PhantomPoints = [Point; 4];
242

            
243
/// A single glyph
244
#[derive(Debug, PartialEq, Clone)]
245
pub enum Glyph {
246
    /// A glyph with no outline
247
    Empty(EmptyGlyph),
248
    /// A glyph with an outline
249
    Simple(SimpleGlyph),
250
    /// A glyph composed of other glyphs
251
    Composite(CompositeGlyph),
252
}
253

            
254
/// A glyph with no outline
255
#[derive(Debug, PartialEq, Clone)]
256
pub struct EmptyGlyph {
257
    /// The [phantom points](https://learn.microsoft.com/en-us/typography/opentype/spec/tt_instructing_glyphs#phantoms) of this glyph
258
    pub phantom_points: Option<PhantomPoints>,
259
}
260

            
261
/// A glyph with an outline
262
#[derive(Debug, PartialEq, Clone)]
263
pub struct SimpleGlyph {
264
    /// The bounding box of this glyph
265
    pub bounding_box: BoundingBox,
266
    /// The end points of the contours of this glyph
267
    ///
268
    /// Array of point indices for the last point of each contour, in increasing numeric order.
269
    pub end_pts_of_contours: Vec<u16>,
270
    /// Hinting instruction byte code
271
    pub instructions: Box<[u8]>,
272
    /// Contour point coordinates
273
    pub coordinates: Vec<(SimpleGlyphFlags, Point)>,
274
    /// Phantom points, only populated when applying glyph variation deltas
275
    pub phantom_points: Option<Box<PhantomPoints>>,
276
}
277

            
278
/// A glyph composed from other glyphs
279
#[derive(Debug, PartialEq, Clone)]
280
pub struct CompositeGlyph {
281
    /// The bounding box of this glyph
282
    pub bounding_box: BoundingBox,
283
    /// The glyph components
284
    pub glyphs: Vec<CompositeGlyphComponent>,
285
    /// Hinting instruction byte code
286
    pub instructions: Box<[u8]>,
287
    /// Phantom points, only populated when applying glyph variation deltas
288
    pub phantom_points: Option<Box<PhantomPoints>>,
289
}
290

            
291
/// A component of a [CompositeGlyph]
292
#[derive(Debug, PartialEq, Clone)]
293
pub struct CompositeGlyphComponent {
294
    /// Flags for this component
295
    pub flags: CompositeGlyphFlags,
296
    /// The index of the child glyph for this component
297
    pub glyph_index: u16,
298
    /// First argument
299
    ///
300
    /// Meaning depends on `flags`
301
    pub argument1: CompositeGlyphArgument,
302
    /// Second argument
303
    ///
304
    /// Meaning depends on `flags`
305
    pub argument2: CompositeGlyphArgument,
306
    /// Optional scale applied to this child component
307
    pub scale: Option<CompositeGlyphScale>,
308
}
309

            
310
/// Variable size composite glyph argument
311
#[allow(missing_docs)]
312
#[derive(Debug, PartialEq, Copy, Clone)]
313
pub enum CompositeGlyphArgument {
314
    U8(u8),
315
    I8(i8),
316
    U16(u16),
317
    I16(i16),
318
}
319

            
320
/// A scale applied to a [CompositeGlyphComponent]
321
#[derive(Debug, PartialEq, Copy, Clone)]
322
pub enum CompositeGlyphScale {
323
    /// Simple scale for the component
324
    Scale(F2Dot14),
325
    /// Separate X and Y scales
326
    XY {
327
        /// The X scale
328
        x_scale: F2Dot14,
329
        /// The Y scale
330
        y_scale: F2Dot14,
331
    },
332
    /// A 2 by 2 transformation that will be used to scale the component
333
    Matrix([[F2Dot14; 2]; 2]),
334
}
335

            
336
/// Wrapper for composite glyph components
337
pub struct CompositeGlyphs {
338
    /// The child components
339
    pub glyphs: Vec<CompositeGlyphComponent>,
340
    /// Flag indicating if there are hinting instructions for this glyph
341
    pub have_instructions: bool,
342
}
343

            
344
/// An (x, y) point
345
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
346
pub struct Point(pub i16, pub i16);
347

            
348
/// Glyph bounding box
349
#[derive(Debug, PartialEq, Copy, Clone)]
350
pub struct BoundingBox {
351
    /// X minimum
352
    pub x_min: i16,
353
    /// X maximum
354
    pub x_max: i16,
355
    /// Y minimum
356
    pub y_min: i16,
357
    /// Y maximum
358
    pub y_max: i16,
359
}
360

            
361
impl ReadBinaryDep for GlyfTable<'_> {
362
    type Args<'a> = &'a LocaTable<'a>;
363
    type HostType<'a> = GlyfTable<'a>;
364

            
365
    fn read_dep<'a>(
366
        ctxt: &mut ReadCtxt<'a>,
367
        loca: Self::Args<'a>,
368
    ) -> Result<Self::HostType<'a>, ParseError> {
369
        if loca.offsets.len() < 2 {
370
            return Err(ParseError::BadIndex);
371
        }
372

            
373
        let glyph_records = (0..loca.offsets.len() - 1)
374
            .map(|i| {
375
                // NOTE(unwrap): Bounded by `loca.offsets.len() - 1`, both
376
                // indices are guaranteed in range.
377
                let start = loca.offsets.get(i).unwrap();
378
                let end = loca.offsets.get(i + 1).unwrap();
379
                (start, end)
380
            })
381
            .map(|(start, end)| match end.checked_sub(start) {
382
                Some(0) => Ok(GlyfRecord::empty()),
383
                Some(length) => {
384
                    let offset = usize::try_from(start)?;
385
                    let glyph_scope = ctxt.scope().offset_length(offset, usize::try_from(length)?);
386
                    match glyph_scope {
387
                        Ok(scope) => {
388
                            let number_of_contours = scope.read::<I16Be>()?;
389
                            Ok(GlyfRecord::Present {
390
                                number_of_contours,
391
                                scope,
392
                            })
393
                        }
394
                        Err(ParseError::BadEof) => {
395
                            // The length specified by `loca` is beyond the end of the `glyf`
396
                            // table. Try parsing the glyph without a length limit to see if it's
397
                            // valid. This is a workaround for a font where the last `loca` offset
398
                            // was incorrectly 1 byte beyond the end of the `glyf` table but the
399
                            // actual glyph data was valid.
400
                            warn!("glyph length out of bounds, trying to parse");
401
                            let scope = ctxt.scope().offset(offset);
402
                            scope.read::<Glyph>().map(GlyfRecord::Parsed)
403
                        }
404
                        Err(err) => Err(err),
405
                    }
406
                }
407
                None => Err(ParseError::BadOffset),
408
            })
409
            .collect::<Result<Vec<_>, _>>()?;
410

            
411
        Ok(GlyfTable {
412
            records: glyph_records,
413
        })
414
    }
415
}
416

            
417
impl<'a> WriteBinaryDep<Self> for GlyfTable<'a> {
418
    type Output = owned::LocaTable;
419
    type Args = IndexToLocFormat;
420

            
421
    /// Write this glyf table into `ctxt`.
422
    ///
423
    /// ## A Note About Padding
424
    ///
425
    /// On the [loca table documentation](https://docs.microsoft.com/en-us/typography/opentype/spec/loca#long-version)
426
    /// at the bottom it states:
427
    ///
428
    /// > Note that the local offsets should be 32-bit aligned. Offsets which are not 32-bit
429
    /// > aligned may seriously degrade performance of some processors.
430
    ///
431
    /// On the [Recommendations for OpenType Fonts](https://docs.microsoft.com/en-us/typography/opentype/spec/recom#loca-table)
432
    /// page it states:
433
    ///
434
    /// > We recommend that local offsets should be 16-bit aligned, in both the short and long
435
    /// > formats of this table.
436
    ///
437
    /// On [Apple's loca documentation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6loca.html)
438
    /// it says:
439
    ///
440
    /// > The glyph data is always word aligned.
441
    ///
442
    /// Elsewhere in the Apple docs they refer to long as 32-bits, so assuming word here means
443
    /// 16-bits.
444
    ///
445
    /// [An issue](https://github.com/MicrosoftDocs/typography-issues/issues/241) was raised against
446
    /// Microsoft's docs regarding this.
447
    /// Behdad Esfahbod [commented](https://github.com/MicrosoftDocs/typography-issues/issues/241#issuecomment-495265379):
448
    ///
449
    /// > All the requirements should be removed since 2019.
450
    /// >
451
    /// > In reality, in the short format, you are forced to do 16-bit alignment because of how
452
    /// > offsets are stored. In the long format, use alignment 1. We've been doing that in
453
    /// > fonttools for years and never ever heard a complaint whatsoever.
454
    ///
455
    /// So with this in mind we implement 16-bit alignment when `index_to_loc_format` is 0,
456
    /// and no alignment/padding otherwise.
457
    fn write_dep<C: WriteContext>(
458
        ctxt: &mut C,
459
        table: GlyfTable<'a>,
460
        index_to_loc_format: IndexToLocFormat,
461
    ) -> Result<Self::Output, WriteError> {
462
        let mut offsets: Vec<u32> = Vec::with_capacity(table.records.len() + 1);
463

            
464
        let start = ctxt.bytes_written();
465
        for record in table.records {
466
            let offset = ctxt.bytes_written();
467

            
468
            offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
469

            
470
            match record {
471
                GlyfRecord::Present { scope, .. } => ReadScope::write(ctxt, scope)?,
472
                GlyfRecord::Parsed(glyph) => Glyph::write(ctxt, glyph)?,
473
            }
474

            
475
            if index_to_loc_format == IndexToLocFormat::Short {
476
                let length = ctxt.bytes_written() - offset;
477
                let padded_length = word_align(length);
478
                ctxt.write_zeros(padded_length - length)?;
479
            }
480
        }
481

            
482
        // Add the final loca entry
483
        offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
484

            
485
        Ok(owned::LocaTable { offsets })
486
    }
487
}
488

            
489
impl Glyph {
490
    /// Construct a new empty glyph
491
5778
    pub fn empty() -> Glyph {
492
5778
        Glyph::Empty(EmptyGlyph::new())
493
5778
    }
494

            
495
    /// The number of contours of this glyph
496
    ///
497
    /// - Zero for empty glyphs
498
    /// - Negative for composite glyphs
499
    pub fn number_of_contours(&self) -> i16 {
500
        match self {
501
            Glyph::Empty(_) => 0,
502
            Glyph::Simple(simple) => simple.number_of_contours(),
503
            Glyph::Composite(_) => -1,
504
        }
505
    }
506

            
507
    /// Returns the bounding box of the glyph.
508
    ///
509
    /// Returns `None` if the glyph is an [EmptyGlyph].
510
    pub fn bounding_box(&self) -> Option<BoundingBox> {
511
        match self {
512
            Glyph::Empty(_) => None,
513
            Glyph::Simple(simple) => Some(simple.bounding_box),
514
            Glyph::Composite(composite) => Some(composite.bounding_box),
515
        }
516
    }
517

            
518
    /// Returns the phantom points of the glyph.
519
    ///
520
    /// Returns `None` if the phantom points have not been assigned through glyph variation.
521
    pub fn phantom_points(&self) -> Option<PhantomPoints> {
522
        match self {
523
            Glyph::Empty(empty) => empty.phantom_points,
524
            Glyph::Simple(SimpleGlyph { phantom_points, .. })
525
            | Glyph::Composite(CompositeGlyph { phantom_points, .. }) => {
526
                phantom_points.as_deref().copied()
527
            }
528
        }
529
    }
530

            
531
    /// The number of delta adjustable points in this glyph excluding phantom points.
532
    fn number_of_points(&self) -> Result<u16, ParseError> {
533
        match self {
534
            Glyph::Empty(_) => Ok(0),
535
            Glyph::Simple(glyph) => Ok(glyph.coordinates.len().try_into()?),
536
            Glyph::Composite(composite) => Ok(composite.glyphs.len().try_into()?),
537
        }
538
    }
539
}
540

            
541
/// Calculate the phantom points from the glyph.
542
///
543
/// Requires that the bounding box of the glyph is accurate/up-to-date.
544
pub(crate) fn calculate_phantom_points(
545
    glyph_id: u16,
546
    bounding_box: Option<BoundingBox>,
547
    hmtx: &HmtxTable<'_>,
548
    vmtx: Option<&HmtxTable<'_>>,
549
    os2: Option<&Os2>,
550
    hhea: &HheaTable,
551
) -> Result<[Point; 4], ParseError> {
552
    // In a font with TrueType outlines, xMin and xMax values for each glyph are given in the
553
    // 'glyf' table. The advance width (“aw”) and left side bearing (“lsb”) can be derived from
554
    // the glyph “phantom points”.
555
    //
556
    // If pp1 and pp2 are TrueType phantom points used to control lsb and rsb, their initial
557
    // position in the X-direction is calculated as follows:
558
    //
559
    // pp1 = xMin - lsb
560
    // pp2 = pp1 + aw
561
    //
562
    // If a glyph has no contours, xMax/xMin are not defined. The left side bearing indicated
563
    // in the 'hmtx' table for such glyphs should be zero.
564
    //
565
    // https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx
566
    //
567
    // See also notes in FreeType:
568
    // https://gitlab.freedesktop.org/freetype/freetype/-/blob/7d45cf2c8f219263c5b9d84763a9a101138b0ed1/src/truetype/ttgload.c#L1280-1363
569
    let horizonal_metrics = hmtx.metric(glyph_id)?;
570
    let x_min = bounding_box.map(|bbox| bbox.x_min).unwrap_or(0);
571
    let y_max = bounding_box.map(|bbox| bbox.y_max).unwrap_or(0);
572
    let pp1 = Point(x_min - horizonal_metrics.lsb, 0);
573
    let pp2 = Point(pp1.0 + i16::try_from(horizonal_metrics.advance_width)?, 0);
574

            
575
    let (advance_height, tsb) = match vmtx {
576
        Some(vmtx) => vmtx.metric(glyph_id).and_then(|metric| {
577
            i16::try_from(metric.advance_width)
578
                .map(|aw| (aw, metric.lsb))
579
                .map_err(|_| ParseError::LimitExceeded)
580
        })?,
581
        // Fall back on OS/2 table if vmtx table is not present
582
        None => {
583
            let (default_ascender, default_descender) =
584
                match os2.and_then(|os2| os2.version0.as_ref()) {
585
                    Some(os2) => (os2.s_typo_ascender, os2.s_typo_descender),
586
                    None => (hhea.ascender, hhea.descender),
587
                };
588
            let advance_height = default_ascender - default_descender;
589
            let tsb = default_ascender - y_max;
590
            (advance_height, tsb)
591
        }
592
    };
593

            
594
    let x = 0;
595
    let pp3 = Point(x, y_max + tsb);
596
    let pp4 = Point(x, pp3.1 - advance_height);
597

            
598
    Ok([pp1, pp2, pp3, pp4])
599
}
600

            
601
impl ReadBinary for Glyph {
602
    type HostType<'a> = Glyph;
603

            
604
66042
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
605
66042
        let number_of_contours = ctxt.read_i16be()?;
606

            
607
66042
        if number_of_contours >= 0 {
608
            // Simple glyph
609
            // Cast is safe as we've checked value is positive above
610
65772
            let glyph = ctxt.read_dep::<SimpleGlyph>(number_of_contours as u16)?;
611
65772
            Ok(Glyph::Simple(glyph))
612
        } else {
613
            // Composite glyph
614
270
            let glyph = ctxt.read::<CompositeGlyph>()?;
615
270
            Ok(Glyph::Composite(glyph))
616
        }
617
66042
    }
618
}
619

            
620
impl WriteBinary for Glyph {
621
    type Output = ();
622

            
623
    fn write<C: WriteContext>(ctxt: &mut C, glyph: Glyph) -> Result<(), WriteError> {
624
        match glyph {
625
            Glyph::Empty(_) => Ok(()),
626
            Glyph::Simple(simple_glyph) => SimpleGlyph::write(ctxt, simple_glyph),
627
            Glyph::Composite(composite) => CompositeGlyph::write(ctxt, composite),
628
        }
629
    }
630
}
631

            
632
impl SimpleGlyph {
633
    /// The number of contours in this glyph
634
    pub fn number_of_contours(&self) -> i16 {
635
        // TODO: Revisit this to see how we might enforce its validity
636
        // In theory there could be more than i16::MAX items in end_pts_of_contours
637
        self.end_pts_of_contours.len() as i16
638
    }
639

            
640
    /// Iterator over the contours of this glyph
641
65880
    pub fn contours(&self) -> impl Iterator<Item = &[(SimpleGlyphFlags, Point)]> {
642
86126
        self.end_pts_of_contours.iter().scan(0, move |i, &end| {
643
84925
            let start = *i;
644
84925
            let end = usize::from(end);
645
84925
            *i = end + 1;
646
84925
            self.coordinates.get(start..=end)
647
84925
        })
648
65880
    }
649

            
650
    /// The bounding box of this glyph
651
    pub fn bounding_box(&self) -> BoundingBox {
652
        BoundingBox::from_points(self.coordinates.iter().copied().map(|(_flag, point)| point))
653
    }
654
}
655

            
656
impl ReadBinaryDep for SimpleGlyph {
657
    type Args<'a> = u16;
658
    type HostType<'a> = SimpleGlyph;
659

            
660
65772
    fn read_dep<'a>(
661
65772
        ctxt: &mut ReadCtxt<'a>,
662
65772
        number_of_contours: Self::Args<'_>,
663
65772
    ) -> Result<Self::HostType<'a>, ParseError> {
664
65772
        let number_of_contours = usize::from(number_of_contours);
665
65772
        let bounding_box = ctxt.read::<BoundingBox>()?;
666
65772
        let end_pts_of_contours = ctxt.read_array::<U16Be>(number_of_contours)?.to_vec();
667
65772
        let instruction_length = ctxt.read::<U16Be>()?;
668
65772
        let instructions = ctxt.read_slice(usize::from(instruction_length))?;
669
        // end_pts_of_contours stores the index of the end points.
670
        // Therefore the number of coordinates is the last index + 1
671
65772
        let number_of_coordinates = end_pts_of_contours
672
65772
            .last()
673
65772
            .map_or(0, |&last| usize::from(last) + 1);
674

            
675
        // Read all the flags
676
65772
        let mut coordinates = Vec::with_capacity(number_of_coordinates);
677
1358100
        while coordinates.len() < number_of_coordinates {
678
1292328
            let flag = ctxt.read::<SimpleGlyphFlags>()?;
679
1292328
            if flag.is_repeated() {
680
272160
                let count = usize::from(ctxt.read::<U8>()?) + 1; // + 1 to include the current entry
681
272160
                let repeat = std::iter::repeat_n((flag, Point::zero()), count);
682
272160
                coordinates.extend(repeat)
683
1020168
            } else {
684
1020168
                coordinates.push((flag, Point::zero()));
685
1020168
            }
686
        }
687

            
688
        // Read the x coordinates
689
1751598
        for (flag, Point(x, _y)) in coordinates.iter_mut() {
690
1751598
            *x = if flag.x_is_short() {
691
1299240
                ctxt.read::<U8>()
692
1299240
                    .map(|val| i16::from(val) * flag.x_short_sign())?
693
452358
            } else if flag.x_is_same_or_positive() {
694
287118
                0
695
            } else {
696
165240
                ctxt.read::<I16Be>()?
697
            }
698
        }
699

            
700
        // Read y coordinates, updating the Points in `coordinates`
701
65772
        let mut prev_point = Point::zero();
702
1751598
        for (flag, point) in coordinates.iter_mut() {
703
1751598
            let y = if flag.y_is_short() {
704
1103112
                ctxt.read::<U8>()
705
1103112
                    .map(|val| i16::from(val) * flag.y_short_sign())?
706
648486
            } else if flag.y_is_same_or_positive() {
707
422550
                0
708
            } else {
709
225936
                ctxt.read::<I16Be>()?
710
            };
711

            
712
            // The x and y coordinates are stored as deltas against the previous point, with the
713
            // first one being implicitly against (0, 0). Here we resolve these deltas into
714
            // absolute (x, y) values.
715
1751598
            prev_point = Point(prev_point.0 + point.0, prev_point.1 + y);
716
1751598
            *point = prev_point
717
        }
718

            
719
65772
        Ok(SimpleGlyph {
720
65772
            bounding_box,
721
65772
            end_pts_of_contours,
722
65772
            instructions: Box::from(instructions),
723
65772
            coordinates,
724
65772
            phantom_points: None,
725
65772
        })
726
65772
    }
727
}
728

            
729
impl WriteBinary for SimpleGlyph {
730
    type Output = ();
731

            
732
    fn write<C: WriteContext>(ctxt: &mut C, glyph: SimpleGlyph) -> Result<(), WriteError> {
733
        I16Be::write(ctxt, glyph.number_of_contours())?;
734
        BoundingBox::write(ctxt, glyph.bounding_box)?;
735
        ctxt.write_vec::<U16Be, _>(glyph.end_pts_of_contours)?;
736
        U16Be::write(ctxt, u16::try_from(glyph.instructions.len())?)?;
737
        ctxt.write_bytes(&glyph.instructions)?;
738

            
739
        // Flags and coordinates are written without any attempt to compact them using
740
        // smaller representation, use of REPEAT, or X/Y_IS_SAME.
741
        // TODO: try to compact the values written
742

            
743
        // flags
744
        let mask = SimpleGlyphFlag::ON_CURVE_POINT; // ON_CURVE_POINT is the only flag that needs to carry through
745
        for flag in glyph.coordinates.iter().map(|(flag, _)| *flag) {
746
            U8::write(ctxt, (flag & mask).bits())?;
747
        }
748

            
749
        // x coordinates
750
        let mut prev_x = 0;
751
        for (_, Point(x, _)) in &glyph.coordinates {
752
            let delta_x = x - prev_x;
753
            I16Be::write(ctxt, delta_x)?;
754
            prev_x = *x;
755
        }
756

            
757
        // y coordinates
758
        let mut prev_y = 0;
759
        for (_, Point(_, y)) in &glyph.coordinates {
760
            let delta_y = y - prev_y;
761
            I16Be::write(ctxt, delta_y)?;
762
            prev_y = *y;
763
        }
764

            
765
        Ok(())
766
    }
767
}
768

            
769
impl ReadFrom for SimpleGlyphFlags {
770
    type ReadType = U8;
771

            
772
1292328
    fn read_from(flag: u8) -> Self {
773
1292328
        SimpleGlyphFlags::from_bits_truncate(flag)
774
1292328
    }
775
}
776

            
777
impl ReadBinary for CompositeGlyphs {
778
    type HostType<'a> = Self;
779

            
780
270
    fn read(ctxt: &mut ReadCtxt<'_>) -> Result<Self, ParseError> {
781
270
        let mut have_instructions = false;
782
270
        let mut glyphs = Vec::new();
783
        loop {
784
810
            let flags = ctxt.read::<CompositeGlyphFlags>()?;
785
810
            let data = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
786

            
787
810
            if flags.we_have_instructions() {
788
270
                have_instructions = true;
789
540
            }
790

            
791
810
            glyphs.push(data);
792

            
793
810
            if !flags.more_components() {
794
270
                break;
795
540
            }
796
        }
797

            
798
270
        Ok(CompositeGlyphs {
799
270
            glyphs,
800
270
            have_instructions,
801
270
        })
802
270
    }
803
}
804

            
805
impl ReadBinary for CompositeGlyph {
806
    type HostType<'a> = CompositeGlyph;
807

            
808
270
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
809
270
        let bounding_box = ctxt.read::<BoundingBox>()?;
810
270
        let glyphs = ctxt.read::<CompositeGlyphs>()?;
811

            
812
270
        let instruction_length = if glyphs.have_instructions {
813
270
            usize::from(ctxt.read::<U16Be>()?)
814
        } else {
815
            0
816
        };
817
270
        let instructions = ctxt.read_slice(instruction_length)?;
818

            
819
270
        Ok(CompositeGlyph {
820
270
            bounding_box,
821
270
            glyphs: glyphs.glyphs,
822
270
            instructions: Box::from(instructions),
823
270
            phantom_points: None,
824
270
        })
825
270
    }
826
}
827

            
828
impl WriteBinary for CompositeGlyph {
829
    type Output = ();
830

            
831
    fn write<C: WriteContext>(ctxt: &mut C, composite: Self) -> Result<Self::Output, WriteError> {
832
        I16Be::write(ctxt, -1_i16)?; // number_of_contours
833
        BoundingBox::write(ctxt, composite.bounding_box)?;
834
        let mut has_instructions = false;
835
        for glyph in composite.glyphs {
836
            has_instructions |= glyph.flags.we_have_instructions();
837
            CompositeGlyphComponent::write(ctxt, glyph)?;
838
        }
839
        if has_instructions {
840
            U16Be::write(ctxt, u16::try_from(composite.instructions.len())?)?;
841
            ctxt.write_bytes(&composite.instructions)?;
842
        }
843
        Ok(())
844
    }
845
}
846

            
847
#[allow(missing_docs)]
848
pub trait SimpleGlyphFlagExt {
849
    fn is_on_curve(self) -> bool;
850
    fn x_is_short(self) -> bool;
851
    fn y_is_short(self) -> bool;
852
    fn is_repeated(self) -> bool;
853
    fn x_short_sign(self) -> i16;
854
    fn y_short_sign(self) -> i16;
855
    fn x_is_same_or_positive(self) -> bool;
856
    fn y_is_same_or_positive(self) -> bool;
857
}
858

            
859
impl SimpleGlyphFlagExt for SimpleGlyphFlags {
860
4753242
    fn is_on_curve(self) -> bool {
861
4753242
        self.contains(SimpleGlyphFlag::ON_CURVE_POINT)
862
4753242
    }
863

            
864
1751598
    fn x_is_short(self) -> bool {
865
1751598
        self.contains(SimpleGlyphFlag::X_SHORT_VECTOR)
866
1751598
    }
867

            
868
1751598
    fn y_is_short(self) -> bool {
869
1751598
        self.contains(SimpleGlyphFlag::Y_SHORT_VECTOR)
870
1751598
    }
871

            
872
1292328
    fn is_repeated(self) -> bool {
873
1292328
        self.contains(SimpleGlyphFlag::REPEAT_FLAG)
874
1292328
    }
875

            
876
1299240
    fn x_short_sign(self) -> i16 {
877
1299240
        if self.x_is_same_or_positive() {
878
685044
            1
879
        } else {
880
614196
            -1
881
        }
882
1299240
    }
883

            
884
1103112
    fn y_short_sign(self) -> i16 {
885
1103112
        if self.y_is_same_or_positive() {
886
536976
            1
887
        } else {
888
566136
            -1
889
        }
890
1103112
    }
891

            
892
1751598
    fn x_is_same_or_positive(self) -> bool {
893
1751598
        self.contains(SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)
894
1751598
    }
895

            
896
1751598
    fn y_is_same_or_positive(self) -> bool {
897
1751598
        self.contains(SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)
898
1751598
    }
899
}
900

            
901
impl ReadFrom for CompositeGlyphFlags {
902
    type ReadType = U16Be;
903

            
904
810
    fn read_from(flag: u16) -> Self {
905
810
        CompositeGlyphFlags::from_bits_truncate(flag)
906
810
    }
907
}
908

            
909
impl ReadBinaryDep for CompositeGlyphArgument {
910
    type Args<'a> = CompositeGlyphFlags;
911
    type HostType<'a> = Self;
912

            
913
1620
    fn read_dep(ctxt: &mut ReadCtxt<'_>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
914
1620
        let arg = match (flags.arg_1_and_2_are_words(), flags.args_are_xy_values()) {
915
540
            (true, true) => CompositeGlyphArgument::I16(ctxt.read_i16be()?),
916
            (true, false) => CompositeGlyphArgument::U16(ctxt.read_u16be()?),
917
1080
            (false, true) => CompositeGlyphArgument::I8(ctxt.read_i8()?),
918
            (false, false) => CompositeGlyphArgument::U8(ctxt.read_u8()?),
919
        };
920

            
921
1620
        Ok(arg)
922
1620
    }
923
}
924

            
925
impl WriteBinary for CompositeGlyphArgument {
926
    type Output = ();
927

            
928
    fn write<C: WriteContext>(ctxt: &mut C, arg: CompositeGlyphArgument) -> Result<(), WriteError> {
929
        match arg {
930
            CompositeGlyphArgument::U8(val) => U8::write(ctxt, val),
931
            CompositeGlyphArgument::I8(val) => I8::write(ctxt, val),
932
            CompositeGlyphArgument::U16(val) => U16Be::write(ctxt, val),
933
            CompositeGlyphArgument::I16(val) => I16Be::write(ctxt, val),
934
        }
935
    }
936
}
937

            
938
impl ReadBinaryDep for CompositeGlyphComponent {
939
    type Args<'a> = CompositeGlyphFlags;
940
    type HostType<'a> = Self;
941

            
942
810
    fn read_dep(ctxt: &mut ReadCtxt<'_>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
943
810
        let glyph_index = ctxt.read_u16be()?;
944
810
        let argument1 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
945
810
        let argument2 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
946

            
947
810
        let scale = if flags.we_have_a_scale() {
948
            Some(CompositeGlyphScale::Scale(ctxt.read::<F2Dot14>()?))
949
810
        } else if flags.we_have_an_x_and_y_scale() {
950
            Some(CompositeGlyphScale::XY {
951
                x_scale: ctxt.read::<F2Dot14>()?,
952
                y_scale: ctxt.read::<F2Dot14>()?,
953
            })
954
810
        } else if flags.we_have_a_two_by_two() {
955
            Some(CompositeGlyphScale::Matrix([
956
                [ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
957
                [ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
958
            ]))
959
        } else {
960
810
            None
961
        };
962

            
963
810
        Ok(CompositeGlyphComponent {
964
810
            flags,
965
810
            glyph_index,
966
810
            argument1,
967
810
            argument2,
968
810
            scale,
969
810
        })
970
810
    }
971
}
972

            
973
impl WriteBinary for CompositeGlyphComponent {
974
    type Output = ();
975

            
976
    fn write<C: WriteContext>(
977
        ctxt: &mut C,
978
        glyph: CompositeGlyphComponent,
979
    ) -> Result<(), WriteError> {
980
        U16Be::write(ctxt, glyph.flags.bits())?;
981
        U16Be::write(ctxt, glyph.glyph_index)?;
982
        CompositeGlyphArgument::write(ctxt, glyph.argument1)?;
983
        CompositeGlyphArgument::write(ctxt, glyph.argument2)?;
984
        if let Some(scale) = glyph.scale {
985
            CompositeGlyphScale::write(ctxt, scale)?;
986
        }
987
        Ok(())
988
    }
989
}
990

            
991
impl WriteBinary for CompositeGlyphScale {
992
    type Output = ();
993

            
994
    fn write<C: WriteContext>(ctxt: &mut C, scale: CompositeGlyphScale) -> Result<(), WriteError> {
995
        match scale {
996
            CompositeGlyphScale::Scale(scale) => F2Dot14::write(ctxt, scale)?,
997
            CompositeGlyphScale::XY { x_scale, y_scale } => {
998
                F2Dot14::write(ctxt, x_scale)?;
999
                F2Dot14::write(ctxt, y_scale)?;
            }
            CompositeGlyphScale::Matrix(matrix) => {
                F2Dot14::write(ctxt, matrix[0][0])?;
                F2Dot14::write(ctxt, matrix[0][1])?;
                F2Dot14::write(ctxt, matrix[1][0])?;
                F2Dot14::write(ctxt, matrix[1][1])?;
            }
        }
        Ok(())
    }
}
impl ReadFrom for BoundingBox {
    type ReadType = ((I16Be, I16Be), (I16Be, I16Be));
66042
    fn read_from(((x_min, y_min), (x_max, y_max)): ((i16, i16), (i16, i16))) -> Self {
66042
        BoundingBox {
66042
            x_min,
66042
            y_min,
66042
            x_max,
66042
            y_max,
66042
        }
66042
    }
}
impl WriteBinary for BoundingBox {
    type Output = ();
    fn write<C: WriteContext>(ctxt: &mut C, bbox: BoundingBox) -> Result<(), WriteError> {
        I16Be::write(ctxt, bbox.x_min)?;
        I16Be::write(ctxt, bbox.y_min)?;
        I16Be::write(ctxt, bbox.x_max)?;
        I16Be::write(ctxt, bbox.y_max)?;
        Ok(())
    }
}
impl<'a> GlyfTable<'a> {
    /// Construct a glyph table from the supplied glyphs
    pub fn new(records: Vec<GlyfRecord<'a>>) -> Result<Self, ParseError> {
        if records.len() > usize::from(u16::MAX) {
            return Err(ParseError::LimitExceeded);
        }
        Ok(GlyfTable { records })
    }
    /// Returns the number of glyphs in this `glyf` table.
    ///
    /// Returns a `u16` for convenience of interacting with other parts of the code.
    pub fn num_glyphs(&self) -> u16 {
        // NOTE(cast): Safe as we check records length in `new` and `push`.
        self.records.len() as u16
    }
    /// The glyphs in this `glyf` table
    pub fn records(&self) -> &[GlyfRecord<'a>] {
        &self.records
    }
    /// Mutable access to the glyphs of this table
    pub fn records_mut(&mut self) -> &mut [GlyfRecord<'a>] {
        &mut self.records
    }
    /// Append a new glyph to this glyph table
    ///
    /// If the maximum number of glyphs is reached `ParseError::LimitExceeded` is returned.
    pub fn push(&mut self, record: GlyfRecord<'a>) -> Result<(), ParseError> {
        if self.num_glyphs() < u16::MAX {
            self.records.push(record);
            Ok(())
        } else {
            Err(ParseError::LimitExceeded)
        }
    }
    /// Returns a parsed glyph, converting [GlyfRecord::Present] into [GlyfRecord::Parsed] if
    /// necessary.
    pub fn get_parsed_glyph(&mut self, glyph_index: u16) -> Result<&Glyph, ParseError> {
        let record = self
            .records
            .get_mut(usize::from(glyph_index))
            .ok_or(ParseError::BadIndex)?;
        record.parse()?;
        match record {
            GlyfRecord::Parsed(glyph) => Ok(glyph),
            GlyfRecord::Present { .. } => unreachable!("glyph should be parsed"),
        }
    }
    /// Takes the glyph at `glyph_index` out of the table replacing it with `GlyphRecord::Empty`
    /// and returns it.
    ///
    /// Returns `None` if the index is out-of-bounds.
    pub(crate) fn take(&mut self, glyph_index: u16) -> Option<GlyfRecord<'a>> {
        let target = self.records.get_mut(usize::from(glyph_index))?;
        Some(mem::replace(target, GlyfRecord::empty()))
    }
    /// Replaces the glyph at `glyph_index` with the supplied `GlyfRecord`.
    ///
    /// Returns an error if the index is out-of-bounds.
    pub(crate) fn replace(
        &mut self,
        glyph_index: u16,
        record: GlyfRecord<'a>,
    ) -> Result<(), ParseError> {
        let target = self
            .records
            .get_mut(usize::from(glyph_index))
            .ok_or(ParseError::BadIndex)?;
        *target = record;
        Ok(())
    }
}
impl LocaGlyf {
    /// Construct an unloaded LocaGlyf structure
    ///
    /// Attempts to read glyphs when the type is in this state will fail.
22464
    pub fn new() -> Self {
22464
        LocaGlyf {
22464
            loaded: false,
22464
            loca: owned::LocaTable::new(),
22464
            glyf: GlyfBytes::Owned(Box::default()),
22464
            cache: FxHashMap::default(),
22464
        }
22464
    }
    /// Load tables from the supplied FontTableProvider
6151
    pub fn load<F: FontTableProvider>(provider: &F) -> Result<Self, ParseError> {
6151
        let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
6151
        let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
6151
        let loca_data = provider.read_table_data(tag::LOCA)?;
6151
        let loca = ReadScope::new(&loca_data)
6151
            .read_dep::<LocaTable<'_>>((maxp.num_glyphs, head.index_to_loc_format))?;
6151
        let loca = owned::LocaTable::from(&loca);
6151
        let glyf = read_and_box_table(provider, tag::GLYF)?;
6151
        Ok(LocaGlyf {
6151
            loaded: true,
6151
            loca,
6151
            glyf: GlyfBytes::Owned(glyf),
6151
            cache: FxHashMap::default(),
6151
        })
6151
    }
    /// Load like [`load`][Self::load], but keep the `glyf` table as a
    /// **zero-copy** view into `owner` instead of copying it onto the heap.
    ///
    /// `owner` must be the exact byte buffer the `provider` reads its tables
    /// from (typically an `Arc` over the mmap'd/owned font file). The `glyf`
    /// table is located within `owner` by matching the provider's borrowed
    /// slice against `owner`'s address range; if the provider returns an
    /// *owned* `glyf` (e.g. a WOFF-decompressed table) or the range can't be
    /// validated, this transparently falls back to an owned copy — so the
    /// result is always correct, only sometimes not zero-copy.
    ///
    /// Saves a per-font `glyf`-sized allocation (tens of MB for large CJK /
    /// system fonts). See scripts/RELEASE_SIZE_MEMORY_AUDIT_2026_07_04.md §3.3a.
6625
    pub fn load_shared<F: FontTableProvider>(
6625
        provider: &F,
6625
        owner: Arc<dyn AsRef<[u8]> + Send + Sync>,
6625
    ) -> Result<Self, ParseError> {
6625
        let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
6625
        let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
6625
        let loca_data = provider.read_table_data(tag::LOCA)?;
6625
        let loca = ReadScope::new(&loca_data)
6625
            .read_dep::<LocaTable<'_>>((maxp.num_glyphs, head.index_to_loc_format))?;
6625
        let loca = owned::LocaTable::from(&loca);
6625
        let glyf_cow = provider.read_table_data(tag::GLYF)?;
6625
        let glyf = match glyf_within(&glyf_cow, (*owner).as_ref()) {
            // The provider handed back a slice that lives inside `owner` — take
            // a zero-copy view. `owner` keeps the buffer alive.
6625
            Some((offset, len)) => GlyfBytes::Shared { owner, offset, len },
            // Owned/relocated table: fall back to a copy (still correct).
            None => GlyfBytes::Owned(Box::from(glyf_cow.into_owned())),
        };
6625
        Ok(LocaGlyf {
6625
            loaded: true,
6625
            loca,
6625
            glyf,
6625
            cache: FxHashMap::default(),
6625
        })
6625
    }
    /// Construct a loaded LocaGlyf structure from the supplied `loca` and `glyf` tables.
    ///
    /// [owned::LocaTable] can be constructed by
    /// parsing a `loca` table and then converting it to the owned version with
    /// [owned::LocaTable::from][crate::tables::loca::owned::LocaTable::from].
    pub fn loaded(loca: owned::LocaTable, glyf: Box<[u8]>) -> Self {
        LocaGlyf {
            loaded: true,
            loca,
            glyf: GlyfBytes::Owned(glyf),
            cache: FxHashMap::default(),
        }
    }
    /// Returns true if this is a loaded instance.
    pub fn is_loaded(&self) -> bool {
        self.loaded
    }
    /// Look up the glyph at the supplied index
143046
    pub fn glyph(&mut self, index: u16) -> Result<Arc<Glyph>, ParseError> {
143046
        if let Some(glyph) = self.cache.get(&index) {
71226
            return Ok(Arc::clone(glyph));
71820
        }
        // Get the start and end offsets for the glyph
71820
        let start = self
71820
            .loca
71820
            .offsets
71820
            .get(usize::from(index))
71820
            .copied()
71820
            .ok_or(ParseError::BadOffset)
71820
            .map(usize::safe_from)?;
        // The end is clamped to the length of the glyf table. This is a workaround for a font where
        // the last `loca` offset was incorrectly 1 byte beyond the end of the `glyf` table but the
        // actual glyph data did not extend beyond the table.
71820
        let end = self
71820
            .loca
71820
            .offsets
71820
            .get(
71820
                index
71820
                    .checked_add(1)
71820
                    .ok_or(ParseError::LimitExceeded)
71820
                    .map(usize::from)?,
            )
71820
            .copied()
71820
            .ok_or(ParseError::BadOffset)
71820
            .map(usize::safe_from)?
71820
            .min(self.glyf.as_bytes().len());
        // Fetch the slice for the glyph
71820
        let glyph_data = self
71820
            .glyf
71820
            .as_bytes()
71820
            .get(start..end)
71820
            .ok_or(ParseError::BadOffset)?;
        // If the slice is empty, then this is a valid, but empty glyph
71820
        let glyph = if glyph_data.is_empty() {
5778
            Arc::new(Glyph::empty())
        } else {
66042
            ReadScope::new(glyph_data).read::<Glyph>().map(Arc::new)?
        };
71820
        self.cache.insert(index, Arc::clone(&glyph));
71820
        Ok(glyph)
143046
    }
}
impl GlyfRecord<'_> {
    /// Construct an empty glyph record
    pub fn empty() -> Self {
        GlyfRecord::Parsed(Glyph::empty())
    }
    /// The number of contours of this glyph
    ///
    /// - Zero for empty glyphs
    /// - Negative for composite glyphs
    pub fn number_of_contours(&self) -> i16 {
        match self {
            GlyfRecord::Present {
                number_of_contours, ..
            } => *number_of_contours,
            GlyfRecord::Parsed(glyph) => glyph.number_of_contours(),
        }
    }
    /// The number of delta adjustable points in this glyph record excluding phantom points.
    pub fn number_of_points(&self) -> Result<u16, ParseError> {
        // The `maxp` table contains fields:
        //
        // * maxPoints            Maximum points in a non-composite glyph.
        // * maxCompositePoints   Maximum points in a composite glyph.
        //
        // Both of which are u16 so that's what we return here.
        match self {
            GlyfRecord::Present {
                scope,
                number_of_contours,
            } => {
                let mut ctxt = scope.ctxt();
                // skip glyph header: number_of_contours and the bounding box
                let _skip = ctxt.read_slice(U16Be::SIZE + BoundingBox::SIZE)?;
                if *number_of_contours >= 0 {
                    // Simple glyph
                    let end_pts_of_contours =
                        ctxt.read_array::<U16Be>(*number_of_contours as usize)?;
                    // end_pts_of_contours stores the index of the end points.
                    // Therefore the number of coordinates is the last index + 1
                    match end_pts_of_contours.last() {
                        Some(last) => last.checked_add(1).ok_or(ParseError::LimitExceeded),
                        None => Ok(0),
                    }
                } else {
                    // Composite glyph
                    let mut count = 0;
                    loop {
                        let flags = ctxt.read::<CompositeGlyphFlags>()?;
                        let _composite_glyph = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
                        count += 1;
                        if !flags.more_components() {
                            break;
                        }
                    }
                    Ok(count)
                }
            }
            GlyfRecord::Parsed(glyph) => glyph.number_of_points(),
        }
    }
    /// True if this is a composite glyph
    pub fn is_composite(&self) -> bool {
        self.number_of_contours() < 0
    }
    /// Turn self from GlyfRecord::Present into GlyfRecord::Parsed
    pub fn parse(&mut self) -> Result<(), ParseError> {
        if let GlyfRecord::Present { scope, .. } = self {
            *self = scope.read::<Glyph>().map(GlyfRecord::Parsed)?;
        }
        Ok(())
    }
}
impl<'a> From<SimpleGlyph> for GlyfRecord<'a> {
    fn from(glyph: SimpleGlyph) -> GlyfRecord<'a> {
        GlyfRecord::Parsed(Glyph::Simple(glyph))
    }
}
impl<'a> From<CompositeGlyph> for GlyfRecord<'a> {
    fn from(glyph: CompositeGlyph) -> GlyfRecord<'a> {
        GlyfRecord::Parsed(Glyph::Composite(glyph))
    }
}
impl EmptyGlyph {
    /// Construct a new empty glyph
5778
    pub fn new() -> Self {
5778
        EmptyGlyph {
5778
            phantom_points: None,
5778
        }
5778
    }
}
#[allow(missing_docs)]
pub trait CompositeGlyphFlagExt {
    fn arg_1_and_2_are_words(self) -> bool;
    fn args_are_xy_values(self) -> bool;
    fn we_have_a_scale(self) -> bool;
    fn we_have_an_x_and_y_scale(self) -> bool;
    fn we_have_a_two_by_two(self) -> bool;
    fn more_components(self) -> bool;
    fn we_have_instructions(self) -> bool;
    fn component_offsets(self) -> ComponentOffsets;
}
impl CompositeGlyphFlagExt for CompositeGlyphFlags {
1620
    fn arg_1_and_2_are_words(self) -> bool {
1620
        self.contains(CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS)
1620
    }
2430
    fn args_are_xy_values(self) -> bool {
2430
        self.contains(CompositeGlyphFlag::ARGS_ARE_XY_VALUES)
2430
    }
810
    fn we_have_a_scale(self) -> bool {
810
        self.contains(CompositeGlyphFlag::WE_HAVE_A_SCALE)
810
    }
810
    fn we_have_an_x_and_y_scale(self) -> bool {
810
        self.contains(CompositeGlyphFlag::WE_HAVE_AN_X_AND_Y_SCALE)
810
    }
810
    fn we_have_a_two_by_two(self) -> bool {
810
        self.contains(CompositeGlyphFlag::WE_HAVE_A_TWO_BY_TWO)
810
    }
810
    fn more_components(self) -> bool {
810
        self.contains(CompositeGlyphFlag::MORE_COMPONENTS)
810
    }
810
    fn we_have_instructions(self) -> bool {
810
        self.contains(CompositeGlyphFlag::WE_HAVE_INSTRUCTIONS)
810
    }
    fn component_offsets(self) -> ComponentOffsets {
        // The SCALED_COMPONENT_OFFSET and UNSCALED_COMPONENT_OFFSET flags are used to determine
        // how x and y offset values are to be interpreted when the component glyph is scaled. If
        // the SCALED_COMPONENT_OFFSET flag is set, then the x and y offset values are deemed to be
        // in the component glyph's coordinate system, and the scale transformation is applied to
        // both values.
        //
        // If the UNSCALED_COMPONENT_OFFSET flag is set, then the x and y offset values are deemed
        // to be in the current glyph's coordinate system, and the scale transformation is not
        // applied to either value.
        //
        // If neither flag is set, then the rasterizer will apply a default behavior. On Microsoft
        // and Apple platforms, the default behavior is the same as when the
        // UNSCALED_COMPONENT_OFFSET flag is set; this behavior is recommended for all rasterizer
        // implementations. If a font has both flags set, this is invalid; the rasterizer should use
        // its default behavior for this case.
        let scaled = self.contains(CompositeGlyphFlag::SCALED_COMPONENT_OFFSET);
        let unscaled = self.contains(CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET);
        match (scaled, unscaled) {
            (true, false) => ComponentOffsets::Scaled,
            (false, true) => ComponentOffsets::Unscaled,
            // Default for neither or both set
            (true, true) | (false, false) => ComponentOffsets::Unscaled,
        }
    }
}
/// Flag indicating whether the offsets in a composite glyph component are scaled or not
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ComponentOffsets {
    /// Offsets are scaled
    Scaled,
    /// Offsets are not scaled
    Unscaled,
}
impl Point {
    /// A point at (0, 0)
1358100
    pub fn zero() -> Self {
1358100
        Point(0, 0)
1358100
    }
}
impl BoundingBox {
    /// Contruct a new, empty bounding box
    pub fn empty() -> Self {
        BoundingBox {
            x_min: 0,
            x_max: 0,
            y_min: 0,
            y_max: 0,
        }
    }
    /// Calculate xMin, xMax and yMin, yMax from a collection of `Points`
    ///
    /// Panics if `points` is empty.
    pub fn from_points(points: impl ExactSizeIterator<Item = Point>) -> Self {
        assert!(points.len() > 0);
        let mut points = points.peekable();
        // NOTE(unwrap): Safe as length is at least 1
        let &Point(initial_x, initial_y) = points.peek().unwrap();
        let initial = BoundingBox {
            x_min: initial_x,
            x_max: initial_x,
            y_min: initial_y,
            y_max: initial_y,
        };
        points.fold(initial, |mut bounding_box, point| {
            bounding_box.add(point);
            bounding_box
        })
    }
    /// Update this bounding box to contain `point`.
    pub fn add(&mut self, Point(x, y): Point) {
        self.x_min = i16::min(x, self.x_min);
        self.x_max = i16::max(x, self.x_max);
        self.y_min = i16::min(y, self.y_min);
        self.y_max = i16::max(y, self.y_max);
    }
}
impl std::ops::Add for Point {
    type Output = Self;
    fn add(self, Point(x1, y1): Point) -> Self::Output {
        let Point(x, y) = self;
        Point(x + x1, y + y1)
    }
}
impl From<CompositeGlyphArgument> for i32 {
1620
    fn from(arg: CompositeGlyphArgument) -> Self {
1620
        match arg {
            CompositeGlyphArgument::U8(value) => i32::from(value),
1080
            CompositeGlyphArgument::I8(value) => i32::from(value),
            CompositeGlyphArgument::U16(value) => i32::from(value),
540
            CompositeGlyphArgument::I16(value) => i32::from(value),
        }
1620
    }
}
impl TryFrom<CompositeGlyphArgument> for u16 {
    type Error = std::num::TryFromIntError;
    fn try_from(arg: CompositeGlyphArgument) -> Result<Self, Self::Error> {
        match arg {
            CompositeGlyphArgument::U8(value) => Ok(u16::from(value)),
            CompositeGlyphArgument::I8(value) => u16::try_from(value),
            CompositeGlyphArgument::U16(value) => Ok(value),
            CompositeGlyphArgument::I16(value) => u16::try_from(value),
        }
    }
}
impl From<CompositeGlyphScale> for Matrix2x2F {
    fn from(scale: CompositeGlyphScale) -> Self {
        match scale {
            CompositeGlyphScale::Scale(scale) => {
                let scale = f32::from(scale);
                Matrix2x2F::from_scale(scale)
            }
            CompositeGlyphScale::XY { x_scale, y_scale } => {
                let scale = Vector2F::new(f32::from(x_scale), f32::from(y_scale));
                Matrix2x2F::from_scale(scale)
            }
            CompositeGlyphScale::Matrix(matrix) => Matrix2x2F::row_major(
                f32::from(matrix[0][0]),
                f32::from(matrix[0][1]),
                f32::from(matrix[1][0]),
                f32::from(matrix[1][1]),
            ),
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::binary::write::WriteBuffer;
    use crate::error::ReadWriteError;
    pub(super) fn simple_glyph_fixture() -> SimpleGlyph {
        SimpleGlyph {
            bounding_box: BoundingBox {
                x_min: 60,
                x_max: 915,
                y_min: -105,
                y_max: 702,
            },
            end_pts_of_contours: vec![8],
            instructions: Box::default(),
            coordinates: vec![
                (
                    SimpleGlyphFlag::ON_CURVE_POINT
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
                    Point(433, 77),
                ),
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
                    Point(499, 30),
                ),
                (
                    SimpleGlyphFlag::ON_CURVE_POINT
                        | SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
                    Point(625, 2),
                ),
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
                    Point(756, -27),
                ),
                (
                    SimpleGlyphFlag::ON_CURVE_POINT
                        | SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR
                        | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
                    Point(915, -31),
                ),
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
                    Point(891, -47),
                ),
                (
                    SimpleGlyphFlag::ON_CURVE_POINT
                        | SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR,
                    Point(862, -60),
                ),
                (
                    SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
                    Point(832, -73),
                ),
                (
                    SimpleGlyphFlag::ON_CURVE_POINT
                        | SimpleGlyphFlag::X_SHORT_VECTOR
                        | SimpleGlyphFlag::Y_SHORT_VECTOR,
                    Point(819, -103),
                ),
            ],
            phantom_points: None,
        }
    }
    pub(super) fn composite_glyph_fixture(instructions: &'static [u8]) -> CompositeGlyph {
        CompositeGlyph {
            bounding_box: BoundingBox {
                x_min: 205,
                x_max: 4514,
                y_min: 0,
                y_max: 1434,
            },
            glyphs: vec![
                CompositeGlyphComponent {
                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
                        | CompositeGlyphFlag::MORE_COMPONENTS
                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
                    glyph_index: 5,
                    argument1: CompositeGlyphArgument::I16(3453),
                    argument2: CompositeGlyphArgument::I16(0),
                    scale: None,
                },
                CompositeGlyphComponent {
                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
                        | CompositeGlyphFlag::MORE_COMPONENTS
                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
                    glyph_index: 4,
                    argument1: CompositeGlyphArgument::I16(2773),
                    argument2: CompositeGlyphArgument::I16(0),
                    scale: None,
                },
                CompositeGlyphComponent {
                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
                        | CompositeGlyphFlag::MORE_COMPONENTS
                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
                    glyph_index: 3,
                    argument1: CompositeGlyphArgument::I16(1182),
                    argument2: CompositeGlyphArgument::I16(0),
                    scale: None,
                },
                CompositeGlyphComponent {
                    flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
                        | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
                        | CompositeGlyphFlag::ROUND_XY_TO_GRID
                        | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET
                        | CompositeGlyphFlag::WE_HAVE_INSTRUCTIONS,
                    glyph_index: 2,
                    argument1: CompositeGlyphArgument::I16(205),
                    argument2: CompositeGlyphArgument::I16(0),
                    scale: None,
                },
            ],
            instructions: Box::from(instructions),
            phantom_points: None,
        }
    }
    #[test]
    fn test_point_bounding_box() {
        let points = [Point(1761, 565), Point(2007, 565), Point(1884, 1032)];
        let expected = BoundingBox {
            x_min: 1761,
            y_min: 565,
            x_max: 2007,
            y_max: 1032,
        };
        assert_eq!(BoundingBox::from_points(points.iter().copied()), expected);
    }
    #[test]
    fn write_glyf_table_loca_sanity_check() {
        let glyf = GlyfTable {
            records: vec![GlyfRecord::empty(), GlyfRecord::empty()],
        };
        let num_glyphs = glyf.records.len();
        let mut buffer = WriteBuffer::new();
        let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
        assert_eq!(loca.offsets.len(), num_glyphs + 1);
    }
    #[test]
    fn write_composite_glyf_instructions() {
        let glyph = Glyph::Composite(composite_glyph_fixture(&[1, 2, 3, 4]));
        let mut buffer = WriteBuffer::new();
        Glyph::write(&mut buffer, glyph).unwrap();
        // Read it back and check the instructions are intact
        match ReadScope::new(buffer.bytes()).read::<Glyph>() {
            Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
                assert_eq!(&*instructions, vec![1, 2, 3, 4].as_slice())
            }
            _ => panic!("did not read back expected instructions"),
        }
    }
    #[test]
    fn read_glyph_offsets_correctly() {
        // Test for a bug in which only the length relative to current ReadCtxt offset was used
        // to read a glyph out of the `glyf` table. It should have been using `start` and `end`
        // offsets read from `loca`. The bug was discovered when reading the Baekmuk Batang font
        // in which the glyph data starts at offset 366.
        let glyph = simple_glyph_fixture();
        // Write the glyph out
        let mut buffer = WriteBuffer::new();
        buffer.write_zeros(4).unwrap(); // Add some unused data at the start
        SimpleGlyph::write(&mut buffer, glyph).unwrap();
        let glyph_data = buffer.into_inner();
        let mut buffer = WriteBuffer::new();
        let loca = owned::LocaTable {
            offsets: vec![4, 4, glyph_data.len() as u32 - 4],
        };
        owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
            .expect("unable to generate loca");
        let loca_data = buffer.into_inner();
        // Parse and verify
        let num_glyphs = 2;
        let loca = ReadScope::new(&loca_data)
            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
            .expect("unable to read loca");
        let glyf = ReadScope::new(&glyph_data)
            .read_dep::<GlyfTable<'_>>(&loca)
            .expect("unable to read glyf");
        assert_eq!(glyf.records.len(), 2);
        assert_eq!(&glyf.records[0], &GlyfRecord::empty());
        let glyph = &glyf.records[1];
        // Before the fix num_contours was read as 0
        assert_eq!(glyph.number_of_contours(), 1);
    }
    // Regarding simple glyphs the OpenType spec says:
    // This is the table information needed if numberOfContours is greater than or equal to zero
    // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description
    //
    // We previously rejected glyphs with zero contours.
    #[test]
    fn simple_glyph_with_zero_contours() {
        let glyph_data = &[
            0, 0, 0, 0, 0, 0, 0, 0, // bounding box
            0, 0, // instruction length
        ];
        let expected = SimpleGlyph {
            bounding_box: BoundingBox::empty(),
            end_pts_of_contours: vec![],
            instructions: Box::default(),
            coordinates: vec![],
            phantom_points: None,
        };
        let glyph = ReadScope::new(glyph_data)
            .read_dep::<SimpleGlyph>(0)
            .unwrap();
        assert_eq!(glyph, expected);
    }
    #[test]
    fn write_simple_glyph_with_zero_contours() {
        let glyph = SimpleGlyph {
            bounding_box: BoundingBox::empty(),
            end_pts_of_contours: vec![],
            instructions: Box::default(),
            coordinates: vec![],
            phantom_points: None,
        };
        let mut buffer = WriteBuffer::new();
        assert!(SimpleGlyph::write(&mut buffer, glyph).is_ok());
    }
    #[test]
    fn read_glyph_with_incorrect_loca_length() {
        // Write the glyph out
        let glyph = simple_glyph_fixture();
        let mut buffer = WriteBuffer::new();
        Glyph::write(&mut buffer, Glyph::Simple(glyph)).unwrap();
        let glyph_data = buffer.into_inner();
        let mut buffer = WriteBuffer::new();
        let loca = owned::LocaTable {
            offsets: vec![0, 0, glyph_data.len() as u32 + 1], // + 1 to go past end of glyf
        };
        owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
            .expect("unable to generate loca");
        let loca_data = buffer.into_inner();
        // Parse and verify
        let num_glyphs = 2;
        let loca = ReadScope::new(&loca_data)
            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
            .expect("unable to read loca");
        assert!(ReadScope::new(&glyph_data)
            .read_dep::<GlyfTable<'_>>(&loca)
            .is_ok())
    }
    // This is a test for a bug in which a composite glyph read with has_instructions = yes, but
    // instruction length 0 would be written without an instruction length field. This resulting
    // font was invalid as parsers would see the has_instructions flag and attempt to read the
    // non-existent instruction length.
    #[test]
    fn write_composite_glyph_with_empty_instructions() {
        let glyph = composite_glyph_fixture(&[]);
        let mut buffer = WriteBuffer::new();
        Glyph::write(&mut buffer, Glyph::Composite(glyph)).unwrap();
        // Ensure we can read it back. Before this fix this failed.
        match ReadScope::new(buffer.bytes()).read::<Glyph>() {
            Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
                assert_eq!(instructions, Box::default())
            }
            Ok(_) => panic!("did not read back expected glyph"),
            Err(_) => panic!("unable to read back glyph"),
        }
    }
    #[test]
    fn test_number_of_points_empty() {
        let glyph = GlyfRecord::empty();
        assert_eq!(glyph.number_of_points().unwrap(), 0);
    }
    #[test]
    fn test_number_of_points_simple_parsed() {
        let glyph = GlyfRecord::from(simple_glyph_fixture());
        assert_eq!(glyph.number_of_points().unwrap(), 9);
    }
    #[test]
    fn test_number_of_points_simple_present() -> Result<(), ReadWriteError> {
        // Serialize
        let glyph = GlyfRecord::from(simple_glyph_fixture());
        let glyf = GlyfTable {
            records: vec![GlyfRecord::empty(), glyph],
        };
        let num_glyphs = glyf.records.len() as u16;
        let mut buffer = WriteBuffer::new();
        let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
        let mut loca_buffer = WriteBuffer::new();
        owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
        let loca_data = loca_buffer.into_inner();
        let loca = ReadScope::new(&loca_data)
            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
        // Read back
        let glyf = ReadScope::new(&buffer.bytes())
            .read_dep::<GlyfTable<'_>>(&loca)
            .unwrap();
        let glyph = &glyf.records[1];
        assert!(matches!(glyph, GlyfRecord::Present { .. }));
        assert_eq!(glyph.number_of_points().unwrap(), 9);
        Ok(())
    }
    #[test]
    fn test_number_of_points_composite_parsed() {
        // Test parsed
        let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
        assert_eq!(glyph.number_of_points().unwrap(), 4);
    }
    #[test]
    fn test_number_of_points_composite_present() -> Result<(), ReadWriteError> {
        // Serialize
        let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
        let glyf = GlyfTable {
            records: vec![GlyfRecord::empty(), glyph],
        };
        let num_glyphs = glyf.records.len() as u16;
        let mut buffer = WriteBuffer::new();
        let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
        let mut loca_buffer = WriteBuffer::new();
        owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
        let loca_data = loca_buffer.into_inner();
        let loca = ReadScope::new(&loca_data)
            .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
        // Read back
        let glyf = ReadScope::new(&buffer.bytes())
            .read_dep::<GlyfTable<'_>>(&loca)
            .unwrap();
        let glyph = &glyf.records[1];
        assert!(matches!(glyph, GlyfRecord::Present { .. }));
        assert_eq!(glyph.number_of_points().unwrap(), 4);
        Ok(())
    }
}