swash/internal/
mod.rs

1//! Low level OpenType parsing and internal data types.
2
3#![allow(dead_code)]
4
5pub mod fixed;
6
7pub mod aat;
8pub mod at;
9pub mod cmap;
10pub mod glyf;
11pub mod head;
12pub mod var;
13pub mod vorg;
14pub mod xmtx;
15
16mod parse;
17
18pub use parse::*;
19
20use head::*;
21
22pub type RawTag = u32;
23
24/// Returns a tag value for the specified four bytes.
25pub const fn raw_tag(bytes: &[u8; 4]) -> RawTag {
26    (bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 | bytes[3] as u32
27}
28
29/// Functions for checking the validity of a font file and extracting
30/// fonts from collections.
31pub mod raw_data {
32    use super::{raw_tag, Bytes, RawTag};
33
34    const OTTO: RawTag = raw_tag(b"OTTO");
35    const TTCF: RawTag = raw_tag(b"ttcf");
36    const FONT: RawTag = 0x10000;
37    const TRUE: RawTag = raw_tag(b"true");
38
39    /// Returns true if the data represents a font collection.
40    pub fn is_collection(data: &[u8]) -> bool {
41        Bytes::new(data).read_u32(0) == Some(TTCF)
42    }
43
44    /// Returns true if the data represents a font at the specified offset.
45    pub fn is_font(data: &[u8], offset: u32) -> bool {
46        let tag = Bytes::new(data).read_u32(offset as usize).unwrap_or(0);
47        tag == FONT || tag == OTTO || tag == TRUE
48    }
49
50    /// Returns the number of fonts contained in the specified data.
51    pub fn count(data: &[u8]) -> u32 {
52        if is_collection(data) {
53            Bytes::new(data).read_u32(8).unwrap_or(0)
54        } else if is_font(data, 0) {
55            1
56        } else {
57            0
58        }
59    }
60
61    /// Returns the byte offset for the font at the specified index in the data.
62    pub fn offset(data: &[u8], index: u32) -> Option<u32> {
63        if index >= count(data) {
64            return None;
65        }
66        if is_font(data, 0) {
67            Some(0)
68        } else {
69            Bytes::new(data).read_u32(12 + index as usize * 4)
70        }
71    }
72}
73
74/// Trait for types that can supply font tables.
75pub trait RawFont<'a>: Sized {
76    /// Returns the font data.
77    fn data(&self) -> &'a [u8];
78
79    /// Returns the offset to the table directory.
80    fn offset(&self) -> u32;
81
82    #[cfg(feature = "std")]
83    fn dump_tables(&self) -> Option<()> {
84        let base = self.offset() as usize;
85        let b = Bytes::new(self.data());
86        let len = b.read_u16(base.checked_add(4)?)? as usize;
87        let record_base = base.checked_add(12)?;
88        let reclen = 16usize;
89        for i in 0..len {
90            let recbase = reclen.checked_mul(i)?.checked_add(record_base)?;
91            let mut s = b.stream_at(recbase)?;
92            let table_tag = s.read_u32()?;
93            let tb = table_tag.to_be_bytes();
94            println!("{}", core::str::from_utf8(&tb).unwrap_or("??"));
95        }
96        Some(())
97    }
98
99    /// Returns the range for the table with the specified tag.
100    fn table_range(&self, tag: RawTag) -> Option<(u32, u32)> {
101        let base = self.offset() as usize;
102        let b = Bytes::new(self.data());
103        let len = b.read_u16(base.checked_add(4)?)? as usize;
104        let record_base = base.checked_add(12)?;
105        let reclen = 16usize;
106        let mut l = 0;
107        let mut h = len;
108        while l < h {
109            use core::cmp::Ordering::*;
110            let i = (l + h) / 2;
111            let recbase = reclen.checked_mul(i)?.checked_add(record_base)?;
112            let mut s = b.stream_at(recbase)?;
113            let table_tag = s.read_u32()?;
114            match tag.cmp(&table_tag) {
115                Less => h = i,
116                Greater => l = i + 1,
117                Equal => {
118                    s.skip(4)?;
119                    let start = s.read_u32()?;
120                    let len = s.read_u32()?;
121                    let end = start.checked_add(len)?;
122                    return Some((start, end));
123                }
124            }
125        }
126        None
127    }
128
129    /// Returns the byte offset of the table with the specified tag.
130    fn table_offset(&self, tag: RawTag) -> u32 {
131        self.table_range(tag).map(|r| r.0).unwrap_or(0)
132    }
133
134    /// Returns the data for the table with the specified tag.
135    fn table_data(&self, tag: RawTag) -> Option<&'a [u8]> {
136        let r = self.table_range(tag)?;
137        self.data().get(r.0 as usize..r.1 as usize)
138    }
139
140    /// Returns the font header table.
141    fn head(&self) -> Option<Head<'a>> {
142        Head::from_font(self)
143    }
144
145    /// Returns the OS/2 and Windows metrics table.
146    fn os2(&self) -> Option<Os2<'a>> {
147        Os2::from_font(self)
148    }
149
150    /// Returns the PostScript table.
151    fn post(&self) -> Option<Post<'a>> {
152        Post::from_font(self)
153    }
154
155    /// Returns the maximum profile table.
156    fn maxp(&self) -> Option<Maxp<'a>> {
157        Maxp::from_font(self)
158    }
159
160    /// Returns the horizontal header table.
161    fn hhea(&self) -> Option<Hhea<'a>> {
162        Hhea::from_font(self)
163    }
164    /// Returns the vertical header table.
165    fn vhea(&self) -> Option<Vhea<'a>> {
166        Vhea::from_font(self)
167    }
168}
169
170impl<'a, T> RawFont<'a> for &T
171where
172    T: RawFont<'a>,
173{
174    fn data(&self) -> &'a [u8] {
175        (*self).data()
176    }
177
178    fn offset(&self) -> u32 {
179        (*self).offset()
180    }
181}