makepad_ttf_parser/
lib.rs

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