1use core::{fmt, ops};
4
5#[cfg(doc)]
6use crate::Font;
7use crate::{alloc::Vec, write::VecExt, ParseError, ParseErrorKind};
8
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
11pub struct TableTag(pub(crate) [u8; 4]);
12
13impl_tag!(TableTag);
14
15macro_rules! tag_const {
16 ($($val:tt, $int:tt $(as $name:ident)?;)+) => {
17 $(
18 tag_const!(@define $val $(as $name)?);
19 )+
20
21 #[cfg(feature = "woff2")]
22 pub(crate) fn as_u8(self) -> Option<u8> {
23 Some(match &self.0 {
24 $($val => $int,)+
25 _ => return None,
26 })
27 }
28
29 #[cfg(feature = "woff2")]
30 pub(crate) fn from_u8(val: u8) -> Option<Self> {
31 Some(match val & 63 {
32 $($int => Self(*$val),)+
33 _ => return None,
34 })
35 }
36 };
37
38 (@define $val:tt as $name:ident) => {
39 #[doc = concat!("Tag for the `", stringify!($val), "` table.")]
40 pub const $name: Self = Self(*$val);
41 };
42 (@define $val:tt) => {
43 };
45}
46
47impl TableTag {
48 tag_const!(
50 b"cmap", 0 as CMAP;
51 b"head", 1 as HEAD;
52 b"hhea", 2 as HHEA;
53 b"hmtx", 3 as HMTX;
54 b"maxp", 4 as MAXP;
55 b"name", 5 as NAME;
56 b"OS/2", 6 as OS2;
57 b"post", 7 as POST;
58 b"cvt ", 8 as CVT;
59 b"fpgm", 9 as FPGM;
60 b"glyf", 10 as GLYF;
61 b"loca", 11 as LOCA;
62 b"prep", 12 as PREP;
63 b"CFF ", 13;
64 b"VORG", 14;
65 b"EBDT", 15;
66 b"EBLC", 16;
67 b"gasp", 17;
68 b"hdmx", 18;
69 b"kern", 19;
70 b"LTSH", 20;
71 b"PCLT", 21;
72 b"VDMX", 22;
73 b"vhea", 23;
74 b"vmtx", 24;
75 b"BASE", 25;
76 b"GDEF", 26;
77 b"GPOS", 27;
78 b"GSUB", 28;
79 b"EBSC", 29;
80 b"JSTF", 30;
81 b"MATH", 31;
82 b"CBDT", 32;
83 b"CBLC", 33;
84 b"COLR", 34;
85 b"CPAL", 35;
86 b"SVG ", 36;
87 b"sbix", 37;
88 b"acnt", 38;
89 b"avar", 39 as AVAR;
90 b"bdat", 40;
91 b"bloc", 41;
92 b"bsln", 42;
93 b"cvar", 43;
94 b"fdsc", 44;
95 b"feat", 45;
96 b"fmtx", 46;
97 b"fvar", 47 as FVAR;
98 b"gvar", 48 as GVAR;
99 b"hsty", 49;
100 b"just", 50;
101 b"lcar", 51;
102 b"mort", 52;
103 b"morx", 53;
104 b"opbd", 54;
105 b"prop", 55;
106 b"trak", 56;
107 b"Zapf", 57;
108 b"Silf", 58;
109 b"Glat", 59;
110 b"Gloc", 60;
111 b"Feat", 61;
112 b"Sill", 62;
113 );
114
115 pub(crate) const STAT: Self = Self(*b"STAT");
116
117 pub(super) fn is_variable(self) -> bool {
118 matches!(self, Self::AVAR | Self::FVAR | Self::GVAR)
119 || matches!(&self.0, b"cvar" | b"HVAR" | b"MVAR" | b"VVAR")
120 }
121}
122
123#[derive(Clone, Copy, PartialEq, Eq, Hash)]
127pub struct Fixed(pub(super) i32);
128
129impl fmt::Debug for Fixed {
130 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
131 fmt::Display::fmt(&f32::from(*self), formatter)
132 }
133}
134
135impl fmt::Display for Fixed {
136 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
137 fmt::Debug::fmt(self, formatter)
138 }
139}
140
141impl From<Fixed> for f32 {
142 #[allow(clippy::cast_precision_loss)] fn from(value: Fixed) -> Self {
144 value.0 as f32 / 65_536.0_f32
145 }
146}
147
148impl From<i16> for Fixed {
149 fn from(value: i16) -> Self {
150 Self(i32::from(value) << 16)
151 }
152}
153
154#[derive(Debug, Clone, Copy)]
156pub(crate) struct Cursor<'a> {
157 pub(super) bytes: &'a [u8],
158 pub(super) offset: usize,
159 pub(super) table: Option<TableTag>,
160}
161
162impl<'a> Cursor<'a> {
163 pub(crate) fn new(bytes: &'a [u8]) -> Self {
164 Self {
165 bytes,
166 offset: 0,
167 table: None,
168 }
169 }
170
171 pub(super) fn for_table(bytes: &'a [u8], offset: usize, table: TableTag) -> Self {
172 Self {
173 bytes,
174 offset,
175 table: Some(table),
176 }
177 }
178
179 pub(crate) fn bytes(&self) -> &'a [u8] {
180 self.bytes
181 }
182
183 pub(super) fn offset(&self) -> usize {
184 self.offset
185 }
186
187 #[cfg(feature = "tracing")]
188 pub(crate) fn range(&self) -> ops::Range<usize> {
189 self.offset..self.offset + self.bytes.len()
190 }
191
192 pub(super) fn err(&self, kind: ParseErrorKind) -> ParseError {
193 ParseError {
194 kind,
195 offset: self.offset,
196 table: self.table,
197 }
198 }
199
200 pub(super) fn skip(&mut self, n: usize) -> Result<(), ParseError> {
201 if self.bytes.len() < n {
202 Err(self.err(ParseErrorKind::UnexpectedEof))
203 } else {
204 self.bytes = &self.bytes[n..];
205 self.offset += n;
206 Ok(())
207 }
208 }
209
210 pub(super) fn read_u16(&mut self) -> Result<u16, ParseError> {
211 let [a, b, rest @ ..] = self.bytes else {
212 return Err(self.err(ParseErrorKind::UnexpectedEof));
213 };
214 self.bytes = rest;
215 self.offset += 2;
216 Ok(u16::from_be_bytes([*a, *b]))
217 }
218
219 #[allow(clippy::cast_possible_wrap)] pub(super) fn read_i16(&mut self) -> Result<i16, ParseError> {
221 Ok(self.read_u16()? as i16)
222 }
223
224 pub(super) fn read_u16_checked<T>(
225 &mut self,
226 check: impl FnOnce(u16) -> Result<T, ParseErrorKind>,
227 ) -> Result<T, ParseError> {
228 check(self.read_u16()?).map_err(|kind| ParseError {
229 kind,
230 table: self.table,
231 offset: self.offset - 2, })
233 }
234
235 pub(super) fn read_u32(&mut self) -> Result<u32, ParseError> {
236 let [a, b, c, d, rest @ ..] = self.bytes else {
237 return Err(self.err(ParseErrorKind::UnexpectedEof));
238 };
239 self.bytes = rest;
240 self.offset += 4;
241 Ok(u32::from_be_bytes([*a, *b, *c, *d]))
242 }
243
244 #[allow(clippy::cast_possible_wrap)] pub(super) fn read_i32(&mut self) -> Result<i32, ParseError> {
246 Ok(self.read_u32()? as i32)
247 }
248
249 pub(super) fn read_u32_checked<T>(
250 &mut self,
251 check: impl FnOnce(u32) -> Result<T, ParseErrorKind>,
252 ) -> Result<T, ParseError> {
253 check(self.read_u32()?).map_err(|kind| ParseError {
254 kind,
255 table: self.table,
256 offset: self.offset - 4, })
258 }
259
260 pub(super) fn read_u64(&mut self) -> Result<u64, ParseError> {
261 let u64_bytes = self
262 .bytes
263 .first_chunk::<8>()
264 .ok_or_else(|| self.err(ParseErrorKind::UnexpectedEof))?;
265
266 self.bytes = &self.bytes[8..];
267 self.offset += 8;
268 Ok(u64::from_be_bytes(*u64_bytes))
269 }
270
271 #[allow(clippy::cast_possible_wrap)] pub(super) fn read_i64(&mut self) -> Result<i64, ParseError> {
273 Ok(self.read_u64()? as i64)
274 }
275
276 pub(super) fn read_u128(&mut self) -> Result<u128, ParseError> {
277 let u128_bytes = self
278 .bytes
279 .first_chunk::<16>()
280 .ok_or_else(|| self.err(ParseErrorKind::UnexpectedEof))?;
281
282 self.bytes = &self.bytes[16..];
283 self.offset += 16;
284 Ok(u128::from_be_bytes(*u128_bytes))
285 }
286
287 pub(super) fn read_byte_array<const N: usize>(&mut self) -> Result<[u8; N], ParseError> {
288 if self.bytes.len() < N {
289 Err(self.err(ParseErrorKind::UnexpectedEof))
290 } else {
291 let (head, tail) = self.bytes.split_at(N);
292 self.bytes = tail;
293 self.offset += N;
294 Ok(head.try_into().unwrap())
295 }
296 }
297
298 pub(super) fn read_range(&self, range: ops::Range<usize>) -> Result<Self, ParseError> {
299 let bytes = self.bytes.get(range.clone()).ok_or_else(|| {
300 self.err(ParseErrorKind::RangeOutOfBounds {
301 range: range.clone(),
302 len: self.bytes.len(),
303 })
304 })?;
305 Ok(Self {
306 bytes,
307 offset: self.offset + range.start,
308 table: self.table,
309 })
310 }
311
312 pub(super) fn split_at(&mut self, pos: usize) -> Result<Self, ParseError> {
313 let prefix = self.read_range(0..pos)?;
314 self.skip(pos)?;
315 Ok(prefix)
316 }
317}
318
319#[derive(Debug, Clone, Copy, PartialEq)]
322pub struct LongDateTime(pub(crate) i64);
323
324impl LongDateTime {
325 const EPOCH_TS: i64 = -2_082_844_800;
327
328 pub fn as_unix_timestamp(self) -> Option<i64> {
331 self.0.checked_add(Self::EPOCH_TS)
332 }
333}
334
335#[derive(Debug, Clone, Copy, PartialEq)]
336pub(crate) struct BoundingBox {
337 pub(crate) x_min: i16,
338 pub(crate) y_min: i16,
339 pub(crate) x_max: i16,
340 pub(crate) y_max: i16,
341}
342
343impl BoundingBox {
344 pub(super) const BYTE_LEN: usize = 8;
345
346 pub(crate) const ZERO: Self = Self {
347 x_min: 0,
348 y_min: 0,
349 x_max: 0,
350 y_max: 0,
351 };
352
353 pub(super) fn parse(cursor: &mut Cursor<'_>) -> Result<Self, ParseError> {
354 let x_min = cursor.read_i16()?;
355 let y_min = cursor.read_i16()?;
356 let x_max = cursor.read_i16()?;
357 let y_max = cursor.read_i16()?;
358 Ok(Self {
359 x_min,
360 y_min,
361 x_max,
362 y_max,
363 })
364 }
365
366 pub(crate) fn write_to_vec(self, buffer: &mut Vec<u8>) {
367 buffer.write_i16(self.x_min);
368 buffer.write_i16(self.y_min);
369 buffer.write_i16(self.x_max);
370 buffer.write_i16(self.y_max);
371 }
372
373 pub(crate) fn union(self, other: Self) -> Self {
374 Self {
375 x_min: self.x_min.min(other.x_min),
376 y_min: self.y_min.min(other.y_min),
377 x_max: self.x_max.max(other.x_max),
378 y_max: self.y_max.max(other.y_max),
379 }
380 }
381}
382
383#[derive(Debug, Clone, Copy)]
384pub(crate) enum OffsetFormat {
385 Short,
386 Long,
387}
388
389impl OffsetFormat {
390 pub(super) const fn bytes_per_offset(self) -> usize {
391 match self {
392 Self::Short => 2,
393 Self::Long => 4,
394 }
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use chrono::{TimeZone, Utc};
401
402 use super::*;
403
404 #[test]
405 fn timestamp_conversion_is_correct() {
406 let ts = Utc
407 .with_ymd_and_hms(1904, 1, 1, 0, 0, 0)
408 .unwrap()
409 .timestamp();
410 assert_eq!(ts, LongDateTime::EPOCH_TS);
411
412 let unix_ts = LongDateTime(-LongDateTime::EPOCH_TS)
413 .as_unix_timestamp()
414 .unwrap();
415 assert_eq!(unix_ts, 0);
416 }
417}