ttf_parser/
lib.rs

1/*!
2A high-level, safe, zero-allocation TrueType font parser.
3
4Supports [TrueType](https://docs.microsoft.com/en-us/typography/truetype/),
5[OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/)
6and [AAT](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)
7fonts.
8
9Font parsing starts with a [`Face`].
10
11## Features
12
13- A high-level API for most common properties, hiding all parsing and data resolving logic.
14- A low-level, but safe API to access TrueType tables data.
15- Highly configurable. You can disable most of the features, reducing binary size.
16  You can also parse TrueType tables separately, without loading the whole font/face.
17- Zero heap allocations.
18- Zero unsafe.
19- Zero dependencies.
20- `no_std`/WASM compatible.
21- Fast.
22- Stateless. All parsing methods are immutable.
23- Simple and maintainable code (no magic numbers).
24
25## Safety
26
27- The library must not panic. Any panic considered as a critical bug and should be reported.
28- The library forbids unsafe code.
29- No heap allocations, so crash due to OOM is not possible.
30- All recursive methods have a depth limit.
31- Technically, should use less than 64KiB of stack in worst case scenario.
32- Most of arithmetic operations are checked.
33- Most of numeric casts are checked.
34*/
35
36#![no_std]
37#![forbid(unsafe_code)]
38#![warn(missing_docs)]
39#![warn(missing_copy_implementations)]
40#![warn(missing_debug_implementations)]
41
42#[cfg(feature = "std")]
43#[macro_use]
44extern crate std;
45
46macro_rules! try_opt_or {
47    ($value:expr, $ret:expr) => {
48        match $value {
49            Some(v) => v,
50            None => return $ret,
51        }
52    };
53}
54
55mod parser;
56mod tables;
57#[cfg(feature = "apple-layout")] mod aat;
58#[cfg(feature = "opentype-layout")] mod ggg;
59#[cfg(feature = "variable-fonts")] mod var_store;
60
61use parser::{Stream, NumFrom, TryNumFrom, Offset32, Offset};
62pub use parser::{FromData, LazyArray16, LazyArrayIter16, LazyArray32, LazyArrayIter32, Fixed};
63use head::IndexToLocationFormat;
64
65#[cfg(feature = "variable-fonts")] pub use fvar::VariationAxis;
66
67pub use name::{name_id, PlatformId};
68pub use os2::{Weight, Width, ScriptMetrics, Style};
69pub use tables::CFFError;
70pub use tables::{cmap, kern, sbix, maxp, hmtx, name, os2, loca, svg, vorg, post, head, hhea, glyf};
71pub use tables::{cff1 as cff, vhea, cbdt, cblc};
72#[cfg(feature = "opentype-layout")] pub use tables::{gdef, gpos, gsub};
73#[cfg(feature = "apple-layout")] pub use tables::{ankr, feat, kerx, morx, trak};
74#[cfg(feature = "variable-fonts")] pub use tables::{cff2, avar, fvar, gvar, hvar, mvar};
75
76#[cfg(feature = "opentype-layout")]
77pub mod opentype_layout {
78    //! This module contains
79    //! [OpenType Layout](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#overview)
80    //! supplementary tables implementation.
81    pub use crate::ggg::*;
82}
83
84#[cfg(feature = "apple-layout")]
85pub mod apple_layout {
86    //! This module contains
87    //! [Apple Advanced Typography Layout](
88    //! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)
89    //! supplementary tables implementation.
90    pub use crate::aat::*;
91}
92
93
94/// A type-safe wrapper for glyph ID.
95#[repr(transparent)]
96#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Debug, Hash)]
97pub struct GlyphId(pub u16);
98
99impl FromData for GlyphId {
100    const SIZE: usize = 2;
101
102    #[inline]
103    fn parse(data: &[u8]) -> Option<Self> {
104        u16::parse(data).map(GlyphId)
105    }
106}
107
108
109/// A TrueType font magic.
110///
111/// https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
112#[derive(Clone, Copy, PartialEq, Debug)]
113enum Magic {
114    TrueType,
115    OpenType,
116    FontCollection,
117}
118
119impl FromData for Magic {
120    const SIZE: usize = 4;
121
122    #[inline]
123    fn parse(data: &[u8]) -> Option<Self> {
124        match u32::parse(data)? {
125            0x00010000 | 0x74727565 => Some(Magic::TrueType),
126            0x4F54544F => Some(Magic::OpenType),
127            0x74746366 => Some(Magic::FontCollection),
128            _ => None,
129        }
130    }
131}
132
133
134/// A variation coordinate in a normalized coordinate system.
135///
136/// Basically any number in a -1.0..1.0 range.
137/// Where 0 is a default value.
138///
139/// The number is stored as f2.16
140#[repr(transparent)]
141#[derive(Clone, Copy, PartialEq, Default, Debug)]
142pub struct NormalizedCoordinate(i16);
143
144impl From<i16> for NormalizedCoordinate {
145    /// Creates a new coordinate.
146    ///
147    /// The provided number will be clamped to the -16384..16384 range.
148    #[inline]
149    fn from(n: i16) -> Self {
150        NormalizedCoordinate(parser::i16_bound(-16384, n, 16384))
151    }
152}
153
154impl From<f32> for NormalizedCoordinate {
155    /// Creates a new coordinate.
156    ///
157    /// The provided number will be clamped to the -1.0..1.0 range.
158    #[inline]
159    fn from(n: f32) -> Self {
160        NormalizedCoordinate((parser::f32_bound(-1.0, n, 1.0) * 16384.0) as i16)
161    }
162}
163
164impl NormalizedCoordinate {
165    /// Returns the coordinate value as f2.14.
166    #[inline]
167    pub fn get(self) -> i16 {
168        self.0
169    }
170}
171
172
173/// A font variation value.
174///
175/// # Example
176///
177/// ```
178/// use ttf_parser::{Variation, Tag};
179///
180/// Variation { axis: Tag::from_bytes(b"wght"), value: 500.0 };
181/// ```
182#[derive(Clone, Copy, PartialEq, Debug)]
183pub struct Variation {
184    /// An axis tag name.
185    pub axis: Tag,
186    /// An axis value.
187    pub value: f32,
188}
189
190
191/// A 4-byte tag.
192#[repr(transparent)]
193#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194pub struct Tag(pub u32);
195
196impl Tag {
197    /// Creates a `Tag` from bytes.
198    ///
199    /// # Example
200    ///
201    /// ```rust
202    /// println!("{}", ttf_parser::Tag::from_bytes(b"name"));
203    /// ```
204    #[inline]
205    pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
206        Tag(((bytes[0] as u32) << 24) | ((bytes[1] as u32) << 16) |
207            ((bytes[2] as u32) << 8) | (bytes[3] as u32))
208    }
209
210    /// Creates a `Tag` from bytes.
211    ///
212    /// In case of empty data will return `Tag` set to 0.
213    ///
214    /// When `bytes` are shorter than 4, will set missing bytes to ` `.
215    ///
216    /// Data after first 4 bytes is ignored.
217    #[inline]
218    pub fn from_bytes_lossy(bytes: &[u8]) -> Self {
219        if bytes.is_empty() {
220            return Tag::from_bytes(&[0, 0, 0, 0]);
221        }
222
223        let mut iter = bytes.iter().cloned().chain(core::iter::repeat(b' '));
224        Tag::from_bytes(&[
225            iter.next().unwrap(),
226            iter.next().unwrap(),
227            iter.next().unwrap(),
228            iter.next().unwrap(),
229        ])
230    }
231
232    /// Returns tag as 4-element byte array.
233    #[inline]
234    pub const fn to_bytes(self) -> [u8; 4] {
235        [
236            (self.0 >> 24 & 0xff) as u8,
237            (self.0 >> 16 & 0xff) as u8,
238            (self.0 >> 8 & 0xff) as u8,
239            (self.0 >> 0 & 0xff) as u8,
240        ]
241    }
242
243    /// Returns tag as 4-element byte array.
244    #[inline]
245    pub const fn to_chars(self) -> [char; 4] {
246        [
247            (self.0 >> 24 & 0xff) as u8 as char,
248            (self.0 >> 16 & 0xff) as u8 as char,
249            (self.0 >> 8 & 0xff) as u8 as char,
250            (self.0 >> 0 & 0xff) as u8 as char,
251        ]
252    }
253
254    /// Checks if tag is null / `[0, 0, 0, 0]`.
255    #[inline]
256    pub const fn is_null(&self) -> bool {
257        self.0 == 0
258    }
259
260    /// Returns tag value as `u32` number.
261    #[inline]
262    pub const fn as_u32(&self) -> u32 {
263        self.0
264    }
265}
266
267impl core::fmt::Debug for Tag {
268    #[inline]
269    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270        write!(f, "Tag({})", self)
271    }
272}
273
274impl core::fmt::Display for Tag {
275    #[inline]
276    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
277        let b = self.to_chars();
278        write!(
279            f,
280            "{}{}{}{}",
281            b.get(0).unwrap_or(&' '),
282            b.get(1).unwrap_or(&' '),
283            b.get(2).unwrap_or(&' '),
284            b.get(3).unwrap_or(&' ')
285        )
286    }
287}
288
289impl FromData for Tag {
290    const SIZE: usize = 4;
291
292    #[inline]
293    fn parse(data: &[u8]) -> Option<Self> {
294        u32::parse(data).map(Tag)
295    }
296}
297
298
299/// A line metrics.
300///
301/// Used for underline and strikeout.
302#[repr(C)]
303#[derive(Clone, Copy, PartialEq, Debug)]
304pub struct LineMetrics {
305    /// Line position.
306    pub position: i16,
307
308    /// Line thickness.
309    pub thickness: i16,
310}
311
312
313/// A rectangle.
314///
315/// Doesn't guarantee that `x_min` <= `x_max` and/or `y_min` <= `y_max`.
316#[repr(C)]
317#[allow(missing_docs)]
318#[derive(Clone, Copy, PartialEq, Debug)]
319pub struct Rect {
320    pub x_min: i16,
321    pub y_min: i16,
322    pub x_max: i16,
323    pub y_max: i16,
324}
325
326impl Rect {
327    /// Returns rect's width.
328    #[inline]
329    pub fn width(&self) -> i16 {
330        self.x_max - self.x_min
331    }
332
333    /// Returns rect's height.
334    #[inline]
335    pub fn height(&self) -> i16 {
336        self.y_max - self.y_min
337    }
338}
339
340
341#[derive(Clone, Copy, Debug)]
342pub(crate) struct BBox {
343    x_min: f32,
344    y_min: f32,
345    x_max: f32,
346    y_max: f32,
347}
348
349impl BBox {
350    #[inline]
351    fn new() -> Self {
352        BBox {
353            x_min: core::f32::MAX,
354            y_min: core::f32::MAX,
355            x_max: core::f32::MIN,
356            y_max: core::f32::MIN,
357        }
358    }
359
360    #[inline]
361    fn is_default(&self) -> bool {
362        self.x_min == core::f32::MAX &&
363        self.y_min == core::f32::MAX &&
364        self.x_max == core::f32::MIN &&
365        self.y_max == core::f32::MIN
366    }
367
368    #[inline]
369    fn extend_by(&mut self, x: f32, y: f32) {
370        self.x_min = self.x_min.min(x);
371        self.y_min = self.y_min.min(y);
372        self.x_max = self.x_max.max(x);
373        self.y_max = self.y_max.max(y);
374    }
375
376    #[inline]
377    fn to_rect(&self) -> Option<Rect> {
378        Some(Rect {
379            x_min: i16::try_num_from(self.x_min)?,
380            y_min: i16::try_num_from(self.y_min)?,
381            x_max: i16::try_num_from(self.x_max)?,
382            y_max: i16::try_num_from(self.y_max)?,
383        })
384    }
385}
386
387
388/// A trait for glyph outline construction.
389pub trait OutlineBuilder {
390    /// Appends a MoveTo segment.
391    ///
392    /// Start of a contour.
393    fn move_to(&mut self, x: f32, y: f32);
394
395    /// Appends a LineTo segment.
396    fn line_to(&mut self, x: f32, y: f32);
397
398    /// Appends a QuadTo segment.
399    fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32);
400
401    /// Appends a CurveTo segment.
402    fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32);
403
404    /// Appends a ClosePath segment.
405    ///
406    /// End of a contour.
407    fn close(&mut self);
408}
409
410
411struct DummyOutline;
412impl OutlineBuilder for DummyOutline {
413    fn move_to(&mut self, _: f32, _: f32) {}
414    fn line_to(&mut self, _: f32, _: f32) {}
415    fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {}
416    fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {}
417    fn close(&mut self) {}
418}
419
420
421/// A glyph raster image format.
422#[allow(missing_docs)]
423#[derive(Clone, Copy, PartialEq, Debug)]
424pub enum RasterImageFormat {
425    PNG,
426}
427
428
429/// A glyph's raster image.
430///
431/// Note, that glyph metrics are in pixels and not in font units.
432#[derive(Clone, Copy, PartialEq, Debug)]
433pub struct RasterGlyphImage<'a> {
434    /// Horizontal offset.
435    pub x: i16,
436
437    /// Vertical offset.
438    pub y: i16,
439
440    /// Image width.
441    ///
442    /// It doesn't guarantee that this value is the same as set in the `data`.
443    pub width: u16,
444
445    /// Image height.
446    ///
447    /// It doesn't guarantee that this value is the same as set in the `data`.
448    pub height: u16,
449
450    /// A pixels per em of the selected strike.
451    pub pixels_per_em: u16,
452
453    /// An image format.
454    pub format: RasterImageFormat,
455
456    /// A raw image data. It's up to the caller to decode it.
457    pub data: &'a [u8],
458}
459
460
461#[derive(Clone, Copy)]
462struct TableRecord {
463    table_tag: Tag,
464    #[allow(dead_code)]
465    check_sum: u32,
466    offset: u32,
467    length: u32,
468}
469
470impl FromData for TableRecord {
471    const SIZE: usize = 16;
472
473    #[inline]
474    fn parse(data: &[u8]) -> Option<Self> {
475        let mut s = Stream::new(data);
476        Some(TableRecord {
477            table_tag: s.read::<Tag>()?,
478            check_sum: s.read::<u32>()?,
479            offset: s.read::<u32>()?,
480            length: s.read::<u32>()?,
481        })
482    }
483}
484
485
486#[cfg(feature = "variable-fonts")]
487const MAX_VAR_COORDS: usize = 32;
488
489#[cfg(feature = "variable-fonts")]
490#[derive(Clone, Default)]
491struct VarCoords {
492    data: [NormalizedCoordinate; MAX_VAR_COORDS],
493    len: u8,
494}
495
496#[cfg(feature = "variable-fonts")]
497impl VarCoords {
498    #[inline]
499    fn as_slice(&self) -> &[NormalizedCoordinate] {
500        &self.data[0..usize::from(self.len)]
501    }
502
503    #[inline]
504    fn as_mut_slice(&mut self) -> &mut [NormalizedCoordinate] {
505        let end = usize::from(self.len);
506        &mut self.data[0..end]
507    }
508}
509
510
511/// A list of font face parsing errors.
512#[derive(Clone, Copy, PartialEq, Debug)]
513pub enum FaceParsingError {
514    /// An attempt to read out of bounds detected.
515    ///
516    /// Should occur only on malformed fonts.
517    MalformedFont,
518
519    /// Face data must start with `0x00010000`, `0x74727565`, `0x4F54544F` or `0x74746366`.
520    UnknownMagic,
521
522    /// The face index is larger than the number of faces in the font.
523    FaceIndexOutOfBounds,
524
525    /// The `head` table is missing or malformed.
526    NoHeadTable,
527
528    /// The `hhea` table is missing or malformed.
529    NoHheaTable,
530
531    /// The `maxp` table is missing or malformed.
532    NoMaxpTable,
533}
534
535impl core::fmt::Display for FaceParsingError {
536    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
537        match self {
538            FaceParsingError::MalformedFont => write!(f, "malformed font"),
539            FaceParsingError::UnknownMagic => write!(f, "unknown magic"),
540            FaceParsingError::FaceIndexOutOfBounds => write!(f, "face index is out of bounds"),
541            FaceParsingError::NoHeadTable => write!(f, "the head table is missing or malformed"),
542            FaceParsingError::NoHheaTable => write!(f, "the hhea table is missing or malformed"),
543            FaceParsingError::NoMaxpTable => write!(f, "the maxp table is missing or malformed"),
544        }
545    }
546}
547
548#[cfg(feature = "std")]
549impl std::error::Error for FaceParsingError {}
550
551
552/// A raw font face.
553///
554/// You are probably looking for [`Face`]. This is a low-level type.
555///
556/// Unlike [`Face`], [`RawFace`] parses only face table records.
557/// Meaning all you can get from this type is a raw (`&[u8]`) data of a requested table.
558/// Then you can either parse just a singe table from a font/face or populate [`RawFaceTables`]
559/// manually before passing it to [`Face::from_raw_tables`].
560#[derive(Clone, Copy)]
561pub struct RawFace<'a> {
562    data: &'a [u8],
563    table_records: LazyArray16<'a, TableRecord>,
564}
565
566impl<'a> RawFace<'a> {
567    /// Creates a new [`RawFace`] from a raw data.
568    ///
569    /// `index` indicates the specific font face in a font collection.
570    /// Use [`fonts_in_collection`] to get the total number of font faces.
571    /// Set to 0 if unsure.
572    ///
573    /// While we do reuse [`FaceParsingError`], `No*Table` errors will not be throws.
574    pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
575        // https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
576
577        let mut s = Stream::new(data);
578
579        // Read **font** magic.
580        let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
581        if magic == Magic::FontCollection {
582            s.skip::<u32>(); // version
583            let number_of_faces = s.read::<u32>().ok_or(FaceParsingError::MalformedFont)?;
584            let offsets = s.read_array32::<Offset32>(number_of_faces)
585                .ok_or(FaceParsingError::MalformedFont)?;
586
587            let face_offset = offsets.get(index).ok_or(FaceParsingError::FaceIndexOutOfBounds)?;
588            // Face offset is from the start of the font data,
589            // so we have to adjust it to the current parser offset.
590            let face_offset = face_offset.to_usize().checked_sub(s.offset())
591                .ok_or(FaceParsingError::MalformedFont)?;
592            s.advance_checked(face_offset).ok_or(FaceParsingError::MalformedFont)?;
593
594            // Read **face** magic.
595            // Each face in a font collection also starts with a magic.
596            let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
597            // And face in a font collection can't be another collection.
598            if magic == Magic::FontCollection {
599                return Err(FaceParsingError::UnknownMagic);
600            }
601        }
602
603        let num_tables = s.read::<u16>().ok_or(FaceParsingError::MalformedFont)?;
604        s.advance(6); // searchRange (u16) + entrySelector (u16) + rangeShift (u16)
605        let table_records = s.read_array16::<TableRecord>(num_tables)
606            .ok_or(FaceParsingError::MalformedFont)?;
607
608        Ok(RawFace {
609            data,
610            table_records,
611        })
612    }
613
614    /// Returns the raw data of a selected table.
615    pub fn table(&self, tag: Tag) -> Option<&'a [u8]> {
616        let (_, table) = self.table_records.binary_search_by(|record| record.table_tag.cmp(&tag))?;
617        let offset = usize::num_from(table.offset);
618        let length = usize::num_from(table.length);
619        let end = offset.checked_add(length)?;
620        self.data.get(offset..end)
621    }
622}
623
624impl core::fmt::Debug for RawFace<'_> {
625    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
626        write!(f, "RawFace {{ ... }}")
627    }
628}
629
630
631/// A list of all supported tables as raw data.
632///
633/// This type should be used in tandem with
634/// [`Face::from_raw_tables()`](struct.Face.html#method.from_raw_tables).
635///
636/// This allows loading font faces not only from TrueType font files,
637/// but from any source. Mainly used for parsing WOFF.
638#[allow(missing_docs)]
639#[allow(missing_debug_implementations)]
640#[derive(Clone, Default)]
641pub struct RawFaceTables<'a> {
642    // Mandatory tables.
643    pub head: &'a [u8],
644    pub hhea: &'a [u8],
645    pub maxp: &'a [u8],
646
647    pub cbdt: Option<&'a [u8]>,
648    pub cblc: Option<&'a [u8]>,
649    pub cff:  Option<&'a [u8]>,
650    pub cmap: Option<&'a [u8]>,
651    pub glyf: Option<&'a [u8]>,
652    pub hmtx: Option<&'a [u8]>,
653    pub kern: Option<&'a [u8]>,
654    pub loca: Option<&'a [u8]>,
655    pub name: Option<&'a [u8]>,
656    pub os2:  Option<&'a [u8]>,
657    pub post: Option<&'a [u8]>,
658    pub sbix: Option<&'a [u8]>,
659    pub svg:  Option<&'a [u8]>,
660    pub vhea: Option<&'a [u8]>,
661    pub vmtx: Option<&'a [u8]>,
662    pub vorg: Option<&'a [u8]>,
663
664    #[cfg(feature = "opentype-layout")] pub gdef: Option<&'a [u8]>,
665    #[cfg(feature = "opentype-layout")] pub gpos: Option<&'a [u8]>,
666    #[cfg(feature = "opentype-layout")] pub gsub: Option<&'a [u8]>,
667
668    #[cfg(feature = "apple-layout")] pub ankr: Option<&'a [u8]>,
669    #[cfg(feature = "apple-layout")] pub feat: Option<&'a [u8]>,
670    #[cfg(feature = "apple-layout")] pub kerx: Option<&'a [u8]>,
671    #[cfg(feature = "apple-layout")] pub morx: Option<&'a [u8]>,
672    #[cfg(feature = "apple-layout")] pub trak: Option<&'a [u8]>,
673
674    #[cfg(feature = "variable-fonts")] pub avar: Option<&'a [u8]>,
675    #[cfg(feature = "variable-fonts")] pub cff2: Option<&'a [u8]>,
676    #[cfg(feature = "variable-fonts")] pub fvar: Option<&'a [u8]>,
677    #[cfg(feature = "variable-fonts")] pub gvar: Option<&'a [u8]>,
678    #[cfg(feature = "variable-fonts")] pub hvar: Option<&'a [u8]>,
679    #[cfg(feature = "variable-fonts")] pub mvar: Option<&'a [u8]>,
680    #[cfg(feature = "variable-fonts")] pub vvar: Option<&'a [u8]>,
681}
682
683/// Parsed face tables.
684///
685/// Unlike [`Face`], provides a low-level parsing abstraction over TrueType tables.
686/// Useful when you need a direct access to tables data.
687///
688/// Also, used when high-level API is problematic to implement.
689/// A good example would be OpenType layout tables (GPOS/GSUB).
690#[allow(missing_docs)]
691#[allow(missing_debug_implementations)]
692#[derive(Clone)]
693pub struct FaceTables<'a> {
694    // Mandatory tables.
695    pub head: head::Table,
696    pub hhea: hhea::Table,
697    pub maxp: maxp::Table,
698
699    pub cbdt: Option<cbdt::Table<'a>>,
700    pub cff:  Option<cff::Table<'a>>,
701    pub cmap: Option<cmap::Table<'a>>,
702    pub glyf: Option<glyf::Table<'a>>,
703    pub hmtx: Option<hmtx::Table<'a>>,
704    pub kern: Option<kern::Table<'a>>,
705    pub name: Option<name::Table<'a>>,
706    pub os2:  Option<os2::Table<'a>>,
707    pub post: Option<post::Table<'a>>,
708    pub sbix: Option<sbix::Table<'a>>,
709    pub svg:  Option<svg::Table<'a>>,
710    pub vhea: Option<vhea::Table>,
711    pub vmtx: Option<hmtx::Table<'a>>,
712    pub vorg: Option<vorg::Table<'a>>,
713
714    #[cfg(feature = "opentype-layout")] pub gdef: Option<gdef::Table<'a>>,
715    #[cfg(feature = "opentype-layout")] pub gpos: Option<opentype_layout::LayoutTable<'a>>,
716    #[cfg(feature = "opentype-layout")] pub gsub: Option<opentype_layout::LayoutTable<'a>>,
717
718    #[cfg(feature = "apple-layout")] pub ankr: Option<ankr::Table<'a>>,
719    #[cfg(feature = "apple-layout")] pub feat: Option<feat::Table<'a>>,
720    #[cfg(feature = "apple-layout")] pub kerx: Option<kerx::Table<'a>>,
721    #[cfg(feature = "apple-layout")] pub morx: Option<morx::Table<'a>>,
722    #[cfg(feature = "apple-layout")] pub trak: Option<trak::Table<'a>>,
723
724    #[cfg(feature = "variable-fonts")] pub avar: Option<avar::Table<'a>>,
725    #[cfg(feature = "variable-fonts")] pub cff2: Option<cff2::Table<'a>>,
726    #[cfg(feature = "variable-fonts")] pub fvar: Option<fvar::Table<'a>>,
727    #[cfg(feature = "variable-fonts")] pub gvar: Option<gvar::Table<'a>>,
728    #[cfg(feature = "variable-fonts")] pub hvar: Option<hvar::Table<'a>>,
729    #[cfg(feature = "variable-fonts")] pub mvar: Option<mvar::Table<'a>>,
730    #[cfg(feature = "variable-fonts")] pub vvar: Option<hvar::Table<'a>>,
731}
732
733/// A font face.
734///
735/// Provides a high-level API for working with TrueType fonts.
736/// If you're not familiar with how TrueType works internally, you should use this type.
737/// If you do know and want a bit more low-level access - checkout [`FaceTables`].
738///
739/// Note that `Face` doesn't own the font data and doesn't allocate anything in heap.
740/// Therefore you cannot "store" it. The idea is that you should parse the `Face`
741/// when needed, get required data and forget about it.
742/// That's why the initial parsing is highly optimized and should not become a bottleneck.
743///
744/// If you still want to store `Face` - checkout
745/// [owned_ttf_parser](https://crates.io/crates/owned_ttf_parser). Requires `unsafe`.
746///
747/// While `Face` is technically copyable, we disallow it because it's almost 2KB big.
748#[derive(Clone)]
749pub struct Face<'a> {
750    raw_face: RawFace<'a>,
751    tables: FaceTables<'a>, // Parsed tables.
752    #[cfg(feature = "variable-fonts")] coordinates: VarCoords,
753}
754
755impl<'a> Face<'a> {
756    /// Creates a new [`Face`] from a raw data.
757    ///
758    /// `index` indicates the specific font face in a font collection.
759    /// Use [`fonts_in_collection`] to get the total number of font faces.
760    /// Set to 0 if unsure.
761    ///
762    /// This method will do some parsing and sanitization,
763    /// but in general can be considered free. No significant performance overhead.
764    ///
765    /// Required tables: `head`, `hhea` and `maxp`.
766    ///
767    /// If an optional table has invalid data it will be skipped.
768    pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
769        let raw_face = RawFace::from_slice(data, index)?;
770        let raw_tables = Self::collect_tables(raw_face);
771
772        #[allow(unused_mut)]
773        let mut face = Face {
774            raw_face,
775            #[cfg(feature = "variable-fonts")] coordinates: VarCoords::default(),
776            tables: Self::parse_tables(raw_tables)?,
777        };
778
779        #[cfg(feature = "variable-fonts")] {
780            if let Some(ref fvar) = face.tables.fvar {
781                face.coordinates.len = fvar.axes.len().min(MAX_VAR_COORDS as u16) as u8;
782            }
783        }
784
785        Ok(face)
786    }
787
788    fn collect_tables(raw_face: RawFace<'a>) -> RawFaceTables<'a> {
789        let mut tables = RawFaceTables::default();
790
791        for record in raw_face.table_records {
792            let start = usize::num_from(record.offset);
793            let end = match start.checked_add(usize::num_from(record.length)) {
794                Some(v) => v,
795                None => continue,
796            };
797
798            let table_data = raw_face.data.get(start..end);
799            match &record.table_tag.to_bytes() {
800                b"CBDT" => tables.cbdt = table_data,
801                b"CBLC" => tables.cblc = table_data,
802                b"CFF " => tables.cff = table_data,
803                #[cfg(feature = "variable-fonts")]
804                b"CFF2" => tables.cff2 = table_data,
805                #[cfg(feature = "opentype-layout")]
806                b"GDEF" => tables.gdef = table_data,
807                #[cfg(feature = "opentype-layout")]
808                b"GPOS" => tables.gpos = table_data,
809                #[cfg(feature = "opentype-layout")]
810                b"GSUB" => tables.gsub = table_data,
811                #[cfg(feature = "variable-fonts")]
812                b"HVAR" => tables.hvar = table_data,
813                #[cfg(feature = "variable-fonts")]
814                b"MVAR" => tables.mvar = table_data,
815                b"OS/2" => tables.os2 = table_data,
816                b"SVG " => tables.svg = table_data,
817                b"VORG" => tables.vorg = table_data,
818                #[cfg(feature = "variable-fonts")]
819                b"VVAR" => tables.vvar = table_data,
820                #[cfg(feature = "apple-layout")]
821                b"ankr" => tables.ankr = table_data,
822                #[cfg(feature = "variable-fonts")]
823                b"avar" => tables.avar = table_data,
824                b"cmap" => tables.cmap = table_data,
825                #[cfg(feature = "apple-layout")]
826                b"feat" => tables.feat = table_data,
827                #[cfg(feature = "variable-fonts")]
828                b"fvar" => tables.fvar = table_data,
829                b"glyf" => tables.glyf = table_data,
830                #[cfg(feature = "variable-fonts")]
831                b"gvar" => tables.gvar = table_data,
832                b"head" => tables.head = table_data.unwrap_or_default(),
833                b"hhea" => tables.hhea = table_data.unwrap_or_default(),
834                b"hmtx" => tables.hmtx = table_data,
835                b"kern" => tables.kern = table_data,
836                #[cfg(feature = "apple-layout")]
837                b"kerx" => tables.kerx = table_data,
838                b"loca" => tables.loca = table_data,
839                b"maxp" => tables.maxp = table_data.unwrap_or_default(),
840                #[cfg(feature = "apple-layout")]
841                b"morx" => tables.morx = table_data,
842                b"name" => tables.name = table_data,
843                b"post" => tables.post = table_data,
844                b"sbix" => tables.sbix = table_data,
845                #[cfg(feature = "apple-layout")]
846                b"trak" => tables.trak = table_data,
847                b"vhea" => tables.vhea = table_data,
848                b"vmtx" => tables.vmtx = table_data,
849                _ => {}
850            }
851        }
852
853        tables
854    }
855
856    /// Creates a new [`Face`] from provided [`RawFaceTables`].
857    pub fn from_raw_tables(raw_tables: RawFaceTables<'a>) -> Result<Self, FaceParsingError> {
858        #[allow(unused_mut)]
859        let mut face = Face {
860            raw_face: RawFace {
861                data: &[],
862                table_records: LazyArray16::default(),
863            },
864            #[cfg(feature = "variable-fonts")] coordinates: VarCoords::default(),
865            tables: Self::parse_tables(raw_tables)?,
866        };
867
868        #[cfg(feature = "variable-fonts")] {
869            if let Some(ref fvar) = face.tables.fvar {
870                face.coordinates.len = fvar.axes.len().min(MAX_VAR_COORDS as u16) as u8;
871            }
872        }
873
874        Ok(face)
875    }
876
877    fn parse_tables(raw_tables: RawFaceTables<'a>) -> Result<FaceTables<'a>, FaceParsingError> {
878        let head = head::Table::parse(raw_tables.head).ok_or(FaceParsingError::NoHeadTable)?;
879        let hhea = hhea::Table::parse(raw_tables.hhea).ok_or(FaceParsingError::NoHheaTable)?;
880        let maxp = maxp::Table::parse(raw_tables.maxp).ok_or(FaceParsingError::NoMaxpTable)?;
881
882        let hmtx = raw_tables.hmtx.and_then(|data|
883            hmtx::Table::parse(hhea.number_of_metrics, maxp.number_of_glyphs, data)
884        );
885
886        let vhea = raw_tables.vhea.and_then(vhea::Table::parse);
887        let vmtx = if let Some(vhea) = vhea {
888            raw_tables.vmtx.and_then(|data|
889                hmtx::Table::parse(vhea.number_of_metrics, maxp.number_of_glyphs, data)
890            )
891        } else {
892            None
893        };
894
895        let loca = raw_tables.loca.and_then(|data|
896            loca::Table::parse(maxp.number_of_glyphs, head.index_to_location_format, data)
897        );
898        let glyf = if let Some(loca) = loca {
899            raw_tables.glyf.and_then(|data| glyf::Table::parse(loca, data))
900        } else {
901            None
902        };
903
904        let cbdt = if let Some(cblc) = raw_tables.cblc.and_then(cblc::Table::parse) {
905            raw_tables.cbdt.and_then(|data| cbdt::Table::parse(cblc, data))
906        } else {
907            None
908        };
909
910        Ok(FaceTables {
911            head,
912            hhea,
913            maxp,
914
915            cbdt,
916            cff:  raw_tables.cff.and_then(cff::Table::parse),
917            cmap: raw_tables.cmap.and_then(cmap::Table::parse),
918            glyf,
919            hmtx,
920            kern: raw_tables.kern.and_then(kern::Table::parse),
921            name: raw_tables.name.and_then(name::Table::parse),
922            os2:  raw_tables.os2.and_then(os2::Table::parse),
923            post: raw_tables.post.and_then(post::Table::parse),
924            sbix: raw_tables.sbix.and_then(|data| sbix::Table::parse(maxp.number_of_glyphs, data)),
925            svg:  raw_tables.svg.and_then(svg::Table::parse),
926            vhea: raw_tables.vhea.and_then(vhea::Table::parse),
927            vmtx,
928            vorg: raw_tables.vorg.and_then(vorg::Table::parse),
929
930            #[cfg(feature = "opentype-layout")] gdef: raw_tables.gdef.and_then(gdef::Table::parse),
931            #[cfg(feature = "opentype-layout")] gpos: raw_tables.gpos.and_then(opentype_layout::LayoutTable::parse),
932            #[cfg(feature = "opentype-layout")] gsub: raw_tables.gsub.and_then(opentype_layout::LayoutTable::parse),
933
934            #[cfg(feature = "apple-layout")] ankr: raw_tables.ankr
935                .and_then(|data| ankr::Table::parse(maxp.number_of_glyphs, data)),
936            #[cfg(feature = "apple-layout")] feat: raw_tables.feat.and_then(feat::Table::parse),
937            #[cfg(feature = "apple-layout")] kerx: raw_tables.kerx
938                .and_then(|data| kerx::Table::parse(maxp.number_of_glyphs, data)),
939            #[cfg(feature = "apple-layout")] morx: raw_tables.morx
940                .and_then(|data| morx::Table::parse(maxp.number_of_glyphs, data)),
941            #[cfg(feature = "apple-layout")] trak: raw_tables.trak.and_then(trak::Table::parse),
942
943            #[cfg(feature = "variable-fonts")] avar: raw_tables.avar.and_then(avar::Table::parse),
944            #[cfg(feature = "variable-fonts")] cff2: raw_tables.cff2.and_then(cff2::Table::parse),
945            #[cfg(feature = "variable-fonts")] fvar: raw_tables.fvar.and_then(fvar::Table::parse),
946            #[cfg(feature = "variable-fonts")] gvar: raw_tables.gvar.and_then(gvar::Table::parse),
947            #[cfg(feature = "variable-fonts")] hvar: raw_tables.hvar.and_then(hvar::Table::parse),
948            #[cfg(feature = "variable-fonts")] mvar: raw_tables.mvar.and_then(mvar::Table::parse),
949            #[cfg(feature = "variable-fonts")] vvar: raw_tables.vvar.and_then(hvar::Table::parse),
950        })
951    }
952
953    /// Returns low-level face tables.
954    #[inline]
955    pub fn tables(&self) -> &FaceTables<'a> {
956        &self.tables
957    }
958
959    /// Returns the raw data of a selected table.
960    ///
961    /// Useful if you want to parse the data manually.
962    ///
963    /// Available only for faces created using [`Face::from_slice()`](struct.Face.html#method.from_slice).
964    #[inline]
965    pub fn table_data(&self, tag: Tag) -> Option<&'a [u8]> {
966        self.raw_face.table(tag)
967    }
968
969    /// Returns a list of names.
970    ///
971    /// Contains face name and other strings.
972    #[inline]
973    pub fn names(&self) -> name::Names<'a> {
974        self.tables.name.unwrap_or_default().names
975    }
976
977    /// Checks that face is marked as *Regular*.
978    ///
979    /// Returns `false` when OS/2 table is not present.
980    #[inline]
981    pub fn is_regular(&self) -> bool {
982        self.tables.os2.map(|s| s.style() == Style::Normal).unwrap_or(false)
983    }
984
985    /// Checks that face is marked as *Italic*.
986    ///
987    /// Returns `false` when OS/2 table is not present.
988    #[inline]
989    pub fn is_italic(&self) -> bool {
990        self.tables.os2.map(|s| s.style() == Style::Italic).unwrap_or(false)
991    }
992
993    /// Checks that face is marked as *Bold*.
994    ///
995    /// Returns `false` when OS/2 table is not present.
996    #[inline]
997    pub fn is_bold(&self) -> bool {
998        try_opt_or!(self.tables.os2, false).is_bold()
999    }
1000
1001    /// Checks that face is marked as *Oblique*.
1002    ///
1003    /// Returns `false` when OS/2 table is not present or when its version is < 4.
1004    #[inline]
1005    pub fn is_oblique(&self) -> bool {
1006        self.tables.os2.map(|s| s.style() == Style::Oblique).unwrap_or(false)
1007    }
1008
1009    /// Returns face style.
1010    #[inline]
1011    pub fn style(&self) -> Style {
1012        try_opt_or!(self.tables.os2, Style::Normal).style()
1013    }
1014
1015    /// Checks that face is marked as *Monospaced*.
1016    ///
1017    /// Returns `false` when `post` table is not present.
1018    #[inline]
1019    pub fn is_monospaced(&self) -> bool {
1020        try_opt_or!(self.tables.post, false).is_monospaced
1021    }
1022
1023    /// Checks that face is variable.
1024    ///
1025    /// Simply checks the presence of a `fvar` table.
1026    #[inline]
1027    pub fn is_variable(&self) -> bool {
1028        #[cfg(feature = "variable-fonts")] {
1029            // `fvar::Table::parse` already checked that `axisCount` is non-zero.
1030            self.tables.fvar.is_some()
1031        }
1032
1033        #[cfg(not(feature = "variable-fonts"))] {
1034            false
1035        }
1036    }
1037
1038    /// Returns face's weight.
1039    ///
1040    /// Returns `Weight::Normal` when OS/2 table is not present.
1041    #[inline]
1042    pub fn weight(&self) -> Weight {
1043        try_opt_or!(self.tables.os2, Weight::default()).weight()
1044    }
1045
1046    /// Returns face's width.
1047    ///
1048    /// Returns `Width::Normal` when OS/2 table is not present or when value is invalid.
1049    #[inline]
1050    pub fn width(&self) -> Width {
1051        try_opt_or!(self.tables.os2, Width::default()).width()
1052    }
1053
1054    /// Returns face's italic angle.
1055    ///
1056    /// Returns `None` when `post` table is not present.
1057    #[inline]
1058    pub fn italic_angle(&self) -> Option<f32> {
1059        self.tables.post.map(|table| table.italic_angle)
1060    }
1061
1062    // Read https://github.com/freetype/freetype/blob/49270c17011491227ec7bd3fb73ede4f674aa065/src/sfnt/sfobjs.c#L1279
1063    // to learn more about the logic behind the following functions.
1064
1065    /// Returns a horizontal face ascender.
1066    ///
1067    /// This method is affected by variation axes.
1068    #[inline]
1069    pub fn ascender(&self) -> i16 {
1070        if let Some(os_2) = self.tables.os2 {
1071            if os_2.use_typographic_metrics() {
1072                let value = os_2.typographic_ascender();
1073                return self.apply_metrics_variation(Tag::from_bytes(b"hasc"), value);
1074            }
1075        }
1076
1077        let mut value = self.tables.hhea.ascender;
1078        if value == 0 {
1079            if let Some(os_2) = self.tables.os2 {
1080                value = os_2.typographic_ascender();
1081                if value == 0 {
1082                    value = os_2.windows_ascender();
1083                    value = self.apply_metrics_variation(Tag::from_bytes(b"hcla"), value);
1084                } else {
1085                    value = self.apply_metrics_variation(Tag::from_bytes(b"hasc"), value);
1086                }
1087            }
1088        }
1089
1090        value
1091    }
1092
1093    /// Returns a horizontal face descender.
1094    ///
1095    /// This method is affected by variation axes.
1096    #[inline]
1097    pub fn descender(&self) -> i16 {
1098        if let Some(os_2) = self.tables.os2 {
1099            if os_2.use_typographic_metrics() {
1100                let value = os_2.typographic_descender();
1101                return self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), value);
1102            }
1103        }
1104
1105        let mut value = self.tables.hhea.descender;
1106        if value == 0 {
1107            if let Some(os_2) = self.tables.os2 {
1108                value = os_2.typographic_descender();
1109                if value == 0 {
1110                    value = os_2.windows_descender();
1111                    value = self.apply_metrics_variation(Tag::from_bytes(b"hcld"), value);
1112                } else {
1113                    value = self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), value);
1114                }
1115            }
1116        }
1117
1118        value
1119    }
1120
1121    /// Returns face's height.
1122    ///
1123    /// This method is affected by variation axes.
1124    #[inline]
1125    pub fn height(&self) -> i16 {
1126        self.ascender() - self.descender()
1127    }
1128
1129    /// Returns a horizontal face line gap.
1130    ///
1131    /// This method is affected by variation axes.
1132    #[inline]
1133    pub fn line_gap(&self) -> i16 {
1134        if let Some(os_2) = self.tables.os2 {
1135            if os_2.use_typographic_metrics() {
1136                let value = os_2.typographic_line_gap();
1137                return self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), value);
1138            }
1139        }
1140
1141        let mut value = self.tables.hhea.line_gap;
1142        // For line gap, we have to check that ascender or descender are 0, not line gap itself.
1143        if self.tables.hhea.ascender == 0 || self.tables.hhea.descender == 0 {
1144            if let Some(os_2) = self.tables.os2 {
1145                if os_2.typographic_ascender() != 0 || os_2.typographic_descender() != 0 {
1146                    value = os_2.typographic_line_gap();
1147                    value = self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), value);
1148                } else {
1149                    value = 0;
1150                }
1151            }
1152        }
1153
1154        value
1155    }
1156
1157    /// Returns a horizontal typographic face ascender.
1158    ///
1159    /// Prefer `Face::ascender` unless you explicitly want this. This is a more
1160    /// low-level alternative.
1161    ///
1162    /// This method is affected by variation axes.
1163    ///
1164    /// Returns `None` when OS/2 table is not present.
1165    #[inline]
1166    pub fn typographic_ascender(&self) -> Option<i16> {
1167        self.tables.os2.map(|table| {
1168            let v = table.typographic_ascender();
1169            self.apply_metrics_variation(Tag::from_bytes(b"hasc"), v)
1170        })
1171    }
1172
1173    /// Returns a horizontal typographic face descender.
1174    ///
1175    /// Prefer `Face::descender` unless you explicitly want this. This is a more
1176    /// low-level alternative.
1177    ///
1178    /// This method is affected by variation axes.
1179    ///
1180    /// Returns `None` when OS/2 table is not present.
1181    #[inline]
1182    pub fn typographic_descender(&self) -> Option<i16> {
1183        self.tables.os2.map(|table| {
1184            let v = table.typographic_descender();
1185            self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), v)
1186        })
1187    }
1188
1189    /// Returns a horizontal typographic face line gap.
1190    ///
1191    /// Prefer `Face::line_gap` unless you explicitly want this. This is a more
1192    /// low-level alternative.
1193    ///
1194    /// This method is affected by variation axes.
1195    ///
1196    /// Returns `None` when OS/2 table is not present.
1197    #[inline]
1198    pub fn typographic_line_gap(&self) -> Option<i16> {
1199        self.tables.os2.map(|table| {
1200            let v = table.typographic_line_gap();
1201            self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), v)
1202        })
1203    }
1204
1205    /// Returns a vertical face ascender.
1206    ///
1207    /// This method is affected by variation axes.
1208    #[inline]
1209    pub fn vertical_ascender(&self) -> Option<i16> {
1210        self.tables.vhea.map(|vhea| vhea.ascender)
1211            .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vasc"), v))
1212    }
1213
1214    /// Returns a vertical face descender.
1215    ///
1216    /// This method is affected by variation axes.
1217    #[inline]
1218    pub fn vertical_descender(&self) -> Option<i16> {
1219        self.tables.vhea.map(|vhea| vhea.descender)
1220            .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vdsc"), v))
1221    }
1222
1223    /// Returns a vertical face height.
1224    ///
1225    /// This method is affected by variation axes.
1226    #[inline]
1227    pub fn vertical_height(&self) -> Option<i16> {
1228        Some(self.vertical_ascender()? - self.vertical_descender()?)
1229    }
1230
1231    /// Returns a vertical face line gap.
1232    ///
1233    /// This method is affected by variation axes.
1234    #[inline]
1235    pub fn vertical_line_gap(&self) -> Option<i16> {
1236        self.tables.vhea.map(|vhea| vhea.line_gap)
1237            .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vlgp"), v))
1238    }
1239
1240    /// Returns face's units per EM.
1241    ///
1242    /// Guarantee to be in a 16..=16384 range.
1243    #[inline]
1244    pub fn units_per_em(&self) -> u16 {
1245        self.tables.head.units_per_em
1246    }
1247
1248    /// Returns face's x height.
1249    ///
1250    /// This method is affected by variation axes.
1251    ///
1252    /// Returns `None` when OS/2 table is not present or when its version is < 2.
1253    #[inline]
1254    pub fn x_height(&self) -> Option<i16> {
1255        self.tables.os2.and_then(|os_2| os_2.x_height())
1256            .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"xhgt"), v))
1257    }
1258
1259    /// Returns face's capital height.
1260    ///
1261    /// This method is affected by variation axes.
1262    ///
1263    /// Returns `None` when OS/2 table is not present or when its version is < 2.
1264    #[inline]
1265    pub fn capital_height(&self) -> Option<i16> {
1266        self.tables.os2.and_then(|os_2| os_2.capital_height())
1267            .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"cpht"), v))
1268    }
1269
1270    /// Returns face's underline metrics.
1271    ///
1272    /// This method is affected by variation axes.
1273    ///
1274    /// Returns `None` when `post` table is not present.
1275    #[inline]
1276    pub fn underline_metrics(&self) -> Option<LineMetrics> {
1277        let mut metrics = self.tables.post?.underline_metrics;
1278
1279        if self.is_variable() {
1280            self.apply_metrics_variation_to(Tag::from_bytes(b"undo"), &mut metrics.position);
1281            self.apply_metrics_variation_to(Tag::from_bytes(b"unds"), &mut metrics.thickness);
1282        }
1283
1284        Some(metrics)
1285    }
1286
1287    /// Returns face's strikeout metrics.
1288    ///
1289    /// This method is affected by variation axes.
1290    ///
1291    /// Returns `None` when OS/2 table is not present.
1292    #[inline]
1293    pub fn strikeout_metrics(&self) -> Option<LineMetrics> {
1294        let mut metrics = self.tables.os2?.strikeout_metrics();
1295
1296        if self.is_variable() {
1297            self.apply_metrics_variation_to(Tag::from_bytes(b"stro"), &mut metrics.position);
1298            self.apply_metrics_variation_to(Tag::from_bytes(b"strs"), &mut metrics.thickness);
1299        }
1300
1301        Some(metrics)
1302    }
1303
1304    /// Returns face's subscript metrics.
1305    ///
1306    /// This method is affected by variation axes.
1307    ///
1308    /// Returns `None` when OS/2 table is not present.
1309    #[inline]
1310    pub fn subscript_metrics(&self) -> Option<ScriptMetrics> {
1311        let mut metrics = self.tables.os2?.subscript_metrics();
1312
1313        if self.is_variable() {
1314            self.apply_metrics_variation_to(Tag::from_bytes(b"sbxs"), &mut metrics.x_size);
1315            self.apply_metrics_variation_to(Tag::from_bytes(b"sbys"), &mut metrics.y_size);
1316            self.apply_metrics_variation_to(Tag::from_bytes(b"sbxo"), &mut metrics.x_offset);
1317            self.apply_metrics_variation_to(Tag::from_bytes(b"sbyo"), &mut metrics.y_offset);
1318        }
1319
1320        Some(metrics)
1321    }
1322
1323    /// Returns face's superscript metrics.
1324    ///
1325    /// This method is affected by variation axes.
1326    ///
1327    /// Returns `None` when OS/2 table is not present.
1328    #[inline]
1329    pub fn superscript_metrics(&self) -> Option<ScriptMetrics> {
1330        let mut metrics = self.tables.os2?.superscript_metrics();
1331
1332        if self.is_variable() {
1333            self.apply_metrics_variation_to(Tag::from_bytes(b"spxs"), &mut metrics.x_size);
1334            self.apply_metrics_variation_to(Tag::from_bytes(b"spys"), &mut metrics.y_size);
1335            self.apply_metrics_variation_to(Tag::from_bytes(b"spxo"), &mut metrics.x_offset);
1336            self.apply_metrics_variation_to(Tag::from_bytes(b"spyo"), &mut metrics.y_offset);
1337        }
1338
1339        Some(metrics)
1340    }
1341
1342    /// Returns a total number of glyphs in the face.
1343    ///
1344    /// Never zero.
1345    ///
1346    /// The value was already parsed, so this function doesn't involve any parsing.
1347    #[inline]
1348    pub fn number_of_glyphs(&self) -> u16 {
1349        self.tables.maxp.number_of_glyphs.get()
1350    }
1351
1352    /// Resolves a Glyph ID for a code point.
1353    ///
1354    /// Returns `None` instead of `0` when glyph is not found.
1355    ///
1356    /// All subtable formats except Mixed Coverage (8) are supported.
1357    ///
1358    /// If you need a more low-level control, prefer `Face::tables().cmap`.
1359    #[inline]
1360    pub fn glyph_index(&self, code_point: char) -> Option<GlyphId> {
1361        for encoding in self.tables.cmap?.subtables {
1362            if !encoding.is_unicode() {
1363                continue;
1364            }
1365
1366            if let Some(id) = encoding.glyph_index(u32::from(code_point)) {
1367                return Some(id);
1368            }
1369        }
1370
1371        None
1372    }
1373
1374    /// Resolves a variation of a Glyph ID from two code points.
1375    ///
1376    /// Implemented according to
1377    /// [Unicode Variation Sequences](
1378    /// https://docs.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences).
1379    ///
1380    /// Returns `None` instead of `0` when glyph is not found.
1381    #[inline]
1382    pub fn glyph_variation_index(&self, code_point: char, variation: char) -> Option<GlyphId> {
1383        for subtable in self.tables.cmap?.subtables {
1384            if let cmap::Format::UnicodeVariationSequences(ref table) = subtable.format {
1385                return match table.glyph_index(u32::from(code_point), u32::from(variation))? {
1386                    cmap::GlyphVariationResult::Found(v) => Some(v),
1387                    cmap::GlyphVariationResult::UseDefault => self.glyph_index(code_point),
1388                };
1389            }
1390        }
1391
1392        None
1393    }
1394
1395    /// Returns glyph's horizontal advance.
1396    ///
1397    /// This method is affected by variation axes.
1398    #[inline]
1399    pub fn glyph_hor_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1400        #[cfg(feature = "variable-fonts")] {
1401            let mut advance = self.tables.hmtx?.advance(glyph_id)? as f32;
1402
1403            if self.is_variable() {
1404                // Ignore variation offset when `hvar` is not set.
1405                if let Some(hvar) = self.tables.hvar {
1406                    if let Some(offset) = hvar.advance_offset(glyph_id, self.coords()) {
1407                        // We can't use `round()` in `no_std`, so this is the next best thing.
1408                        advance += offset + 0.5;
1409                    }
1410                }
1411            }
1412
1413            u16::try_num_from(advance)
1414        }
1415
1416        #[cfg(not(feature = "variable-fonts"))] {
1417            self.tables.hmtx?.advance(glyph_id)
1418        }
1419    }
1420
1421    /// Returns glyph's vertical advance.
1422    ///
1423    /// This method is affected by variation axes.
1424    #[inline]
1425    pub fn glyph_ver_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1426        #[cfg(feature = "variable-fonts")] {
1427            let mut advance = self.tables.vmtx?.advance(glyph_id)? as f32;
1428
1429            if self.is_variable() {
1430                // Ignore variation offset when `vvar` is not set.
1431                if let Some(vvar) = self.tables.vvar {
1432                    if let Some(offset) = vvar.advance_offset(glyph_id, self.coords()) {
1433                        // We can't use `round()` in `no_std`, so this is the next best thing.
1434                        advance += offset + 0.5;
1435                    }
1436                }
1437            }
1438
1439            u16::try_num_from(advance)
1440        }
1441
1442        #[cfg(not(feature = "variable-fonts"))] {
1443            self.tables.vmtx?.advance(glyph_id)
1444        }
1445    }
1446
1447    /// Returns glyph's horizontal side bearing.
1448    ///
1449    /// This method is affected by variation axes.
1450    #[inline]
1451    pub fn glyph_hor_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1452        #[cfg(feature = "variable-fonts")] {
1453            let mut bearing = self.tables.hmtx?.side_bearing(glyph_id)? as f32;
1454
1455            if self.is_variable() {
1456                // Ignore variation offset when `hvar` is not set.
1457                if let Some(hvar) = self.tables.hvar {
1458                    if let Some(offset) = hvar.side_bearing_offset(glyph_id, self.coords()) {
1459                        // We can't use `round()` in `no_std`, so this is the next best thing.
1460                        bearing += offset + 0.5;
1461                    }
1462                }
1463            }
1464
1465            i16::try_num_from(bearing)
1466        }
1467
1468        #[cfg(not(feature = "variable-fonts"))] {
1469            self.tables.hmtx?.side_bearing(glyph_id)
1470        }
1471    }
1472
1473    /// Returns glyph's vertical side bearing.
1474    ///
1475    /// This method is affected by variation axes.
1476    #[inline]
1477    pub fn glyph_ver_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1478        #[cfg(feature = "variable-fonts")] {
1479            let mut bearing = self.tables.vmtx?.side_bearing(glyph_id)? as f32;
1480
1481            if self.is_variable() {
1482                // Ignore variation offset when `vvar` is not set.
1483                if let Some(vvar) = self.tables.vvar {
1484                    if let Some(offset) = vvar.side_bearing_offset(glyph_id, self.coords()) {
1485                        // We can't use `round()` in `no_std`, so this is the next best thing.
1486                        bearing += offset + 0.5;
1487                    }
1488                }
1489            }
1490
1491            i16::try_num_from(bearing)
1492        }
1493
1494        #[cfg(not(feature = "variable-fonts"))] {
1495            self.tables.vmtx?.side_bearing(glyph_id)
1496        }
1497    }
1498
1499    /// Returns glyph's vertical origin according to
1500    /// [Vertical Origin Table](https://docs.microsoft.com/en-us/typography/opentype/spec/vorg).
1501    pub fn glyph_y_origin(&self, glyph_id: GlyphId) -> Option<i16> {
1502        self.tables.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))
1503    }
1504
1505    /// Returns glyph's name.
1506    ///
1507    /// Uses the `post` and `CFF` tables as sources.
1508    ///
1509    /// Returns `None` when no name is associated with a `glyph`.
1510    #[cfg(feature = "glyph-names")]
1511    #[inline]
1512    pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&str> {
1513        if let Some(name) = self.tables.post.and_then(|post| post.names.get(glyph_id)) {
1514            return Some(name);
1515        }
1516
1517        if let Some(name) = self.tables.cff.as_ref().and_then(|cff1| cff1.glyph_name(glyph_id)) {
1518            return Some(name);
1519        }
1520
1521        None
1522    }
1523
1524    /// Outlines a glyph and returns its tight bounding box.
1525    ///
1526    /// **Warning**: since `ttf-parser` is a pull parser,
1527    /// `OutlineBuilder` will emit segments even when outline is partially malformed.
1528    /// You must check `outline_glyph()` result before using
1529    /// `OutlineBuilder`'s output.
1530    ///
1531    /// `gvar`, `glyf`, `CFF` and `CFF2` tables are supported.
1532    /// And they will be accesses in this specific order.
1533    ///
1534    /// This method is affected by variation axes.
1535    ///
1536    /// Returns `None` when glyph has no outline or on error.
1537    ///
1538    /// # Example
1539    ///
1540    /// ```
1541    /// use std::fmt::Write;
1542    /// use ttf_parser;
1543    ///
1544    /// struct Builder(String);
1545    ///
1546    /// impl ttf_parser::OutlineBuilder for Builder {
1547    ///     fn move_to(&mut self, x: f32, y: f32) {
1548    ///         write!(&mut self.0, "M {} {} ", x, y).unwrap();
1549    ///     }
1550    ///
1551    ///     fn line_to(&mut self, x: f32, y: f32) {
1552    ///         write!(&mut self.0, "L {} {} ", x, y).unwrap();
1553    ///     }
1554    ///
1555    ///     fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
1556    ///         write!(&mut self.0, "Q {} {} {} {} ", x1, y1, x, y).unwrap();
1557    ///     }
1558    ///
1559    ///     fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
1560    ///         write!(&mut self.0, "C {} {} {} {} {} {} ", x1, y1, x2, y2, x, y).unwrap();
1561    ///     }
1562    ///
1563    ///     fn close(&mut self) {
1564    ///         write!(&mut self.0, "Z ").unwrap();
1565    ///     }
1566    /// }
1567    ///
1568    /// let data = std::fs::read("tests/fonts/demo.ttf").unwrap();
1569    /// let face = ttf_parser::Face::from_slice(&data, 0).unwrap();
1570    /// let mut builder = Builder(String::new());
1571    /// let bbox = face.outline_glyph(ttf_parser::GlyphId(1), &mut builder).unwrap();
1572    /// assert_eq!(builder.0, "M 173 267 L 369 267 L 270 587 L 173 267 Z M 6 0 L 224 656 \
1573    ///                        L 320 656 L 541 0 L 452 0 L 390 200 L 151 200 L 85 0 L 6 0 Z ");
1574    /// assert_eq!(bbox, ttf_parser::Rect { x_min: 6, y_min: 0, x_max: 541, y_max: 656 });
1575    /// ```
1576    #[inline]
1577    pub fn outline_glyph(
1578        &self,
1579        glyph_id: GlyphId,
1580        builder: &mut dyn OutlineBuilder,
1581    ) -> Option<Rect> {
1582        #[cfg(feature = "variable-fonts")] {
1583            if let Some(ref gvar) = self.tables.gvar {
1584                return gvar.outline(self.tables.glyf?, self.coords(), glyph_id, builder);
1585            }
1586        }
1587
1588        if let Some(table) = self.tables.glyf {
1589            return table.outline(glyph_id, builder);
1590        }
1591
1592        if let Some(ref cff) = self.tables.cff {
1593            return cff.outline(glyph_id, builder).ok();
1594        }
1595
1596        #[cfg(feature = "variable-fonts")] {
1597            if let Some(ref cff2) = self.tables.cff2 {
1598                return cff2.outline(self.coords(), glyph_id, builder).ok();
1599            }
1600        }
1601
1602        None
1603    }
1604
1605    /// Returns a tight glyph bounding box.
1606    ///
1607    /// This is just a shorthand for `outline_glyph()` since only the `glyf` table stores
1608    /// a bounding box. We ignore `glyf` table bboxes because they can be malformed.
1609    /// In case of CFF and variable fonts we have to actually outline
1610    /// a glyph to find it's bounding box.
1611    ///
1612    /// When a glyph is defined by a raster or a vector image,
1613    /// that can be obtained via `glyph_image()`,
1614    /// the bounding box must be calculated manually and this method will return `None`.
1615    ///
1616    /// Note: the returned bbox is not validated in any way. A font file can have a glyph bbox
1617    /// set to zero/negative width and/or height and this is perfectly ok.
1618    /// For calculated bboxes, zero width and/or height is also perfectly fine.
1619    ///
1620    /// This method is affected by variation axes.
1621    #[inline]
1622    pub fn glyph_bounding_box(&self, glyph_id: GlyphId) -> Option<Rect> {
1623        self.outline_glyph(glyph_id, &mut DummyOutline)
1624    }
1625
1626    /// Returns a bounding box that large enough to enclose any glyph from the face.
1627    #[inline]
1628    pub fn global_bounding_box(&self) -> Rect {
1629        self.tables.head.global_bbox
1630    }
1631
1632    /// Returns a reference to a glyph's raster image.
1633    ///
1634    /// A font can define a glyph using a raster or a vector image instead of a simple outline.
1635    /// Which is primarily used for emojis. This method should be used to access raster images.
1636    ///
1637    /// `pixels_per_em` allows selecting a preferred image size. The chosen size will
1638    /// be closer to an upper one. So when font has 64px and 96px images and `pixels_per_em`
1639    /// is set to 72, 96px image will be returned.
1640    /// To get the largest image simply use `std::u16::MAX`.
1641    ///
1642    /// Note that this method will return an encoded image. It should be decoded
1643    /// by the caller. We don't validate or preprocess it in any way.
1644    ///
1645    /// Currently, only PNG images are supported.
1646    ///
1647    /// Also, a font can contain both: images and outlines. So when this method returns `None`
1648    /// you should also try `outline_glyph()` afterwards.
1649    ///
1650    /// There are multiple ways an image can be stored in a TrueType font
1651    /// and this method supports only `sbix`, `CBLC`+`CBDT`.
1652    /// Font's tables be accesses in this specific order.
1653    #[inline]
1654    pub fn glyph_raster_image(&self, glyph_id: GlyphId, pixels_per_em: u16) -> Option<RasterGlyphImage> {
1655        if let Some(table) = self.tables.sbix {
1656            if let Some(strike) = table.best_strike(pixels_per_em) {
1657                return strike.get(glyph_id);
1658            }
1659        }
1660
1661        if let Some(cbdt) = self.tables.cbdt {
1662            return cbdt.get(glyph_id, pixels_per_em);
1663        }
1664
1665        None
1666    }
1667
1668    /// Returns a reference to a glyph's SVG image.
1669    ///
1670    /// A font can define a glyph using a raster or a vector image instead of a simple outline.
1671    /// Which is primarily used for emojis. This method should be used to access SVG images.
1672    ///
1673    /// Note that this method will return just an SVG data. It should be rendered
1674    /// or even decompressed (in case of SVGZ) by the caller.
1675    /// We don't validate or preprocess it in any way.
1676    ///
1677    /// Also, a font can contain both: images and outlines. So when this method returns `None`
1678    /// you should also try `outline_glyph()` afterwards.
1679    #[inline]
1680    pub fn glyph_svg_image(&self, glyph_id: GlyphId) -> Option<&'a [u8]> {
1681        self.tables.svg.and_then(|svg| svg.documents.find(glyph_id))
1682    }
1683
1684    /// Returns an iterator over variation axes.
1685    #[cfg(feature = "variable-fonts")]
1686    #[inline]
1687    pub fn variation_axes(&self) -> LazyArray16<'a, VariationAxis> {
1688        self.tables.fvar.map(|fvar| fvar.axes).unwrap_or_default()
1689    }
1690
1691    /// Sets a variation axis coordinate.
1692    ///
1693    /// This is the only mutable method in the library.
1694    /// We can simplify the API a lot by storing the variable coordinates
1695    /// in the face object itself.
1696    ///
1697    /// Since coordinates are stored on the stack, we allow only 32 of them.
1698    ///
1699    /// Returns `None` when face is not variable or doesn't have such axis.
1700    #[cfg(feature = "variable-fonts")]
1701    pub fn set_variation(&mut self, axis: Tag, value: f32) -> Option<()> {
1702        if !self.is_variable() {
1703            return None;
1704        }
1705
1706        let v = self.variation_axes().into_iter().enumerate().find(|(_, a)| a.tag == axis);
1707        if let Some((idx, a)) = v {
1708            if idx >= MAX_VAR_COORDS {
1709                return None;
1710            }
1711
1712            self.coordinates.data[idx] = a.normalized_value(value);
1713        } else {
1714            return None;
1715        }
1716
1717        // TODO: optimize
1718        if let Some(avar) = self.tables.avar {
1719            // Ignore error.
1720            let _ = avar.map_coordinates(self.coordinates.as_mut_slice());
1721        }
1722
1723        Some(())
1724    }
1725
1726    /// Returns the current normalized variation coordinates.
1727    #[cfg(feature = "variable-fonts")]
1728    #[inline]
1729    pub fn variation_coordinates(&self) -> &[NormalizedCoordinate] {
1730        self.coordinates.as_slice()
1731    }
1732
1733    /// Checks that face has non-default variation coordinates.
1734    #[cfg(feature = "variable-fonts")]
1735    #[inline]
1736    pub fn has_non_default_variation_coordinates(&self) -> bool {
1737        self.coordinates.as_slice().iter().any(|c| c.0 != 0)
1738    }
1739
1740    #[cfg(feature = "variable-fonts")]
1741    #[inline]
1742    fn metrics_var_offset(&self, tag: Tag) -> f32 {
1743        self.tables.mvar.and_then(|table| table.metric_offset(tag, self.coords())).unwrap_or(0.0)
1744    }
1745
1746    #[inline]
1747    fn apply_metrics_variation(&self, tag: Tag, mut value: i16) -> i16 {
1748        self.apply_metrics_variation_to(tag, &mut value);
1749        value
1750    }
1751
1752
1753    #[cfg(feature = "variable-fonts")]
1754    #[inline]
1755    fn apply_metrics_variation_to(&self, tag: Tag, value: &mut i16) {
1756        if self.is_variable() {
1757            let v = f32::from(*value) + self.metrics_var_offset(tag);
1758            // TODO: Should probably round it, but f32::round is not available in core.
1759            if let Some(v) = i16::try_num_from(v) {
1760                *value = v;
1761            }
1762        }
1763    }
1764
1765    #[cfg(not(feature = "variable-fonts"))]
1766    #[inline]
1767    fn apply_metrics_variation_to(&self, _: Tag, _: &mut i16) {
1768    }
1769
1770    #[cfg(feature = "variable-fonts")]
1771    #[inline]
1772    fn coords(&self) -> &[NormalizedCoordinate] {
1773        self.coordinates.as_slice()
1774    }
1775}
1776
1777struct DefaultTableProvider<'a> {
1778    data: &'a [u8],
1779    tables: LazyArrayIter16<'a, TableRecord>,
1780}
1781
1782impl<'a> Iterator for DefaultTableProvider<'a> {
1783    type Item = Result<(Tag, Option<&'a [u8]>), FaceParsingError>;
1784
1785    #[inline]
1786    fn next(&mut self) -> Option<Self::Item> {
1787        self.tables.next().map(|table| {
1788            Ok((table.table_tag, {
1789                let offset = usize::num_from(table.offset);
1790                let length = usize::num_from(table.length);
1791                let end = offset.checked_add(length).ok_or(FaceParsingError::MalformedFont)?;
1792                let range = offset..end;
1793                self.data.get(range)
1794            }))
1795        })
1796    }
1797}
1798
1799impl core::fmt::Debug for Face<'_> {
1800    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1801        write!(f, "Face()")
1802    }
1803}
1804
1805/// Returns the number of fonts stored in a TrueType font collection.
1806///
1807/// Returns `None` if a provided data is not a TrueType font collection.
1808#[inline]
1809pub fn fonts_in_collection(data: &[u8]) -> Option<u32> {
1810    let mut s = Stream::new(data);
1811    if s.read::<Magic>()? != Magic::FontCollection {
1812        return None;
1813    }
1814
1815    s.skip::<u32>(); // version
1816    s.read::<u32>()
1817}