Skip to main content

ttf_view/tables/
head.rs

1use crate::{
2    tables::{Table, TableDirectory, TableError},
3    types::{BigEndian, Fixed, LongDateTime, Tag, int16, tags, uint16, uint32},
4};
5
6#[repr(C)]
7pub struct HeadV0 {
8    _exhaustive_but_dont_instantiate: (),
9    // version any:
10    pub major_version: uint16,
11    pub minor_version: uint16,
12}
13#[repr(C)]
14pub struct HeadV1 {
15    v0: HeadV0,
16    // version = 1.x:
17    pub font_revision: BigEndian<Fixed>,
18    pub checksum_adjustment: uint32,
19    pub magic_number: uint32,
20    pub flags: uint16,
21    pub units_per_em: uint16,
22    pub created: BigEndian<LongDateTime>,
23    pub modified: BigEndian<LongDateTime>,
24    pub x_min: int16,
25    pub y_min: int16,
26    pub x_max: int16,
27    pub y_max: int16,
28    pub mac_style: uint16,
29    pub lowest_rec_ppem: uint16,
30    pub font_direction_hint: int16,
31    pub index_to_loc_format: int16,
32    pub glyph_data_format: int16,
33}
34
35const impl std::ops::Deref for HeadV1 {
36    type Target = HeadV0;
37    fn deref(&self) -> &Self::Target {
38        &self.v0
39    }
40}
41
42impl<'a> Table<'a> for Head<'a> {
43    const TAG: Tag = tags::head;
44    fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
45        let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
46        let v0 = rec.raw_as::<HeadV0>().ok_or(TableError::InvalidLen)?;
47
48        let required_len = match v0.major_version.get() {
49            0 => size_of::<HeadV0>() as u32,
50            1 => size_of::<HeadV1>() as u32,
51            _ => return Err(TableError::UnknownVersion),
52        };
53        if rec.length.get() < required_len {
54            return Err(TableError::InvalidLen);
55        }
56
57        Ok(Self { head: v0 })
58    }
59}
60
61#[derive(Copy)]
62#[derive_const(Clone)]
63pub struct Head<'a> {
64    head: &'a HeadV0,
65}
66
67const impl<'a> std::ops::Deref for Head<'a> {
68    type Target = &'a HeadV0;
69    fn deref(&self) -> &Self::Target {
70        &self.head
71    }
72}
73
74impl<'a> Head<'a> {
75    pub const fn v1(&self) -> Option<&'a HeadV1> {
76        if self.major_version.get() == 1 {
77            Some(unsafe { std::mem::transmute::<&HeadV0, &HeadV1>(self.head) })
78        } else {
79            None
80        }
81    }
82
83    // TODO: We've got two options here, we either:
84    // 1) return Option<&T>, references to big-endian fields, which probably should be fine.
85    //    It would make writing some low-level utils a bit easier. But on the other hand it
86    //    postpones the endianness conversion, which could be inconvenient.
87    // 2) return Option<T>, endianness-converted values. Convenient, and users don't need to
88    //    depend on zerocopy to have the return types available (HeadV0, HeadV1 will still have
89    //    zerocopy's types though).
90
91    // version = 1.x:
92    pub const fn font_revision(&self) -> Option<Fixed> {
93        Some(self.v1()?.font_revision.get())
94    }
95    pub const fn checksum_adjustment(&self) -> Option<uint32> {
96        Some(self.v1()?.checksum_adjustment)
97    }
98    pub const fn magic_number(&self) -> Option<uint32> {
99        Some(self.v1()?.magic_number)
100    }
101    pub const fn flags(&self) -> Option<uint16> {
102        Some(self.v1()?.flags)
103    }
104    pub const fn units_per_em(&self) -> Option<uint16> {
105        Some(self.v1()?.units_per_em)
106    }
107    pub const fn created(&self) -> Option<LongDateTime> {
108        Some(self.v1()?.created.get())
109    }
110    pub const fn modified(&self) -> Option<LongDateTime> {
111        Some(self.v1()?.modified.get())
112    }
113    pub const fn x_min(&self) -> Option<int16> {
114        Some(self.v1()?.x_min)
115    }
116    pub const fn y_min(&self) -> Option<int16> {
117        Some(self.v1()?.y_min)
118    }
119    pub const fn x_max(&self) -> Option<int16> {
120        Some(self.v1()?.x_max)
121    }
122    pub const fn y_max(&self) -> Option<int16> {
123        Some(self.v1()?.y_max)
124    }
125    pub const fn mac_style(&self) -> Option<uint16> {
126        Some(self.v1()?.mac_style)
127    }
128    pub const fn lowest_rec_ppem(&self) -> Option<uint16> {
129        Some(self.v1()?.lowest_rec_ppem)
130    }
131    pub const fn font_direction_hint(&self) -> Option<int16> {
132        Some(self.v1()?.font_direction_hint)
133    }
134    pub const fn index_to_loc_format(&self) -> Option<int16> {
135        Some(self.v1()?.index_to_loc_format)
136    }
137    pub const fn glyph_data_format(&self) -> Option<int16> {
138        Some(self.v1()?.glyph_data_format)
139    }
140}
141
142impl std::fmt::Debug for Head<'_> {
143    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
144        let mut f = f.debug_struct("HeadTable");
145        f.field("major_version", &self.major_version.get())
146            .field("minor_version", &self.minor_version.get());
147
148        if let Some(v1) = self.v1() {
149            f.field("font_revision", &v1.font_revision);
150            f.field_with("checksum_adjustment", |f| write!(f, "{:#010X}", v1.checksum_adjustment));
151            f.field_with("magic_number", |f| write!(f, "{:#010X}", v1.magic_number));
152            f.field_with("flags", |f| write!(f, "{:#017b}", v1.flags));
153            f.field("units_per_em", &v1.units_per_em.get());
154            f.field_with("created", |f| write!(f, "{}", v1.created));
155            f.field_with("modified", |f| write!(f, "{}", v1.modified));
156            f.field("x_min", &v1.x_min.get());
157            f.field("y_min", &v1.y_min.get());
158            f.field("x_max", &v1.x_max.get());
159            f.field("y_max", &v1.y_max.get());
160            f.field_with("mac_style", |f| write!(f, "{:#09b}", v1.mac_style));
161            f.field("lowest_rec_ppem", &v1.lowest_rec_ppem.get());
162            f.field("font_direction_hint", &v1.font_direction_hint.get());
163            f.field("index_to_loc_format", &v1.index_to_loc_format.get());
164            f.field("glyph_data_format", &v1.glyph_data_format.get());
165        }
166        f.finish()
167    }
168}
169
170// Monomorphize all fmts to the above fmt for Head<'a>
171impl std::fmt::Debug for HeadV0 {
172    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
173        Head { head: self }.fmt(f)
174    }
175}
176impl std::fmt::Debug for HeadV1 {
177    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
178        Head { head: self }.fmt(f)
179    }
180}