1#![no_std]
37#![forbid(unsafe_code)]
38#![warn(missing_docs)]
39#![warn(missing_copy_implementations)]
40#![warn(missing_debug_implementations)]
41
42#[cfg(feature = "std")]
43#[macro_use]
44extern crate std;
45
46macro_rules! try_opt_or {
47 ($value:expr, $ret:expr) => {
48 match $value {
49 Some(v) => v,
50 None => return $ret,
51 }
52 };
53}
54
55mod parser;
56mod tables;
57#[cfg(feature = "apple-layout")] mod aat;
58#[cfg(feature = "opentype-layout")] mod ggg;
59#[cfg(feature = "variable-fonts")] mod var_store;
60
61use parser::{Stream, NumFrom, TryNumFrom, Offset32, Offset};
62pub use parser::{FromData, LazyArray16, LazyArrayIter16, LazyArray32, LazyArrayIter32, Fixed};
63use head::IndexToLocationFormat;
64
65#[cfg(feature = "variable-fonts")] pub use fvar::VariationAxis;
66
67pub use name::{name_id, PlatformId};
68pub use os2::{Weight, Width, ScriptMetrics, Style};
69pub use tables::CFFError;
70pub use tables::{cmap, kern, sbix, maxp, hmtx, name, os2, loca, svg, vorg, post, head, hhea, glyf};
71pub use tables::{cff1 as cff, vhea, cbdt, cblc};
72#[cfg(feature = "opentype-layout")] pub use tables::{gdef, gpos, gsub};
73#[cfg(feature = "apple-layout")] pub use tables::{ankr, feat, kerx, morx, trak};
74#[cfg(feature = "variable-fonts")] pub use tables::{cff2, avar, fvar, gvar, hvar, mvar};
75
76#[cfg(feature = "opentype-layout")]
77pub mod opentype_layout {
78 pub use crate::ggg::*;
82}
83
84#[cfg(feature = "apple-layout")]
85pub mod apple_layout {
86 pub use crate::aat::*;
91}
92
93
94#[repr(transparent)]
96#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Debug, Hash)]
97pub struct GlyphId(pub u16);
98
99impl FromData for GlyphId {
100 const SIZE: usize = 2;
101
102 #[inline]
103 fn parse(data: &[u8]) -> Option<Self> {
104 u16::parse(data).map(GlyphId)
105 }
106}
107
108
109#[derive(Clone, Copy, PartialEq, Debug)]
113enum Magic {
114 TrueType,
115 OpenType,
116 FontCollection,
117}
118
119impl FromData for Magic {
120 const SIZE: usize = 4;
121
122 #[inline]
123 fn parse(data: &[u8]) -> Option<Self> {
124 match u32::parse(data)? {
125 0x00010000 | 0x74727565 => Some(Magic::TrueType),
126 0x4F54544F => Some(Magic::OpenType),
127 0x74746366 => Some(Magic::FontCollection),
128 _ => None,
129 }
130 }
131}
132
133
134#[repr(transparent)]
141#[derive(Clone, Copy, PartialEq, Default, Debug)]
142pub struct NormalizedCoordinate(i16);
143
144impl From<i16> for NormalizedCoordinate {
145 #[inline]
149 fn from(n: i16) -> Self {
150 NormalizedCoordinate(parser::i16_bound(-16384, n, 16384))
151 }
152}
153
154impl From<f32> for NormalizedCoordinate {
155 #[inline]
159 fn from(n: f32) -> Self {
160 NormalizedCoordinate((parser::f32_bound(-1.0, n, 1.0) * 16384.0) as i16)
161 }
162}
163
164impl NormalizedCoordinate {
165 #[inline]
167 pub fn get(self) -> i16 {
168 self.0
169 }
170}
171
172
173#[derive(Clone, Copy, PartialEq, Debug)]
183pub struct Variation {
184 pub axis: Tag,
186 pub value: f32,
188}
189
190
191#[repr(transparent)]
193#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194pub struct Tag(pub u32);
195
196impl Tag {
197 #[inline]
205 pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
206 Tag(((bytes[0] as u32) << 24) | ((bytes[1] as u32) << 16) |
207 ((bytes[2] as u32) << 8) | (bytes[3] as u32))
208 }
209
210 #[inline]
218 pub fn from_bytes_lossy(bytes: &[u8]) -> Self {
219 if bytes.is_empty() {
220 return Tag::from_bytes(&[0, 0, 0, 0]);
221 }
222
223 let mut iter = bytes.iter().cloned().chain(core::iter::repeat(b' '));
224 Tag::from_bytes(&[
225 iter.next().unwrap(),
226 iter.next().unwrap(),
227 iter.next().unwrap(),
228 iter.next().unwrap(),
229 ])
230 }
231
232 #[inline]
234 pub const fn to_bytes(self) -> [u8; 4] {
235 [
236 (self.0 >> 24 & 0xff) as u8,
237 (self.0 >> 16 & 0xff) as u8,
238 (self.0 >> 8 & 0xff) as u8,
239 (self.0 >> 0 & 0xff) as u8,
240 ]
241 }
242
243 #[inline]
245 pub const fn to_chars(self) -> [char; 4] {
246 [
247 (self.0 >> 24 & 0xff) as u8 as char,
248 (self.0 >> 16 & 0xff) as u8 as char,
249 (self.0 >> 8 & 0xff) as u8 as char,
250 (self.0 >> 0 & 0xff) as u8 as char,
251 ]
252 }
253
254 #[inline]
256 pub const fn is_null(&self) -> bool {
257 self.0 == 0
258 }
259
260 #[inline]
262 pub const fn as_u32(&self) -> u32 {
263 self.0
264 }
265}
266
267impl core::fmt::Debug for Tag {
268 #[inline]
269 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270 write!(f, "Tag({})", self)
271 }
272}
273
274impl core::fmt::Display for Tag {
275 #[inline]
276 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
277 let b = self.to_chars();
278 write!(
279 f,
280 "{}{}{}{}",
281 b.get(0).unwrap_or(&' '),
282 b.get(1).unwrap_or(&' '),
283 b.get(2).unwrap_or(&' '),
284 b.get(3).unwrap_or(&' ')
285 )
286 }
287}
288
289impl FromData for Tag {
290 const SIZE: usize = 4;
291
292 #[inline]
293 fn parse(data: &[u8]) -> Option<Self> {
294 u32::parse(data).map(Tag)
295 }
296}
297
298
299#[repr(C)]
303#[derive(Clone, Copy, PartialEq, Debug)]
304pub struct LineMetrics {
305 pub position: i16,
307
308 pub thickness: i16,
310}
311
312
313#[repr(C)]
317#[allow(missing_docs)]
318#[derive(Clone, Copy, PartialEq, Debug)]
319pub struct Rect {
320 pub x_min: i16,
321 pub y_min: i16,
322 pub x_max: i16,
323 pub y_max: i16,
324}
325
326impl Rect {
327 #[inline]
329 pub fn width(&self) -> i16 {
330 self.x_max - self.x_min
331 }
332
333 #[inline]
335 pub fn height(&self) -> i16 {
336 self.y_max - self.y_min
337 }
338}
339
340
341#[derive(Clone, Copy, Debug)]
342pub(crate) struct BBox {
343 x_min: f32,
344 y_min: f32,
345 x_max: f32,
346 y_max: f32,
347}
348
349impl BBox {
350 #[inline]
351 fn new() -> Self {
352 BBox {
353 x_min: core::f32::MAX,
354 y_min: core::f32::MAX,
355 x_max: core::f32::MIN,
356 y_max: core::f32::MIN,
357 }
358 }
359
360 #[inline]
361 fn is_default(&self) -> bool {
362 self.x_min == core::f32::MAX &&
363 self.y_min == core::f32::MAX &&
364 self.x_max == core::f32::MIN &&
365 self.y_max == core::f32::MIN
366 }
367
368 #[inline]
369 fn extend_by(&mut self, x: f32, y: f32) {
370 self.x_min = self.x_min.min(x);
371 self.y_min = self.y_min.min(y);
372 self.x_max = self.x_max.max(x);
373 self.y_max = self.y_max.max(y);
374 }
375
376 #[inline]
377 fn to_rect(&self) -> Option<Rect> {
378 Some(Rect {
379 x_min: i16::try_num_from(self.x_min)?,
380 y_min: i16::try_num_from(self.y_min)?,
381 x_max: i16::try_num_from(self.x_max)?,
382 y_max: i16::try_num_from(self.y_max)?,
383 })
384 }
385}
386
387
388pub trait OutlineBuilder {
390 fn move_to(&mut self, x: f32, y: f32);
394
395 fn line_to(&mut self, x: f32, y: f32);
397
398 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32);
400
401 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32);
403
404 fn close(&mut self);
408}
409
410
411struct DummyOutline;
412impl OutlineBuilder for DummyOutline {
413 fn move_to(&mut self, _: f32, _: f32) {}
414 fn line_to(&mut self, _: f32, _: f32) {}
415 fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {}
416 fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {}
417 fn close(&mut self) {}
418}
419
420
421#[allow(missing_docs)]
423#[derive(Clone, Copy, PartialEq, Debug)]
424pub enum RasterImageFormat {
425 PNG,
426}
427
428
429#[derive(Clone, Copy, PartialEq, Debug)]
433pub struct RasterGlyphImage<'a> {
434 pub x: i16,
436
437 pub y: i16,
439
440 pub width: u16,
444
445 pub height: u16,
449
450 pub pixels_per_em: u16,
452
453 pub format: RasterImageFormat,
455
456 pub data: &'a [u8],
458}
459
460
461#[derive(Clone, Copy)]
462struct TableRecord {
463 table_tag: Tag,
464 #[allow(dead_code)]
465 check_sum: u32,
466 offset: u32,
467 length: u32,
468}
469
470impl FromData for TableRecord {
471 const SIZE: usize = 16;
472
473 #[inline]
474 fn parse(data: &[u8]) -> Option<Self> {
475 let mut s = Stream::new(data);
476 Some(TableRecord {
477 table_tag: s.read::<Tag>()?,
478 check_sum: s.read::<u32>()?,
479 offset: s.read::<u32>()?,
480 length: s.read::<u32>()?,
481 })
482 }
483}
484
485
486#[cfg(feature = "variable-fonts")]
487const MAX_VAR_COORDS: usize = 32;
488
489#[cfg(feature = "variable-fonts")]
490#[derive(Clone, Default)]
491struct VarCoords {
492 data: [NormalizedCoordinate; MAX_VAR_COORDS],
493 len: u8,
494}
495
496#[cfg(feature = "variable-fonts")]
497impl VarCoords {
498 #[inline]
499 fn as_slice(&self) -> &[NormalizedCoordinate] {
500 &self.data[0..usize::from(self.len)]
501 }
502
503 #[inline]
504 fn as_mut_slice(&mut self) -> &mut [NormalizedCoordinate] {
505 let end = usize::from(self.len);
506 &mut self.data[0..end]
507 }
508}
509
510
511#[derive(Clone, Copy, PartialEq, Debug)]
513pub enum FaceParsingError {
514 MalformedFont,
518
519 UnknownMagic,
521
522 FaceIndexOutOfBounds,
524
525 NoHeadTable,
527
528 NoHheaTable,
530
531 NoMaxpTable,
533}
534
535impl core::fmt::Display for FaceParsingError {
536 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
537 match self {
538 FaceParsingError::MalformedFont => write!(f, "malformed font"),
539 FaceParsingError::UnknownMagic => write!(f, "unknown magic"),
540 FaceParsingError::FaceIndexOutOfBounds => write!(f, "face index is out of bounds"),
541 FaceParsingError::NoHeadTable => write!(f, "the head table is missing or malformed"),
542 FaceParsingError::NoHheaTable => write!(f, "the hhea table is missing or malformed"),
543 FaceParsingError::NoMaxpTable => write!(f, "the maxp table is missing or malformed"),
544 }
545 }
546}
547
548#[cfg(feature = "std")]
549impl std::error::Error for FaceParsingError {}
550
551
552#[derive(Clone, Copy)]
561pub struct RawFace<'a> {
562 data: &'a [u8],
563 table_records: LazyArray16<'a, TableRecord>,
564}
565
566impl<'a> RawFace<'a> {
567 pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
575 let mut s = Stream::new(data);
578
579 let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
581 if magic == Magic::FontCollection {
582 s.skip::<u32>(); let number_of_faces = s.read::<u32>().ok_or(FaceParsingError::MalformedFont)?;
584 let offsets = s.read_array32::<Offset32>(number_of_faces)
585 .ok_or(FaceParsingError::MalformedFont)?;
586
587 let face_offset = offsets.get(index).ok_or(FaceParsingError::FaceIndexOutOfBounds)?;
588 let face_offset = face_offset.to_usize().checked_sub(s.offset())
591 .ok_or(FaceParsingError::MalformedFont)?;
592 s.advance_checked(face_offset).ok_or(FaceParsingError::MalformedFont)?;
593
594 let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
597 if magic == Magic::FontCollection {
599 return Err(FaceParsingError::UnknownMagic);
600 }
601 }
602
603 let num_tables = s.read::<u16>().ok_or(FaceParsingError::MalformedFont)?;
604 s.advance(6); let table_records = s.read_array16::<TableRecord>(num_tables)
606 .ok_or(FaceParsingError::MalformedFont)?;
607
608 Ok(RawFace {
609 data,
610 table_records,
611 })
612 }
613
614 pub fn table(&self, tag: Tag) -> Option<&'a [u8]> {
616 let (_, table) = self.table_records.binary_search_by(|record| record.table_tag.cmp(&tag))?;
617 let offset = usize::num_from(table.offset);
618 let length = usize::num_from(table.length);
619 let end = offset.checked_add(length)?;
620 self.data.get(offset..end)
621 }
622}
623
624impl core::fmt::Debug for RawFace<'_> {
625 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
626 write!(f, "RawFace {{ ... }}")
627 }
628}
629
630
631#[allow(missing_docs)]
639#[allow(missing_debug_implementations)]
640#[derive(Clone, Default)]
641pub struct RawFaceTables<'a> {
642 pub head: &'a [u8],
644 pub hhea: &'a [u8],
645 pub maxp: &'a [u8],
646
647 pub cbdt: Option<&'a [u8]>,
648 pub cblc: Option<&'a [u8]>,
649 pub cff: Option<&'a [u8]>,
650 pub cmap: Option<&'a [u8]>,
651 pub glyf: Option<&'a [u8]>,
652 pub hmtx: Option<&'a [u8]>,
653 pub kern: Option<&'a [u8]>,
654 pub loca: Option<&'a [u8]>,
655 pub name: Option<&'a [u8]>,
656 pub os2: Option<&'a [u8]>,
657 pub post: Option<&'a [u8]>,
658 pub sbix: Option<&'a [u8]>,
659 pub svg: Option<&'a [u8]>,
660 pub vhea: Option<&'a [u8]>,
661 pub vmtx: Option<&'a [u8]>,
662 pub vorg: Option<&'a [u8]>,
663
664 #[cfg(feature = "opentype-layout")] pub gdef: Option<&'a [u8]>,
665 #[cfg(feature = "opentype-layout")] pub gpos: Option<&'a [u8]>,
666 #[cfg(feature = "opentype-layout")] pub gsub: Option<&'a [u8]>,
667
668 #[cfg(feature = "apple-layout")] pub ankr: Option<&'a [u8]>,
669 #[cfg(feature = "apple-layout")] pub feat: Option<&'a [u8]>,
670 #[cfg(feature = "apple-layout")] pub kerx: Option<&'a [u8]>,
671 #[cfg(feature = "apple-layout")] pub morx: Option<&'a [u8]>,
672 #[cfg(feature = "apple-layout")] pub trak: Option<&'a [u8]>,
673
674 #[cfg(feature = "variable-fonts")] pub avar: Option<&'a [u8]>,
675 #[cfg(feature = "variable-fonts")] pub cff2: Option<&'a [u8]>,
676 #[cfg(feature = "variable-fonts")] pub fvar: Option<&'a [u8]>,
677 #[cfg(feature = "variable-fonts")] pub gvar: Option<&'a [u8]>,
678 #[cfg(feature = "variable-fonts")] pub hvar: Option<&'a [u8]>,
679 #[cfg(feature = "variable-fonts")] pub mvar: Option<&'a [u8]>,
680 #[cfg(feature = "variable-fonts")] pub vvar: Option<&'a [u8]>,
681}
682
683#[allow(missing_docs)]
691#[allow(missing_debug_implementations)]
692#[derive(Clone)]
693pub struct FaceTables<'a> {
694 pub head: head::Table,
696 pub hhea: hhea::Table,
697 pub maxp: maxp::Table,
698
699 pub cbdt: Option<cbdt::Table<'a>>,
700 pub cff: Option<cff::Table<'a>>,
701 pub cmap: Option<cmap::Table<'a>>,
702 pub glyf: Option<glyf::Table<'a>>,
703 pub hmtx: Option<hmtx::Table<'a>>,
704 pub kern: Option<kern::Table<'a>>,
705 pub name: Option<name::Table<'a>>,
706 pub os2: Option<os2::Table<'a>>,
707 pub post: Option<post::Table<'a>>,
708 pub sbix: Option<sbix::Table<'a>>,
709 pub svg: Option<svg::Table<'a>>,
710 pub vhea: Option<vhea::Table>,
711 pub vmtx: Option<hmtx::Table<'a>>,
712 pub vorg: Option<vorg::Table<'a>>,
713
714 #[cfg(feature = "opentype-layout")] pub gdef: Option<gdef::Table<'a>>,
715 #[cfg(feature = "opentype-layout")] pub gpos: Option<opentype_layout::LayoutTable<'a>>,
716 #[cfg(feature = "opentype-layout")] pub gsub: Option<opentype_layout::LayoutTable<'a>>,
717
718 #[cfg(feature = "apple-layout")] pub ankr: Option<ankr::Table<'a>>,
719 #[cfg(feature = "apple-layout")] pub feat: Option<feat::Table<'a>>,
720 #[cfg(feature = "apple-layout")] pub kerx: Option<kerx::Table<'a>>,
721 #[cfg(feature = "apple-layout")] pub morx: Option<morx::Table<'a>>,
722 #[cfg(feature = "apple-layout")] pub trak: Option<trak::Table<'a>>,
723
724 #[cfg(feature = "variable-fonts")] pub avar: Option<avar::Table<'a>>,
725 #[cfg(feature = "variable-fonts")] pub cff2: Option<cff2::Table<'a>>,
726 #[cfg(feature = "variable-fonts")] pub fvar: Option<fvar::Table<'a>>,
727 #[cfg(feature = "variable-fonts")] pub gvar: Option<gvar::Table<'a>>,
728 #[cfg(feature = "variable-fonts")] pub hvar: Option<hvar::Table<'a>>,
729 #[cfg(feature = "variable-fonts")] pub mvar: Option<mvar::Table<'a>>,
730 #[cfg(feature = "variable-fonts")] pub vvar: Option<hvar::Table<'a>>,
731}
732
733#[derive(Clone)]
749pub struct Face<'a> {
750 raw_face: RawFace<'a>,
751 tables: FaceTables<'a>, #[cfg(feature = "variable-fonts")] coordinates: VarCoords,
753}
754
755impl<'a> Face<'a> {
756 pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
769 let raw_face = RawFace::from_slice(data, index)?;
770 let raw_tables = Self::collect_tables(raw_face);
771
772 #[allow(unused_mut)]
773 let mut face = Face {
774 raw_face,
775 #[cfg(feature = "variable-fonts")] coordinates: VarCoords::default(),
776 tables: Self::parse_tables(raw_tables)?,
777 };
778
779 #[cfg(feature = "variable-fonts")] {
780 if let Some(ref fvar) = face.tables.fvar {
781 face.coordinates.len = fvar.axes.len().min(MAX_VAR_COORDS as u16) as u8;
782 }
783 }
784
785 Ok(face)
786 }
787
788 fn collect_tables(raw_face: RawFace<'a>) -> RawFaceTables<'a> {
789 let mut tables = RawFaceTables::default();
790
791 for record in raw_face.table_records {
792 let start = usize::num_from(record.offset);
793 let end = match start.checked_add(usize::num_from(record.length)) {
794 Some(v) => v,
795 None => continue,
796 };
797
798 let table_data = raw_face.data.get(start..end);
799 match &record.table_tag.to_bytes() {
800 b"CBDT" => tables.cbdt = table_data,
801 b"CBLC" => tables.cblc = table_data,
802 b"CFF " => tables.cff = table_data,
803 #[cfg(feature = "variable-fonts")]
804 b"CFF2" => tables.cff2 = table_data,
805 #[cfg(feature = "opentype-layout")]
806 b"GDEF" => tables.gdef = table_data,
807 #[cfg(feature = "opentype-layout")]
808 b"GPOS" => tables.gpos = table_data,
809 #[cfg(feature = "opentype-layout")]
810 b"GSUB" => tables.gsub = table_data,
811 #[cfg(feature = "variable-fonts")]
812 b"HVAR" => tables.hvar = table_data,
813 #[cfg(feature = "variable-fonts")]
814 b"MVAR" => tables.mvar = table_data,
815 b"OS/2" => tables.os2 = table_data,
816 b"SVG " => tables.svg = table_data,
817 b"VORG" => tables.vorg = table_data,
818 #[cfg(feature = "variable-fonts")]
819 b"VVAR" => tables.vvar = table_data,
820 #[cfg(feature = "apple-layout")]
821 b"ankr" => tables.ankr = table_data,
822 #[cfg(feature = "variable-fonts")]
823 b"avar" => tables.avar = table_data,
824 b"cmap" => tables.cmap = table_data,
825 #[cfg(feature = "apple-layout")]
826 b"feat" => tables.feat = table_data,
827 #[cfg(feature = "variable-fonts")]
828 b"fvar" => tables.fvar = table_data,
829 b"glyf" => tables.glyf = table_data,
830 #[cfg(feature = "variable-fonts")]
831 b"gvar" => tables.gvar = table_data,
832 b"head" => tables.head = table_data.unwrap_or_default(),
833 b"hhea" => tables.hhea = table_data.unwrap_or_default(),
834 b"hmtx" => tables.hmtx = table_data,
835 b"kern" => tables.kern = table_data,
836 #[cfg(feature = "apple-layout")]
837 b"kerx" => tables.kerx = table_data,
838 b"loca" => tables.loca = table_data,
839 b"maxp" => tables.maxp = table_data.unwrap_or_default(),
840 #[cfg(feature = "apple-layout")]
841 b"morx" => tables.morx = table_data,
842 b"name" => tables.name = table_data,
843 b"post" => tables.post = table_data,
844 b"sbix" => tables.sbix = table_data,
845 #[cfg(feature = "apple-layout")]
846 b"trak" => tables.trak = table_data,
847 b"vhea" => tables.vhea = table_data,
848 b"vmtx" => tables.vmtx = table_data,
849 _ => {}
850 }
851 }
852
853 tables
854 }
855
856 pub fn from_raw_tables(raw_tables: RawFaceTables<'a>) -> Result<Self, FaceParsingError> {
858 #[allow(unused_mut)]
859 let mut face = Face {
860 raw_face: RawFace {
861 data: &[],
862 table_records: LazyArray16::default(),
863 },
864 #[cfg(feature = "variable-fonts")] coordinates: VarCoords::default(),
865 tables: Self::parse_tables(raw_tables)?,
866 };
867
868 #[cfg(feature = "variable-fonts")] {
869 if let Some(ref fvar) = face.tables.fvar {
870 face.coordinates.len = fvar.axes.len().min(MAX_VAR_COORDS as u16) as u8;
871 }
872 }
873
874 Ok(face)
875 }
876
877 fn parse_tables(raw_tables: RawFaceTables<'a>) -> Result<FaceTables<'a>, FaceParsingError> {
878 let head = head::Table::parse(raw_tables.head).ok_or(FaceParsingError::NoHeadTable)?;
879 let hhea = hhea::Table::parse(raw_tables.hhea).ok_or(FaceParsingError::NoHheaTable)?;
880 let maxp = maxp::Table::parse(raw_tables.maxp).ok_or(FaceParsingError::NoMaxpTable)?;
881
882 let hmtx = raw_tables.hmtx.and_then(|data|
883 hmtx::Table::parse(hhea.number_of_metrics, maxp.number_of_glyphs, data)
884 );
885
886 let vhea = raw_tables.vhea.and_then(vhea::Table::parse);
887 let vmtx = if let Some(vhea) = vhea {
888 raw_tables.vmtx.and_then(|data|
889 hmtx::Table::parse(vhea.number_of_metrics, maxp.number_of_glyphs, data)
890 )
891 } else {
892 None
893 };
894
895 let loca = raw_tables.loca.and_then(|data|
896 loca::Table::parse(maxp.number_of_glyphs, head.index_to_location_format, data)
897 );
898 let glyf = if let Some(loca) = loca {
899 raw_tables.glyf.and_then(|data| glyf::Table::parse(loca, data))
900 } else {
901 None
902 };
903
904 let cbdt = if let Some(cblc) = raw_tables.cblc.and_then(cblc::Table::parse) {
905 raw_tables.cbdt.and_then(|data| cbdt::Table::parse(cblc, data))
906 } else {
907 None
908 };
909
910 Ok(FaceTables {
911 head,
912 hhea,
913 maxp,
914
915 cbdt,
916 cff: raw_tables.cff.and_then(cff::Table::parse),
917 cmap: raw_tables.cmap.and_then(cmap::Table::parse),
918 glyf,
919 hmtx,
920 kern: raw_tables.kern.and_then(kern::Table::parse),
921 name: raw_tables.name.and_then(name::Table::parse),
922 os2: raw_tables.os2.and_then(os2::Table::parse),
923 post: raw_tables.post.and_then(post::Table::parse),
924 sbix: raw_tables.sbix.and_then(|data| sbix::Table::parse(maxp.number_of_glyphs, data)),
925 svg: raw_tables.svg.and_then(svg::Table::parse),
926 vhea: raw_tables.vhea.and_then(vhea::Table::parse),
927 vmtx,
928 vorg: raw_tables.vorg.and_then(vorg::Table::parse),
929
930 #[cfg(feature = "opentype-layout")] gdef: raw_tables.gdef.and_then(gdef::Table::parse),
931 #[cfg(feature = "opentype-layout")] gpos: raw_tables.gpos.and_then(opentype_layout::LayoutTable::parse),
932 #[cfg(feature = "opentype-layout")] gsub: raw_tables.gsub.and_then(opentype_layout::LayoutTable::parse),
933
934 #[cfg(feature = "apple-layout")] ankr: raw_tables.ankr
935 .and_then(|data| ankr::Table::parse(maxp.number_of_glyphs, data)),
936 #[cfg(feature = "apple-layout")] feat: raw_tables.feat.and_then(feat::Table::parse),
937 #[cfg(feature = "apple-layout")] kerx: raw_tables.kerx
938 .and_then(|data| kerx::Table::parse(maxp.number_of_glyphs, data)),
939 #[cfg(feature = "apple-layout")] morx: raw_tables.morx
940 .and_then(|data| morx::Table::parse(maxp.number_of_glyphs, data)),
941 #[cfg(feature = "apple-layout")] trak: raw_tables.trak.and_then(trak::Table::parse),
942
943 #[cfg(feature = "variable-fonts")] avar: raw_tables.avar.and_then(avar::Table::parse),
944 #[cfg(feature = "variable-fonts")] cff2: raw_tables.cff2.and_then(cff2::Table::parse),
945 #[cfg(feature = "variable-fonts")] fvar: raw_tables.fvar.and_then(fvar::Table::parse),
946 #[cfg(feature = "variable-fonts")] gvar: raw_tables.gvar.and_then(gvar::Table::parse),
947 #[cfg(feature = "variable-fonts")] hvar: raw_tables.hvar.and_then(hvar::Table::parse),
948 #[cfg(feature = "variable-fonts")] mvar: raw_tables.mvar.and_then(mvar::Table::parse),
949 #[cfg(feature = "variable-fonts")] vvar: raw_tables.vvar.and_then(hvar::Table::parse),
950 })
951 }
952
953 #[inline]
955 pub fn tables(&self) -> &FaceTables<'a> {
956 &self.tables
957 }
958
959 #[inline]
965 pub fn table_data(&self, tag: Tag) -> Option<&'a [u8]> {
966 self.raw_face.table(tag)
967 }
968
969 #[inline]
973 pub fn names(&self) -> name::Names<'a> {
974 self.tables.name.unwrap_or_default().names
975 }
976
977 #[inline]
981 pub fn is_regular(&self) -> bool {
982 self.tables.os2.map(|s| s.style() == Style::Normal).unwrap_or(false)
983 }
984
985 #[inline]
989 pub fn is_italic(&self) -> bool {
990 self.tables.os2.map(|s| s.style() == Style::Italic).unwrap_or(false)
991 }
992
993 #[inline]
997 pub fn is_bold(&self) -> bool {
998 try_opt_or!(self.tables.os2, false).is_bold()
999 }
1000
1001 #[inline]
1005 pub fn is_oblique(&self) -> bool {
1006 self.tables.os2.map(|s| s.style() == Style::Oblique).unwrap_or(false)
1007 }
1008
1009 #[inline]
1011 pub fn style(&self) -> Style {
1012 try_opt_or!(self.tables.os2, Style::Normal).style()
1013 }
1014
1015 #[inline]
1019 pub fn is_monospaced(&self) -> bool {
1020 try_opt_or!(self.tables.post, false).is_monospaced
1021 }
1022
1023 #[inline]
1027 pub fn is_variable(&self) -> bool {
1028 #[cfg(feature = "variable-fonts")] {
1029 self.tables.fvar.is_some()
1031 }
1032
1033 #[cfg(not(feature = "variable-fonts"))] {
1034 false
1035 }
1036 }
1037
1038 #[inline]
1042 pub fn weight(&self) -> Weight {
1043 try_opt_or!(self.tables.os2, Weight::default()).weight()
1044 }
1045
1046 #[inline]
1050 pub fn width(&self) -> Width {
1051 try_opt_or!(self.tables.os2, Width::default()).width()
1052 }
1053
1054 #[inline]
1058 pub fn italic_angle(&self) -> Option<f32> {
1059 self.tables.post.map(|table| table.italic_angle)
1060 }
1061
1062 #[inline]
1069 pub fn ascender(&self) -> i16 {
1070 if let Some(os_2) = self.tables.os2 {
1071 if os_2.use_typographic_metrics() {
1072 let value = os_2.typographic_ascender();
1073 return self.apply_metrics_variation(Tag::from_bytes(b"hasc"), value);
1074 }
1075 }
1076
1077 let mut value = self.tables.hhea.ascender;
1078 if value == 0 {
1079 if let Some(os_2) = self.tables.os2 {
1080 value = os_2.typographic_ascender();
1081 if value == 0 {
1082 value = os_2.windows_ascender();
1083 value = self.apply_metrics_variation(Tag::from_bytes(b"hcla"), value);
1084 } else {
1085 value = self.apply_metrics_variation(Tag::from_bytes(b"hasc"), value);
1086 }
1087 }
1088 }
1089
1090 value
1091 }
1092
1093 #[inline]
1097 pub fn descender(&self) -> i16 {
1098 if let Some(os_2) = self.tables.os2 {
1099 if os_2.use_typographic_metrics() {
1100 let value = os_2.typographic_descender();
1101 return self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), value);
1102 }
1103 }
1104
1105 let mut value = self.tables.hhea.descender;
1106 if value == 0 {
1107 if let Some(os_2) = self.tables.os2 {
1108 value = os_2.typographic_descender();
1109 if value == 0 {
1110 value = os_2.windows_descender();
1111 value = self.apply_metrics_variation(Tag::from_bytes(b"hcld"), value);
1112 } else {
1113 value = self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), value);
1114 }
1115 }
1116 }
1117
1118 value
1119 }
1120
1121 #[inline]
1125 pub fn height(&self) -> i16 {
1126 self.ascender() - self.descender()
1127 }
1128
1129 #[inline]
1133 pub fn line_gap(&self) -> i16 {
1134 if let Some(os_2) = self.tables.os2 {
1135 if os_2.use_typographic_metrics() {
1136 let value = os_2.typographic_line_gap();
1137 return self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), value);
1138 }
1139 }
1140
1141 let mut value = self.tables.hhea.line_gap;
1142 if self.tables.hhea.ascender == 0 || self.tables.hhea.descender == 0 {
1144 if let Some(os_2) = self.tables.os2 {
1145 if os_2.typographic_ascender() != 0 || os_2.typographic_descender() != 0 {
1146 value = os_2.typographic_line_gap();
1147 value = self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), value);
1148 } else {
1149 value = 0;
1150 }
1151 }
1152 }
1153
1154 value
1155 }
1156
1157 #[inline]
1166 pub fn typographic_ascender(&self) -> Option<i16> {
1167 self.tables.os2.map(|table| {
1168 let v = table.typographic_ascender();
1169 self.apply_metrics_variation(Tag::from_bytes(b"hasc"), v)
1170 })
1171 }
1172
1173 #[inline]
1182 pub fn typographic_descender(&self) -> Option<i16> {
1183 self.tables.os2.map(|table| {
1184 let v = table.typographic_descender();
1185 self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), v)
1186 })
1187 }
1188
1189 #[inline]
1198 pub fn typographic_line_gap(&self) -> Option<i16> {
1199 self.tables.os2.map(|table| {
1200 let v = table.typographic_line_gap();
1201 self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), v)
1202 })
1203 }
1204
1205 #[inline]
1209 pub fn vertical_ascender(&self) -> Option<i16> {
1210 self.tables.vhea.map(|vhea| vhea.ascender)
1211 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vasc"), v))
1212 }
1213
1214 #[inline]
1218 pub fn vertical_descender(&self) -> Option<i16> {
1219 self.tables.vhea.map(|vhea| vhea.descender)
1220 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vdsc"), v))
1221 }
1222
1223 #[inline]
1227 pub fn vertical_height(&self) -> Option<i16> {
1228 Some(self.vertical_ascender()? - self.vertical_descender()?)
1229 }
1230
1231 #[inline]
1235 pub fn vertical_line_gap(&self) -> Option<i16> {
1236 self.tables.vhea.map(|vhea| vhea.line_gap)
1237 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vlgp"), v))
1238 }
1239
1240 #[inline]
1244 pub fn units_per_em(&self) -> u16 {
1245 self.tables.head.units_per_em
1246 }
1247
1248 #[inline]
1254 pub fn x_height(&self) -> Option<i16> {
1255 self.tables.os2.and_then(|os_2| os_2.x_height())
1256 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"xhgt"), v))
1257 }
1258
1259 #[inline]
1265 pub fn capital_height(&self) -> Option<i16> {
1266 self.tables.os2.and_then(|os_2| os_2.capital_height())
1267 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"cpht"), v))
1268 }
1269
1270 #[inline]
1276 pub fn underline_metrics(&self) -> Option<LineMetrics> {
1277 let mut metrics = self.tables.post?.underline_metrics;
1278
1279 if self.is_variable() {
1280 self.apply_metrics_variation_to(Tag::from_bytes(b"undo"), &mut metrics.position);
1281 self.apply_metrics_variation_to(Tag::from_bytes(b"unds"), &mut metrics.thickness);
1282 }
1283
1284 Some(metrics)
1285 }
1286
1287 #[inline]
1293 pub fn strikeout_metrics(&self) -> Option<LineMetrics> {
1294 let mut metrics = self.tables.os2?.strikeout_metrics();
1295
1296 if self.is_variable() {
1297 self.apply_metrics_variation_to(Tag::from_bytes(b"stro"), &mut metrics.position);
1298 self.apply_metrics_variation_to(Tag::from_bytes(b"strs"), &mut metrics.thickness);
1299 }
1300
1301 Some(metrics)
1302 }
1303
1304 #[inline]
1310 pub fn subscript_metrics(&self) -> Option<ScriptMetrics> {
1311 let mut metrics = self.tables.os2?.subscript_metrics();
1312
1313 if self.is_variable() {
1314 self.apply_metrics_variation_to(Tag::from_bytes(b"sbxs"), &mut metrics.x_size);
1315 self.apply_metrics_variation_to(Tag::from_bytes(b"sbys"), &mut metrics.y_size);
1316 self.apply_metrics_variation_to(Tag::from_bytes(b"sbxo"), &mut metrics.x_offset);
1317 self.apply_metrics_variation_to(Tag::from_bytes(b"sbyo"), &mut metrics.y_offset);
1318 }
1319
1320 Some(metrics)
1321 }
1322
1323 #[inline]
1329 pub fn superscript_metrics(&self) -> Option<ScriptMetrics> {
1330 let mut metrics = self.tables.os2?.superscript_metrics();
1331
1332 if self.is_variable() {
1333 self.apply_metrics_variation_to(Tag::from_bytes(b"spxs"), &mut metrics.x_size);
1334 self.apply_metrics_variation_to(Tag::from_bytes(b"spys"), &mut metrics.y_size);
1335 self.apply_metrics_variation_to(Tag::from_bytes(b"spxo"), &mut metrics.x_offset);
1336 self.apply_metrics_variation_to(Tag::from_bytes(b"spyo"), &mut metrics.y_offset);
1337 }
1338
1339 Some(metrics)
1340 }
1341
1342 #[inline]
1348 pub fn number_of_glyphs(&self) -> u16 {
1349 self.tables.maxp.number_of_glyphs.get()
1350 }
1351
1352 #[inline]
1360 pub fn glyph_index(&self, code_point: char) -> Option<GlyphId> {
1361 for encoding in self.tables.cmap?.subtables {
1362 if !encoding.is_unicode() {
1363 continue;
1364 }
1365
1366 if let Some(id) = encoding.glyph_index(u32::from(code_point)) {
1367 return Some(id);
1368 }
1369 }
1370
1371 None
1372 }
1373
1374 #[inline]
1382 pub fn glyph_variation_index(&self, code_point: char, variation: char) -> Option<GlyphId> {
1383 for subtable in self.tables.cmap?.subtables {
1384 if let cmap::Format::UnicodeVariationSequences(ref table) = subtable.format {
1385 return match table.glyph_index(u32::from(code_point), u32::from(variation))? {
1386 cmap::GlyphVariationResult::Found(v) => Some(v),
1387 cmap::GlyphVariationResult::UseDefault => self.glyph_index(code_point),
1388 };
1389 }
1390 }
1391
1392 None
1393 }
1394
1395 #[inline]
1399 pub fn glyph_hor_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1400 #[cfg(feature = "variable-fonts")] {
1401 let mut advance = self.tables.hmtx?.advance(glyph_id)? as f32;
1402
1403 if self.is_variable() {
1404 if let Some(hvar) = self.tables.hvar {
1406 if let Some(offset) = hvar.advance_offset(glyph_id, self.coords()) {
1407 advance += offset + 0.5;
1409 }
1410 }
1411 }
1412
1413 u16::try_num_from(advance)
1414 }
1415
1416 #[cfg(not(feature = "variable-fonts"))] {
1417 self.tables.hmtx?.advance(glyph_id)
1418 }
1419 }
1420
1421 #[inline]
1425 pub fn glyph_ver_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1426 #[cfg(feature = "variable-fonts")] {
1427 let mut advance = self.tables.vmtx?.advance(glyph_id)? as f32;
1428
1429 if self.is_variable() {
1430 if let Some(vvar) = self.tables.vvar {
1432 if let Some(offset) = vvar.advance_offset(glyph_id, self.coords()) {
1433 advance += offset + 0.5;
1435 }
1436 }
1437 }
1438
1439 u16::try_num_from(advance)
1440 }
1441
1442 #[cfg(not(feature = "variable-fonts"))] {
1443 self.tables.vmtx?.advance(glyph_id)
1444 }
1445 }
1446
1447 #[inline]
1451 pub fn glyph_hor_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1452 #[cfg(feature = "variable-fonts")] {
1453 let mut bearing = self.tables.hmtx?.side_bearing(glyph_id)? as f32;
1454
1455 if self.is_variable() {
1456 if let Some(hvar) = self.tables.hvar {
1458 if let Some(offset) = hvar.side_bearing_offset(glyph_id, self.coords()) {
1459 bearing += offset + 0.5;
1461 }
1462 }
1463 }
1464
1465 i16::try_num_from(bearing)
1466 }
1467
1468 #[cfg(not(feature = "variable-fonts"))] {
1469 self.tables.hmtx?.side_bearing(glyph_id)
1470 }
1471 }
1472
1473 #[inline]
1477 pub fn glyph_ver_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1478 #[cfg(feature = "variable-fonts")] {
1479 let mut bearing = self.tables.vmtx?.side_bearing(glyph_id)? as f32;
1480
1481 if self.is_variable() {
1482 if let Some(vvar) = self.tables.vvar {
1484 if let Some(offset) = vvar.side_bearing_offset(glyph_id, self.coords()) {
1485 bearing += offset + 0.5;
1487 }
1488 }
1489 }
1490
1491 i16::try_num_from(bearing)
1492 }
1493
1494 #[cfg(not(feature = "variable-fonts"))] {
1495 self.tables.vmtx?.side_bearing(glyph_id)
1496 }
1497 }
1498
1499 pub fn glyph_y_origin(&self, glyph_id: GlyphId) -> Option<i16> {
1502 self.tables.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))
1503 }
1504
1505 #[cfg(feature = "glyph-names")]
1511 #[inline]
1512 pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&str> {
1513 if let Some(name) = self.tables.post.and_then(|post| post.names.get(glyph_id)) {
1514 return Some(name);
1515 }
1516
1517 if let Some(name) = self.tables.cff.as_ref().and_then(|cff1| cff1.glyph_name(glyph_id)) {
1518 return Some(name);
1519 }
1520
1521 None
1522 }
1523
1524 #[inline]
1577 pub fn outline_glyph(
1578 &self,
1579 glyph_id: GlyphId,
1580 builder: &mut dyn OutlineBuilder,
1581 ) -> Option<Rect> {
1582 #[cfg(feature = "variable-fonts")] {
1583 if let Some(ref gvar) = self.tables.gvar {
1584 return gvar.outline(self.tables.glyf?, self.coords(), glyph_id, builder);
1585 }
1586 }
1587
1588 if let Some(table) = self.tables.glyf {
1589 return table.outline(glyph_id, builder);
1590 }
1591
1592 if let Some(ref cff) = self.tables.cff {
1593 return cff.outline(glyph_id, builder).ok();
1594 }
1595
1596 #[cfg(feature = "variable-fonts")] {
1597 if let Some(ref cff2) = self.tables.cff2 {
1598 return cff2.outline(self.coords(), glyph_id, builder).ok();
1599 }
1600 }
1601
1602 None
1603 }
1604
1605 #[inline]
1622 pub fn glyph_bounding_box(&self, glyph_id: GlyphId) -> Option<Rect> {
1623 self.outline_glyph(glyph_id, &mut DummyOutline)
1624 }
1625
1626 #[inline]
1628 pub fn global_bounding_box(&self) -> Rect {
1629 self.tables.head.global_bbox
1630 }
1631
1632 #[inline]
1654 pub fn glyph_raster_image(&self, glyph_id: GlyphId, pixels_per_em: u16) -> Option<RasterGlyphImage> {
1655 if let Some(table) = self.tables.sbix {
1656 if let Some(strike) = table.best_strike(pixels_per_em) {
1657 return strike.get(glyph_id);
1658 }
1659 }
1660
1661 if let Some(cbdt) = self.tables.cbdt {
1662 return cbdt.get(glyph_id, pixels_per_em);
1663 }
1664
1665 None
1666 }
1667
1668 #[inline]
1680 pub fn glyph_svg_image(&self, glyph_id: GlyphId) -> Option<&'a [u8]> {
1681 self.tables.svg.and_then(|svg| svg.documents.find(glyph_id))
1682 }
1683
1684 #[cfg(feature = "variable-fonts")]
1686 #[inline]
1687 pub fn variation_axes(&self) -> LazyArray16<'a, VariationAxis> {
1688 self.tables.fvar.map(|fvar| fvar.axes).unwrap_or_default()
1689 }
1690
1691 #[cfg(feature = "variable-fonts")]
1701 pub fn set_variation(&mut self, axis: Tag, value: f32) -> Option<()> {
1702 if !self.is_variable() {
1703 return None;
1704 }
1705
1706 let v = self.variation_axes().into_iter().enumerate().find(|(_, a)| a.tag == axis);
1707 if let Some((idx, a)) = v {
1708 if idx >= MAX_VAR_COORDS {
1709 return None;
1710 }
1711
1712 self.coordinates.data[idx] = a.normalized_value(value);
1713 } else {
1714 return None;
1715 }
1716
1717 if let Some(avar) = self.tables.avar {
1719 let _ = avar.map_coordinates(self.coordinates.as_mut_slice());
1721 }
1722
1723 Some(())
1724 }
1725
1726 #[cfg(feature = "variable-fonts")]
1728 #[inline]
1729 pub fn variation_coordinates(&self) -> &[NormalizedCoordinate] {
1730 self.coordinates.as_slice()
1731 }
1732
1733 #[cfg(feature = "variable-fonts")]
1735 #[inline]
1736 pub fn has_non_default_variation_coordinates(&self) -> bool {
1737 self.coordinates.as_slice().iter().any(|c| c.0 != 0)
1738 }
1739
1740 #[cfg(feature = "variable-fonts")]
1741 #[inline]
1742 fn metrics_var_offset(&self, tag: Tag) -> f32 {
1743 self.tables.mvar.and_then(|table| table.metric_offset(tag, self.coords())).unwrap_or(0.0)
1744 }
1745
1746 #[inline]
1747 fn apply_metrics_variation(&self, tag: Tag, mut value: i16) -> i16 {
1748 self.apply_metrics_variation_to(tag, &mut value);
1749 value
1750 }
1751
1752
1753 #[cfg(feature = "variable-fonts")]
1754 #[inline]
1755 fn apply_metrics_variation_to(&self, tag: Tag, value: &mut i16) {
1756 if self.is_variable() {
1757 let v = f32::from(*value) + self.metrics_var_offset(tag);
1758 if let Some(v) = i16::try_num_from(v) {
1760 *value = v;
1761 }
1762 }
1763 }
1764
1765 #[cfg(not(feature = "variable-fonts"))]
1766 #[inline]
1767 fn apply_metrics_variation_to(&self, _: Tag, _: &mut i16) {
1768 }
1769
1770 #[cfg(feature = "variable-fonts")]
1771 #[inline]
1772 fn coords(&self) -> &[NormalizedCoordinate] {
1773 self.coordinates.as_slice()
1774 }
1775}
1776
1777struct DefaultTableProvider<'a> {
1778 data: &'a [u8],
1779 tables: LazyArrayIter16<'a, TableRecord>,
1780}
1781
1782impl<'a> Iterator for DefaultTableProvider<'a> {
1783 type Item = Result<(Tag, Option<&'a [u8]>), FaceParsingError>;
1784
1785 #[inline]
1786 fn next(&mut self) -> Option<Self::Item> {
1787 self.tables.next().map(|table| {
1788 Ok((table.table_tag, {
1789 let offset = usize::num_from(table.offset);
1790 let length = usize::num_from(table.length);
1791 let end = offset.checked_add(length).ok_or(FaceParsingError::MalformedFont)?;
1792 let range = offset..end;
1793 self.data.get(range)
1794 }))
1795 })
1796 }
1797}
1798
1799impl core::fmt::Debug for Face<'_> {
1800 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1801 write!(f, "Face()")
1802 }
1803}
1804
1805#[inline]
1809pub fn fonts_in_collection(data: &[u8]) -> Option<u32> {
1810 let mut s = Stream::new(data);
1811 if s.read::<Magic>()? != Magic::FontCollection {
1812 return None;
1813 }
1814
1815 s.skip::<u32>(); s.read::<u32>()
1817}