ttf_parser/
lib.rs

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