Skip to main content

fontdb/ttf_parser/
mod.rs

1/*!
2A minimal subset of [ttf-parser](https://github.com/harfbuzz/ttf-parser),
3inlined from ttf-parser 0.25.1 (MIT / Apache-2.0).
4
5Contains just enough of the original crate to parse font table records,
6the `name` table and parts of the `OS/2` table.
7*/
8
9mod language;
10pub mod name;
11pub mod os2;
12mod parser;
13
14pub use language::Language;
15pub use name::{name_id, PlatformId};
16pub use os2::{Style, Width};
17pub use parser::LazyArray16;
18
19use parser::{FromData, NumFrom, Offset, Offset32, Stream};
20
21/// A TrueType font magic.
22///
23/// https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
24#[derive(Clone, Copy, PartialEq, Debug)]
25enum Magic {
26    TrueType,
27    OpenType,
28    FontCollection,
29}
30
31impl FromData for Magic {
32    const SIZE: usize = 4;
33
34    #[inline]
35    fn parse(data: &[u8]) -> Option<Self> {
36        match u32::parse(data)? {
37            0x00010000 | 0x74727565 => Some(Magic::TrueType),
38            0x4F54544F => Some(Magic::OpenType),
39            0x74746366 => Some(Magic::FontCollection),
40            _ => None,
41        }
42    }
43}
44
45/// A 4-byte tag.
46#[repr(transparent)]
47#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
48pub struct Tag(pub u32);
49
50impl Tag {
51    /// Creates a `Tag` from bytes.
52    #[inline]
53    pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
54        Tag(((bytes[0] as u32) << 24)
55            | ((bytes[1] as u32) << 16)
56            | ((bytes[2] as u32) << 8)
57            | (bytes[3] as u32))
58    }
59}
60
61impl FromData for Tag {
62    const SIZE: usize = 4;
63
64    #[inline]
65    fn parse(data: &[u8]) -> Option<Self> {
66        u32::parse(data).map(Tag)
67    }
68}
69
70/// A raw table record.
71#[derive(Clone, Copy)]
72struct TableRecord {
73    tag: Tag,
74    offset: u32,
75    length: u32,
76}
77
78impl FromData for TableRecord {
79    const SIZE: usize = 16;
80
81    #[inline]
82    fn parse(data: &[u8]) -> Option<Self> {
83        let mut s = Stream::new(data);
84        let tag = s.read::<Tag>()?;
85        s.skip::<u32>(); // checkSum
86        Some(TableRecord {
87            tag,
88            offset: s.read::<u32>()?,
89            length: s.read::<u32>()?,
90        })
91    }
92}
93
94/// A list of font face parsing errors.
95#[derive(Clone, Copy, PartialEq, Eq, Debug)]
96pub enum FaceParsingError {
97    /// An attempt to read out of bounds detected.
98    ///
99    /// Should occur only on malformed fonts.
100    MalformedFont,
101
102    /// Face data must start with `0x00010000`, `0x74727565`, `0x4F54544F` or `0x74746366`.
103    UnknownMagic,
104
105    /// The face index is larger than the number of faces in the font.
106    FaceIndexOutOfBounds,
107}
108
109/// A raw font face.
110///
111/// Unlike a full font face, `RawFace` parses only face table records.
112/// Meaning all you can get from this type is a raw (`&[u8]`) data of a requested table.
113#[derive(Clone, Copy)]
114pub struct RawFace<'a> {
115    /// The input font file data.
116    pub data: &'a [u8],
117    /// An array of table records.
118    table_records: LazyArray16<'a, TableRecord>,
119}
120
121impl<'a> RawFace<'a> {
122    /// Creates a new [`RawFace`] from a raw data.
123    ///
124    /// `index` indicates the specific font face in a font collection.
125    /// Use [`fonts_in_collection`] to get the total number of font faces.
126    /// Set to 0 if unsure.
127    pub fn parse(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
128        // https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
129
130        let mut s = Stream::new(data);
131
132        // Read **font** magic.
133        let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
134        if magic == Magic::FontCollection {
135            s.skip::<u32>(); // version
136            let number_of_faces = s.read::<u32>().ok_or(FaceParsingError::MalformedFont)?;
137            let offsets = s
138                .read_array32::<Offset32>(number_of_faces)
139                .ok_or(FaceParsingError::MalformedFont)?;
140
141            let face_offset = offsets
142                .get(index)
143                .ok_or(FaceParsingError::FaceIndexOutOfBounds)?;
144            // Face offset is from the start of the font data,
145            // so we have to adjust it to the current parser offset.
146            let face_offset = face_offset
147                .to_usize()
148                .checked_sub(s.offset())
149                .ok_or(FaceParsingError::MalformedFont)?;
150            s.advance_checked(face_offset)
151                .ok_or(FaceParsingError::MalformedFont)?;
152
153            // Read **face** magic.
154            // Each face in a font collection also starts with a magic.
155            let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
156            // And face in a font collection can't be another collection.
157            if magic == Magic::FontCollection {
158                return Err(FaceParsingError::UnknownMagic);
159            }
160        } else {
161            // When reading from a regular font (not a collection) disallow index to be non-zero
162            // Basically treat the font as a one-element collection
163            if index != 0 {
164                return Err(FaceParsingError::FaceIndexOutOfBounds);
165            }
166        }
167
168        let num_tables = s.read::<u16>().ok_or(FaceParsingError::MalformedFont)?;
169        s.advance(6); // searchRange (u16) + entrySelector (u16) + rangeShift (u16)
170        let table_records = s
171            .read_array16::<TableRecord>(num_tables)
172            .ok_or(FaceParsingError::MalformedFont)?;
173
174        Ok(RawFace {
175            data,
176            table_records,
177        })
178    }
179
180    /// Returns the raw data of a selected table.
181    pub fn table(&self, tag: Tag) -> Option<&'a [u8]> {
182        let (_, table) = self
183            .table_records
184            .binary_search_by(|record| record.tag.cmp(&tag))?;
185        let offset = usize::num_from(table.offset);
186        let length = usize::num_from(table.length);
187        let end = offset.checked_add(length)?;
188        self.data.get(offset..end)
189    }
190}
191
192impl core::fmt::Debug for RawFace<'_> {
193    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194        write!(f, "RawFace {{ ... }}")
195    }
196}
197
198/// Returns the number of fonts stored in a TrueType font collection.
199///
200/// Returns `None` if a provided data is not a TrueType font collection.
201#[inline]
202pub fn fonts_in_collection(data: &[u8]) -> Option<u32> {
203    let mut s = Stream::new(data);
204    if s.read::<Magic>()? != Magic::FontCollection {
205        return None;
206    }
207
208    s.skip::<u32>(); // version
209    s.read::<u32>()
210}