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 pub major_version: uint16,
11 pub minor_version: uint16,
12}
13#[repr(C)]
14pub struct HeadV1 {
15 v0: HeadV0,
16 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 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
170impl 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}