1#![doc(html_root_url = "https://docs.rs/ttf-parser/0.10.1")]
28
29#![no_std]
30#![forbid(unsafe_code)]
31#![warn(missing_docs)]
32#![warn(missing_copy_implementations)]
33#![warn(missing_debug_implementations)]
34
35#[cfg(feature = "std")]
36#[macro_use]
37extern crate std;
38
39use core::fmt;
40use core::num::NonZeroU16;
41
42macro_rules! try_opt_or {
43 ($value:expr, $ret:expr) => {
44 match $value {
45 Some(v) => v,
46 None => return $ret,
47 }
48 };
49}
50
51pub mod parser;
52mod ggg;
53mod tables;
54#[cfg(feature = "variable-fonts")] mod var_store;
55
56#[cfg(feature = "std")]
57mod writer;
58
59use tables::*;
60use parser::{Stream, FromData, NumFrom, TryNumFrom, LazyArray16, Offset32, Offset};
61use head::IndexToLocationFormat;
62
63#[cfg(feature = "variable-fonts")] pub use fvar::{VariationAxes, VariationAxis};
64pub use gdef::GlyphClass;
65pub use ggg::*;
66pub use name::*;
67pub use os2::*;
68pub use tables::{cmap, kern};
69
70
71#[repr(transparent)]
73#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
74pub struct GlyphId(pub u16);
75
76impl FromData for GlyphId {
77 const SIZE: usize = 2;
78
79 #[inline]
80 fn parse(data: &[u8]) -> Option<Self> {
81 u16::parse(data).map(GlyphId)
82 }
83}
84
85
86#[derive(Clone, Copy, PartialEq, Debug)]
90enum Magic {
91 TrueType,
92 OpenType,
93 FontCollection,
94}
95
96impl FromData for Magic {
97 const SIZE: usize = 4;
98
99 #[inline]
100 fn parse(data: &[u8]) -> Option<Self> {
101 match u32::parse(data)? {
102 0x00010000 | 0x74727565 => Some(Magic::TrueType),
103 0x4F54544F => Some(Magic::OpenType),
104 0x74746366 => Some(Magic::FontCollection),
105 _ => None,
106 }
107 }
108}
109
110
111#[repr(transparent)]
118#[derive(Clone, Copy, PartialEq, Default, Debug)]
119pub struct NormalizedCoordinate(i16);
120
121impl From<i16> for NormalizedCoordinate {
122 #[inline]
126 fn from(n: i16) -> Self {
127 NormalizedCoordinate(parser::i16_bound(-16384, n, 16384))
128 }
129}
130
131impl From<f32> for NormalizedCoordinate {
132 #[inline]
136 fn from(n: f32) -> Self {
137 NormalizedCoordinate((parser::f32_bound(-1.0, n, 1.0) * 16384.0) as i16)
138 }
139}
140
141impl NormalizedCoordinate {
142 #[inline]
144 pub fn get(self) -> i16 {
145 self.0
146 }
147}
148
149
150#[derive(Clone, Copy, PartialEq, Debug)]
160pub struct Variation {
161 pub axis: Tag,
163 pub value: f32,
165}
166
167
168#[repr(transparent)]
170#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
171pub struct Tag(pub u32);
172
173impl Tag {
174 #[inline]
176 pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
177 Tag(((bytes[0] as u32) << 24) | ((bytes[1] as u32) << 16) |
178 ((bytes[2] as u32) << 8) | (bytes[3] as u32))
179 }
180
181 #[inline]
189 pub fn from_bytes_lossy(bytes: &[u8]) -> Self {
190 if bytes.is_empty() {
191 return Tag::from_bytes(&[0, 0, 0, 0]);
192 }
193
194 let mut iter = bytes.iter().cloned().chain(core::iter::repeat(b' '));
195 Tag::from_bytes(&[
196 iter.next().unwrap(),
197 iter.next().unwrap(),
198 iter.next().unwrap(),
199 iter.next().unwrap(),
200 ])
201 }
202
203 #[inline]
205 pub const fn to_bytes(self) -> [u8; 4] {
206 [
207 (self.0 >> 24 & 0xff) as u8,
208 (self.0 >> 16 & 0xff) as u8,
209 (self.0 >> 8 & 0xff) as u8,
210 (self.0 >> 0 & 0xff) as u8,
211 ]
212 }
213
214 #[inline]
216 pub const fn to_chars(self) -> [char; 4] {
217 [
218 (self.0 >> 24 & 0xff) as u8 as char,
219 (self.0 >> 16 & 0xff) as u8 as char,
220 (self.0 >> 8 & 0xff) as u8 as char,
221 (self.0 >> 0 & 0xff) as u8 as char,
222 ]
223 }
224
225 #[inline]
227 pub const fn is_null(&self) -> bool {
228 self.0 == 0
229 }
230
231 #[inline]
233 pub const fn as_u32(&self) -> u32 {
234 self.0
235 }
236
237 #[inline]
239 pub fn to_lowercase(&self) -> Self {
240 let b = self.to_bytes();
241 Tag::from_bytes(&[
242 b[0].to_ascii_lowercase(),
243 b[1].to_ascii_lowercase(),
244 b[2].to_ascii_lowercase(),
245 b[3].to_ascii_lowercase(),
246 ])
247 }
248
249 #[inline]
251 pub fn to_uppercase(&self) -> Self {
252 let b = self.to_bytes();
253 Tag::from_bytes(&[
254 b[0].to_ascii_uppercase(),
255 b[1].to_ascii_uppercase(),
256 b[2].to_ascii_uppercase(),
257 b[3].to_ascii_uppercase(),
258 ])
259 }
260}
261
262impl core::fmt::Debug for Tag {
263 #[inline]
264 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
265 write!(f, "Tag({})", self)
266 }
267}
268
269impl core::fmt::Display for Tag {
270 #[inline]
271 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
272 let b = self.to_chars();
273 write!(
274 f,
275 "{}{}{}{}",
276 b.get(0).unwrap_or(&' '),
277 b.get(1).unwrap_or(&' '),
278 b.get(2).unwrap_or(&' '),
279 b.get(3).unwrap_or(&' ')
280 )
281 }
282}
283
284impl FromData for Tag {
285 const SIZE: usize = 4;
286
287 #[inline]
288 fn parse(data: &[u8]) -> Option<Self> {
289 u32::parse(data).map(Tag)
290 }
291}
292
293
294
295#[repr(C)]
299#[derive(Clone, Copy, PartialEq, Debug)]
300pub struct LineMetrics {
301 pub position: i16,
303
304 pub thickness: i16,
306}
307
308
309#[repr(C)]
311#[derive(Clone, Copy, PartialEq, Debug)]
312#[allow(missing_docs)]
313pub struct Rect {
314 pub x_min: i16,
315 pub y_min: i16,
316 pub x_max: i16,
317 pub y_max: i16,
318}
319
320impl Rect {
321 #[inline]
323 pub fn width(&self) -> i16 {
324 self.x_max - self.x_min
325 }
326
327 #[inline]
329 pub fn height(&self) -> i16 {
330 self.y_max - self.y_min
331 }
332}
333
334
335#[derive(Clone, Copy, Debug)]
336pub(crate) struct BBox {
337 x_min: f32,
338 y_min: f32,
339 x_max: f32,
340 y_max: f32,
341}
342
343impl BBox {
344 #[inline]
345 fn new() -> Self {
346 BBox {
347 x_min: core::f32::MAX,
348 y_min: core::f32::MAX,
349 x_max: core::f32::MIN,
350 y_max: core::f32::MIN,
351 }
352 }
353
354 #[inline]
355 fn is_default(&self) -> bool {
356 self.x_min == core::f32::MAX &&
357 self.y_min == core::f32::MAX &&
358 self.x_max == core::f32::MIN &&
359 self.y_max == core::f32::MIN
360 }
361
362 #[inline]
363 fn extend_by(&mut self, x: f32, y: f32) {
364 self.x_min = self.x_min.min(x);
365 self.y_min = self.y_min.min(y);
366 self.x_max = self.x_max.max(x);
367 self.y_max = self.y_max.max(y);
368 }
369
370 #[inline]
371 fn to_rect(&self) -> Option<Rect> {
372 Some(Rect {
373 x_min: i16::try_num_from(self.x_min)?,
374 y_min: i16::try_num_from(self.y_min)?,
375 x_max: i16::try_num_from(self.x_max)?,
376 y_max: i16::try_num_from(self.y_max)?,
377 })
378 }
379}
380
381
382pub trait OutlineBuilder {
384 fn move_to(&mut self, x: f32, y: f32);
388
389 fn line_to(&mut self, x: f32, y: f32);
391
392 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32);
394
395 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32);
397
398 fn close(&mut self);
402}
403
404
405struct DummyOutline;
406impl OutlineBuilder for DummyOutline {
407 fn move_to(&mut self, _: f32, _: f32) {}
408 fn line_to(&mut self, _: f32, _: f32) {}
409 fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {}
410 fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {}
411 fn close(&mut self) {}
412}
413
414
415#[allow(missing_docs)]
417#[derive(Clone, Copy, PartialEq, Debug)]
418pub enum RasterImageFormat {
419 PNG,
420}
421
422
423#[derive(Clone, Copy, PartialEq, Debug)]
427pub struct RasterGlyphImage<'a> {
428 pub x: i16,
430
431 pub y: i16,
433
434 pub width: u16,
438
439 pub height: u16,
443
444 pub pixels_per_em: u16,
446
447 pub format: RasterImageFormat,
449
450 pub data: &'a [u8],
452}
453
454
455#[repr(C)]
457#[derive(Clone, Copy, PartialEq, Debug)]
458#[allow(missing_docs)]
459pub enum TableName {
460 AxisVariations = 0,
461 CharacterToGlyphIndexMapping,
462 ColorBitmapData,
463 ColorBitmapLocation,
464 CompactFontFormat,
465 CompactFontFormat2,
466 FontVariations,
467 GlyphData,
468 GlyphDefinition,
469 GlyphVariations,
470 Header,
471 HorizontalHeader,
472 HorizontalMetrics,
473 HorizontalMetricsVariations,
474 IndexToLocation,
475 Kerning,
476 MaximumProfile,
477 MetricsVariations,
478 Naming,
479 PostScript,
480 ScalableVectorGraphics,
481 StandardBitmapGraphics,
482 VerticalHeader,
483 VerticalMetrics,
484 VerticalMetricsVariations,
485 VerticalOrigin,
486 WindowsMetrics,
487}
488
489
490#[derive(Clone, Copy)]
491struct TableRecord {
492 table_tag: Tag,
493 #[allow(dead_code)]
494 check_sum: u32,
495 offset: u32,
496 length: u32,
497}
498
499impl FromData for TableRecord {
500 const SIZE: usize = 16;
501
502 #[inline]
503 fn parse(data: &[u8]) -> Option<Self> {
504 let mut s = Stream::new(data);
505 Some(TableRecord {
506 table_tag: s.read::<Tag>()?,
507 check_sum: s.read::<u32>()?,
508 offset: s.read::<u32>()?,
509 length: s.read::<u32>()?,
510 })
511 }
512}
513
514
515#[cfg(feature = "variable-fonts")]
516const MAX_VAR_COORDS: usize = 32;
517
518#[cfg(feature = "variable-fonts")]
519#[derive(Clone, Default)]
520struct VarCoords {
521 data: [NormalizedCoordinate; MAX_VAR_COORDS],
522 len: u8,
523}
524
525#[cfg(feature = "variable-fonts")]
526impl VarCoords {
527 #[inline]
528 fn as_slice(&self) -> &[NormalizedCoordinate] {
529 &self.data[0..usize::from(self.len)]
530 }
531
532 #[inline]
533 fn as_mut_slice(&mut self) -> &mut [NormalizedCoordinate] {
534 let end = usize::from(self.len);
535 &mut self.data[0..end]
536 }
537}
538
539
540#[derive(Clone, Copy, PartialEq, Debug)]
542pub enum FaceParsingError {
543 MalformedFont,
547
548 UnknownMagic,
550
551 FaceIndexOutOfBounds,
553
554 NoHeadTable,
556
557 NoHheaTable,
559
560 NoMaxpTable,
562}
563
564impl core::fmt::Display for FaceParsingError {
565 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
566 match self {
567 FaceParsingError::MalformedFont => write!(f, "malformed font"),
568 FaceParsingError::UnknownMagic => write!(f, "unknown magic"),
569 FaceParsingError::FaceIndexOutOfBounds => write!(f, "face index is out of bounds"),
570 FaceParsingError::NoHeadTable => write!(f, "the head table is missing or malformed"),
571 FaceParsingError::NoHheaTable => write!(f, "the hhea table is missing or malformed"),
572 FaceParsingError::NoMaxpTable => write!(f, "the maxp table is missing or malformed"),
573 }
574 }
575}
576
577#[cfg(feature = "std")]
578impl std::error::Error for FaceParsingError {}
579
580
581#[derive(Clone)]
583pub struct Face<'a> {
584 font_data: &'a [u8], table_records: LazyArray16<'a, TableRecord>,
586
587 cbdt: Option<&'a [u8]>,
588 cblc: Option<&'a [u8]>,
589 cff1: Option<cff1::Metadata<'a>>,
590 cmap: Option<cmap::Subtables<'a>>,
591 gdef: Option<gdef::Table<'a>>,
592 glyf: Option<&'a [u8]>,
593 head: &'a [u8],
594 hhea: &'a [u8],
595 hmtx: Option<hmtx::Table<'a>>,
596 kern: Option<kern::Subtables<'a>>,
597 loca: Option<loca::Table<'a>>,
598 name: Option<name::Names<'a>>,
599 os_2: Option<os2::Table<'a>>,
600 post: Option<post::Table<'a>>,
601 vhea: Option<&'a [u8]>,
602 vmtx: Option<hmtx::Table<'a>>,
603 sbix: Option<&'a [u8]>,
604 svg_: Option<&'a [u8]>,
605 vorg: Option<vorg::Table<'a>>,
606
607 #[cfg(feature = "variable-fonts")] avar: Option<avar::Table<'a>>,
609 #[cfg(feature = "variable-fonts")] cff2: Option<cff2::Metadata<'a>>,
610 #[cfg(feature = "variable-fonts")] fvar: Option<fvar::Table<'a>>,
611 #[cfg(feature = "variable-fonts")] gvar: Option<gvar::Table<'a>>,
612 #[cfg(feature = "variable-fonts")] hvar: Option<hvar::Table<'a>>,
613 #[cfg(feature = "variable-fonts")] mvar: Option<mvar::Table<'a>>,
614 #[cfg(feature = "variable-fonts")] vvar: Option<hvar::Table<'a>>,
615
616 number_of_glyphs: NonZeroU16,
617 #[cfg(feature = "variable-fonts")] coordinates: VarCoords,
618}
619
620impl<'a> Face<'a> {
621 pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
633 let mut s = Stream::new(data);
636
637 let magic: Magic = s.read().ok_or(FaceParsingError::UnknownMagic)?;
639 if magic == Magic::FontCollection {
640 s.skip::<u32>(); let number_of_faces: u32 = s.read().ok_or(FaceParsingError::MalformedFont)?;
642 let offsets = s.read_array32::<Offset32>(number_of_faces)
643 .ok_or(FaceParsingError::MalformedFont)?;
644
645 let face_offset = offsets.get(index).ok_or(FaceParsingError::FaceIndexOutOfBounds)?;
646 let face_offset = face_offset.to_usize().checked_sub(s.offset())
649 .ok_or(FaceParsingError::MalformedFont)?;
650 s.advance_checked(face_offset).ok_or(FaceParsingError::MalformedFont)?;
651
652 let magic: Magic = s.read().ok_or(FaceParsingError::UnknownMagic)?;
655 if magic == Magic::FontCollection {
657 return Err(FaceParsingError::UnknownMagic);
658 }
659 }
660
661 let num_tables: u16 = s.read().ok_or(FaceParsingError::MalformedFont)?;
662 s.advance(6); let tables = s.read_array16::<TableRecord>(num_tables)
664 .ok_or(FaceParsingError::MalformedFont)?;
665
666 let mut face = Face {
667 font_data: data,
668 table_records: tables,
669 cbdt: None,
670 cblc: None,
671 cff1: None,
672 cmap: None,
673 gdef: None,
674 glyf: None,
675 head: &[],
676 hhea: &[],
677 hmtx: None,
678 kern: None,
679 loca: None,
680 name: None,
681 os_2: None,
682 post: None,
683 vhea: None,
684 vmtx: None,
685 sbix: None,
686 svg_: None,
687 vorg: None,
688 #[cfg(feature = "variable-fonts")] avar: None,
689 #[cfg(feature = "variable-fonts")] cff2: None,
690 #[cfg(feature = "variable-fonts")] fvar: None,
691 #[cfg(feature = "variable-fonts")] gvar: None,
692 #[cfg(feature = "variable-fonts")] hvar: None,
693 #[cfg(feature = "variable-fonts")] mvar: None,
694 #[cfg(feature = "variable-fonts")] vvar: None,
695 number_of_glyphs: NonZeroU16::new(1).unwrap(), #[cfg(feature = "variable-fonts")] coordinates: VarCoords::default(),
697 };
698
699 let mut number_of_glyphs = None;
700 let mut hmtx = None;
701 let mut vmtx = None;
702 let mut loca = None;
703
704 for table in tables {
705 let offset = usize::num_from(table.offset);
706 let length = usize::num_from(table.length);
707 let end = offset.checked_add(length).ok_or(FaceParsingError::MalformedFont)?;
708 let range = offset..end;
709
710 match &table.table_tag.to_bytes() {
711 b"CBDT" => face.cbdt = data.get(range),
712 b"CBLC" => face.cblc = data.get(range),
713 b"CFF " => face.cff1 = data.get(range).and_then(|data| cff1::parse_metadata(data)),
714 #[cfg(feature = "variable-fonts")]
715 b"CFF2" => face.cff2 = data.get(range).and_then(|data| cff2::parse_metadata(data)),
716 b"GDEF" => face.gdef = data.get(range).and_then(|data| gdef::Table::parse(data)),
717 #[cfg(feature = "variable-fonts")]
718 b"HVAR" => face.hvar = data.get(range).and_then(|data| hvar::Table::parse(data)),
719 #[cfg(feature = "variable-fonts")]
720 b"MVAR" => face.mvar = data.get(range).and_then(|data| mvar::Table::parse(data)),
721 b"OS/2" => face.os_2 = data.get(range).and_then(|data| os2::Table::parse(data)),
722 b"SVG " => face.svg_ = data.get(range),
723 b"VORG" => face.vorg = data.get(range).and_then(|data| vorg::Table::parse(data)),
724 #[cfg(feature = "variable-fonts")]
725 b"VVAR" => face.vvar = data.get(range).and_then(|data| hvar::Table::parse(data)),
726 #[cfg(feature = "variable-fonts")]
727 b"avar" => face.avar = data.get(range).and_then(|data| avar::Table::parse(data)),
728 b"cmap" => face.cmap = data.get(range).and_then(|data| cmap::parse(data)),
729 #[cfg(feature = "variable-fonts")]
730 b"fvar" => face.fvar = data.get(range).and_then(|data| fvar::Table::parse(data)),
731 b"glyf" => face.glyf = data.get(range),
732 #[cfg(feature = "variable-fonts")]
733 b"gvar" => face.gvar = data.get(range).and_then(|data| gvar::Table::parse(data)),
734 b"head" => face.head = data.get(range).and_then(|data| head::parse(data)).unwrap_or_default(),
735 b"hhea" => face.hhea = data.get(range).and_then(|data| hhea::parse(data)).unwrap_or_default(),
736 b"hmtx" => hmtx = data.get(range),
737 b"kern" => face.kern = data.get(range).and_then(|data| kern::parse(data)),
738 b"loca" => loca = data.get(range),
739 b"maxp" => number_of_glyphs = data.get(range).and_then(|data| maxp::parse(data)),
740 b"name" => face.name = data.get(range).and_then(|data| name::parse(data)),
741 b"post" => face.post = data.get(range).and_then(|data| post::Table::parse(data)),
742 b"sbix" => face.sbix = data.get(range),
743 b"vhea" => face.vhea = data.get(range).and_then(|data| vhea::parse(data)),
744 b"vmtx" => vmtx = data.get(range),
745 _ => {}
746 }
747 }
748
749 if face.head.is_empty() {
750 return Err(FaceParsingError::NoHeadTable);
751 }
752
753 if face.hhea.is_empty() {
754 return Err(FaceParsingError::NoHheaTable);
755 }
756
757 face.number_of_glyphs = match number_of_glyphs {
758 Some(n) => n,
759 None => return Err(FaceParsingError::NoMaxpTable),
760 };
761
762 #[cfg(feature = "variable-fonts")] {
763 if let Some(ref fvar) = face.fvar {
764 face.coordinates.len = fvar.axes().count().min(MAX_VAR_COORDS) as u8;
765 }
766 }
767
768 if let Some(data) = hmtx {
769 if let Some(number_of_h_metrics) = hhea::number_of_h_metrics(face.hhea) {
770 face.hmtx = hmtx::Table::parse(data, number_of_h_metrics, face.number_of_glyphs);
771 }
772 }
773
774 if let (Some(vhea), Some(data)) = (face.vhea, vmtx) {
775 if let Some(number_of_v_metrics) = vhea::num_of_long_ver_metrics(vhea) {
776 face.vmtx = hmtx::Table::parse(data, number_of_v_metrics, face.number_of_glyphs);
777 }
778 }
779
780 if let Some(data) = loca {
781 if let Some(format) = head::index_to_loc_format(face.head) {
782 face.loca = loca::Table::parse(data, face.number_of_glyphs, format);
783 }
784 }
785
786 Ok(face)
787 }
788
789 #[cfg(feature = "variable-fonts")]
793 #[inline]
794 pub fn has_table(&self, name: TableName) -> bool {
795 match name {
796 TableName::Header => true,
797 TableName::HorizontalHeader => true,
798 TableName::MaximumProfile => true,
799 TableName::AxisVariations => self.avar.is_some(),
800 TableName::CharacterToGlyphIndexMapping => self.cmap.is_some(),
801 TableName::ColorBitmapData => self.cbdt.is_some(),
802 TableName::ColorBitmapLocation => self.cblc.is_some(),
803 TableName::CompactFontFormat => self.cff1.is_some(),
804 TableName::CompactFontFormat2 => self.cff2.is_some(),
805 TableName::FontVariations => self.fvar.is_some(),
806 TableName::GlyphData => self.glyf.is_some(),
807 TableName::GlyphDefinition => self.gdef.is_some(),
808 TableName::GlyphVariations => self.gvar.is_some(),
809 TableName::HorizontalMetrics => self.hmtx.is_some(),
810 TableName::HorizontalMetricsVariations => self.hvar.is_some(),
811 TableName::IndexToLocation => self.loca.is_some(),
812 TableName::Kerning => self.kern.is_some(),
813 TableName::MetricsVariations => self.mvar.is_some(),
814 TableName::Naming => self.name.is_some(),
815 TableName::PostScript => self.post.is_some(),
816 TableName::ScalableVectorGraphics => self.svg_.is_some(),
817 TableName::StandardBitmapGraphics => self.sbix.is_some(),
818 TableName::VerticalHeader => self.vhea.is_some(),
819 TableName::VerticalMetrics => self.vmtx.is_some(),
820 TableName::VerticalMetricsVariations => self.vvar.is_some(),
821 TableName::VerticalOrigin => self.vorg.is_some(),
822 TableName::WindowsMetrics => self.os_2.is_some(),
823 }
824 }
825
826 #[cfg(not(feature = "variable-fonts"))]
830 #[inline]
831 pub fn has_table(&self, name: TableName) -> bool {
832 match name {
833 TableName::Header => true,
834 TableName::HorizontalHeader => true,
835 TableName::MaximumProfile => true,
836 TableName::AxisVariations => false,
837 TableName::CharacterToGlyphIndexMapping => self.cmap.is_some(),
838 TableName::ColorBitmapData => self.cbdt.is_some(),
839 TableName::ColorBitmapLocation => self.cblc.is_some(),
840 TableName::CompactFontFormat => self.cff1.is_some(),
841 TableName::CompactFontFormat2 => false,
842 TableName::FontVariations => false,
843 TableName::GlyphData => self.glyf.is_some(),
844 TableName::GlyphDefinition => self.gdef.is_some(),
845 TableName::GlyphVariations => false,
846 TableName::HorizontalMetrics => self.hmtx.is_some(),
847 TableName::HorizontalMetricsVariations => false,
848 TableName::IndexToLocation => self.loca.is_some(),
849 TableName::Kerning => self.kern.is_some(),
850 TableName::MetricsVariations => false,
851 TableName::Naming => self.name.is_some(),
852 TableName::PostScript => self.post.is_some(),
853 TableName::ScalableVectorGraphics => self.svg_.is_some(),
854 TableName::StandardBitmapGraphics => self.sbix.is_some(),
855 TableName::VerticalHeader => self.vhea.is_some(),
856 TableName::VerticalMetrics => self.vmtx.is_some(),
857 TableName::VerticalMetricsVariations => false,
858 TableName::VerticalOrigin => self.vorg.is_some(),
859 TableName::WindowsMetrics => self.os_2.is_some(),
860 }
861 }
862
863 pub fn table_data(&self, tag: Tag) -> Option<&'a [u8]> {
867 let (_, table) = self.table_records.binary_search_by(|record| record.table_tag.cmp(&tag))?;
868 let offset = usize::num_from(table.offset);
869 let length = usize::num_from(table.length);
870 let end = offset.checked_add(length)?;
871 self.font_data.get(offset..end)
872 }
873
874 #[inline]
880 pub fn names(&self) -> Names {
881 self.name.unwrap_or_default()
882 }
883
884 #[inline]
888 pub fn is_regular(&self) -> bool {
889 try_opt_or!(self.os_2, false).is_regular()
890 }
891
892 #[inline]
896 pub fn is_italic(&self) -> bool {
897 try_opt_or!(self.os_2, false).is_italic()
898 }
899
900 #[inline]
904 pub fn is_bold(&self) -> bool {
905 try_opt_or!(self.os_2, false).is_bold()
906 }
907
908 #[inline]
912 pub fn is_oblique(&self) -> bool {
913 try_opt_or!(self.os_2, false).is_oblique()
914 }
915
916 #[inline]
920 pub fn is_monospaced(&self) -> bool {
921 try_opt_or!(self.post, false).is_monospaced()
922 }
923
924 #[inline]
928 pub fn is_variable(&self) -> bool {
929 #[cfg(feature = "variable-fonts")] {
930 self.fvar.is_some()
932 }
933
934 #[cfg(not(feature = "variable-fonts"))] {
935 false
936 }
937 }
938
939 #[inline]
943 pub fn weight(&self) -> Weight {
944 try_opt_or!(self.os_2, Weight::default()).weight()
945 }
946
947 #[inline]
951 pub fn width(&self) -> Width {
952 try_opt_or!(self.os_2, Width::default()).width()
953 }
954
955 #[inline]
959 pub fn italic_angle(&self) -> Option<f32> {
960 self.post.map(|table| table.italic_angle())
961 }
962
963 #[inline]
964 fn use_typo_metrics(&self) -> Option<os2::Table> {
965 self.os_2.filter(|table| table.is_use_typo_metrics())
966 }
967
968 #[inline]
972 pub fn ascender(&self) -> i16 {
973 if let Some(os_2) = self.use_typo_metrics() {
974 let v = os_2.typo_ascender();
975 self.apply_metrics_variation(Tag::from_bytes(b"hasc"), v)
976 } else {
977 hhea::ascender(self.hhea)
978 }
979 }
980
981 #[inline]
985 pub fn descender(&self) -> i16 {
986 if let Some(os_2) = self.use_typo_metrics() {
987 let v = os_2.typo_descender();
988 self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), v)
989 } else {
990 hhea::descender(self.hhea)
991 }
992 }
993
994 #[inline]
998 pub fn height(&self) -> i16 {
999 self.ascender() - self.descender()
1000 }
1001
1002 #[inline]
1006 pub fn line_gap(&self) -> i16 {
1007 if let Some(os_2) = self.use_typo_metrics() {
1008 let v = os_2.typo_line_gap();
1009 self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), v)
1010 } else {
1011 hhea::line_gap(self.hhea)
1012 }
1013 }
1014
1015 #[inline]
1024 pub fn typographic_ascender(&self) -> Option<i16> {
1025 self.os_2.map(|table| {
1026 let v = table.typo_ascender();
1027 self.apply_metrics_variation(Tag::from_bytes(b"hasc"), v)
1028 })
1029 }
1030
1031 #[inline]
1040 pub fn typographic_descender(&self) -> Option<i16> {
1041 self.os_2.map(|table| {
1042 let v = table.typo_descender();
1043 self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), v)
1044 })
1045 }
1046
1047 #[inline]
1056 pub fn typographic_line_gap(&self) -> Option<i16> {
1057 self.os_2.map(|table| {
1058 let v = table.typo_line_gap();
1059 self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), v)
1060 })
1061 }
1062
1063 #[inline]
1069 pub fn vertical_ascender(&self) -> Option<i16> {
1070 self.vhea.map(vhea::ascender)
1071 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vasc"), v))
1072 }
1073
1074 #[inline]
1078 pub fn vertical_descender(&self) -> Option<i16> {
1079 self.vhea.map(vhea::descender)
1080 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vdsc"), v))
1081 }
1082
1083 #[inline]
1087 pub fn vertical_height(&self) -> Option<i16> {
1088 Some(self.vertical_ascender()? - self.vertical_descender()?)
1089 }
1090
1091 #[inline]
1095 pub fn vertical_line_gap(&self) -> Option<i16> {
1096 self.vhea.map(vhea::line_gap)
1097 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vlgp"), v))
1098 }
1099
1100 #[inline]
1104 pub fn units_per_em(&self) -> Option<u16> {
1105 head::units_per_em(self.head)
1106 }
1107
1108 #[inline]
1114 pub fn x_height(&self) -> Option<i16> {
1115 self.os_2.and_then(|os_2| os_2.x_height())
1116 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"xhgt"), v))
1117 }
1118
1119 #[inline]
1125 pub fn capital_height(&self) -> Option<i16> {
1126 self.os_2.and_then(|os_2| os_2.cap_height())
1127 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"cpht"), v))
1128 }
1129
1130 #[inline]
1136 pub fn underline_metrics(&self) -> Option<LineMetrics> {
1137 let mut metrics = self.post?.underline_metrics();
1138
1139 if self.is_variable() {
1140 self.apply_metrics_variation_to(Tag::from_bytes(b"undo"), &mut metrics.position);
1141 self.apply_metrics_variation_to(Tag::from_bytes(b"unds"), &mut metrics.thickness);
1142 }
1143
1144 Some(metrics)
1145 }
1146
1147 #[inline]
1153 pub fn strikeout_metrics(&self) -> Option<LineMetrics> {
1154 let mut metrics = self.os_2?.strikeout_metrics();
1155
1156 if self.is_variable() {
1157 self.apply_metrics_variation_to(Tag::from_bytes(b"stro"), &mut metrics.position);
1158 self.apply_metrics_variation_to(Tag::from_bytes(b"strs"), &mut metrics.thickness);
1159 }
1160
1161 Some(metrics)
1162 }
1163
1164 #[inline]
1170 pub fn subscript_metrics(&self) -> Option<ScriptMetrics> {
1171 let mut metrics = self.os_2?.subscript_metrics();
1172
1173 if self.is_variable() {
1174 self.apply_metrics_variation_to(Tag::from_bytes(b"sbxs"), &mut metrics.x_size);
1175 self.apply_metrics_variation_to(Tag::from_bytes(b"sbys"), &mut metrics.y_size);
1176 self.apply_metrics_variation_to(Tag::from_bytes(b"sbxo"), &mut metrics.x_offset);
1177 self.apply_metrics_variation_to(Tag::from_bytes(b"sbyo"), &mut metrics.y_offset);
1178 }
1179
1180 Some(metrics)
1181 }
1182
1183 #[inline]
1189 pub fn superscript_metrics(&self) -> Option<ScriptMetrics> {
1190 let mut metrics = self.os_2?.superscript_metrics();
1191
1192 if self.is_variable() {
1193 self.apply_metrics_variation_to(Tag::from_bytes(b"spxs"), &mut metrics.x_size);
1194 self.apply_metrics_variation_to(Tag::from_bytes(b"spys"), &mut metrics.y_size);
1195 self.apply_metrics_variation_to(Tag::from_bytes(b"spxo"), &mut metrics.x_offset);
1196 self.apply_metrics_variation_to(Tag::from_bytes(b"spyo"), &mut metrics.y_offset);
1197 }
1198
1199 Some(metrics)
1200 }
1201
1202 #[inline]
1208 pub fn number_of_glyphs(&self) -> u16 {
1209 self.number_of_glyphs.get()
1210 }
1211
1212 #[inline]
1219 pub fn character_mapping_subtables(&self) -> cmap::Subtables {
1220 self.cmap.unwrap_or_default()
1221 }
1222
1223 #[inline]
1231 pub fn glyph_index(&self, c: char) -> Option<GlyphId> {
1232 for encoding in self.character_mapping_subtables() {
1233 if !encoding.is_unicode() {
1234 continue;
1235 }
1236
1237 if let Some(id) = encoding.glyph_index(u32::from(c)) {
1238 return Some(id);
1239 }
1240 }
1241
1242 None
1243 }
1244
1245 #[inline]
1253 pub fn glyph_variation_index(&self, c: char, variation: char) -> Option<GlyphId> {
1254 let res = self.character_mapping_subtables()
1255 .find(|e| e.format() == cmap::Format::UnicodeVariationSequences)
1256 .and_then(|e| e.glyph_variation_index(c, variation))?;
1257
1258 match res {
1259 cmap::GlyphVariationResult::Found(v) => Some(v),
1260 cmap::GlyphVariationResult::UseDefault => self.glyph_index(c),
1261 }
1262 }
1263
1264 #[inline]
1268 pub fn glyph_hor_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1269 #[cfg(feature = "variable-fonts")] {
1270 let mut advance = self.hmtx?.advance(glyph_id)? as f32;
1271
1272 if self.is_variable() {
1273 if let Some(hvar_data) = self.hvar {
1275 advance += hvar::glyph_advance_offset(hvar_data, glyph_id, self.coords())? + 0.5;
1277 }
1278 }
1279
1280 u16::try_num_from(advance)
1281 }
1282
1283 #[cfg(not(feature = "variable-fonts"))] {
1284 self.hmtx?.advance(glyph_id)
1285 }
1286 }
1287
1288 #[inline]
1292 pub fn glyph_ver_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1293 #[cfg(feature = "variable-fonts")] {
1294 let mut advance = self.vmtx?.advance(glyph_id)? as f32;
1295
1296 if self.is_variable() {
1297 if let Some(vvar_data) = self.vvar {
1299 advance += hvar::glyph_advance_offset(vvar_data, glyph_id, self.coords())? + 0.5;
1301 }
1302 }
1303
1304 u16::try_num_from(advance)
1305 }
1306
1307 #[cfg(not(feature = "variable-fonts"))] {
1308 self.vmtx?.advance(glyph_id)
1309 }
1310 }
1311
1312 #[inline]
1316 pub fn glyph_hor_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1317 #[cfg(feature = "variable-fonts")] {
1318 let mut bearing = self.hmtx?.side_bearing(glyph_id)? as f32;
1319
1320 if self.is_variable() {
1321 if let Some(hvar_data) = self.hvar {
1323 bearing += hvar::glyph_side_bearing_offset(hvar_data, glyph_id, self.coords())? + 0.5;
1325 }
1326 }
1327
1328 i16::try_num_from(bearing)
1329 }
1330
1331 #[cfg(not(feature = "variable-fonts"))] {
1332 self.hmtx?.side_bearing(glyph_id)
1333 }
1334 }
1335
1336 #[inline]
1340 pub fn glyph_ver_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1341 #[cfg(feature = "variable-fonts")] {
1342 let mut bearing = self.vmtx?.side_bearing(glyph_id)? as f32;
1343
1344 if self.is_variable() {
1345 if let Some(vvar_data) = self.vvar {
1347 bearing += hvar::glyph_side_bearing_offset(vvar_data, glyph_id, self.coords())? + 0.5;
1349 }
1350 }
1351
1352 i16::try_num_from(bearing)
1353 }
1354
1355 #[cfg(not(feature = "variable-fonts"))] {
1356 self.vmtx?.side_bearing(glyph_id)
1357 }
1358 }
1359
1360 pub fn glyph_y_origin(&self, glyph_id: GlyphId) -> Option<i16> {
1363 self.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))
1364 }
1365
1366 #[inline]
1372 pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&str> {
1373 if let Some(name) = self.post.and_then(|post| post.glyph_name(glyph_id)) {
1374 return Some(name);
1375 }
1376
1377 if let Some(name) = self.cff1.as_ref().and_then(|cff1| cff1::glyph_name(cff1, glyph_id)) {
1378 return Some(name);
1379 }
1380
1381 None
1382 }
1383
1384 pub fn has_glyph_classes(&self) -> bool {
1388 try_opt_or!(self.gdef, false).has_glyph_classes()
1389 }
1390
1391 pub fn glyph_class(&self, glyph_id: GlyphId) -> Option<GlyphClass> {
1398 self.gdef.and_then(|gdef| gdef.glyph_class(glyph_id))
1399 }
1400
1401 pub fn glyph_mark_attachment_class(&self, glyph_id: GlyphId) -> Class {
1407 try_opt_or!(self.gdef, Class(0)).glyph_mark_attachment_class(glyph_id)
1408 }
1409
1410 #[inline]
1417 pub fn is_mark_glyph(&self, glyph_id: GlyphId, set_index: Option<u16>) -> bool {
1418 try_opt_or!(self.gdef, false).is_mark_glyph(glyph_id, set_index)
1419 }
1420
1421 #[cfg(feature = "variable-fonts")]
1425 #[inline]
1426 pub fn glyph_variation_delta(&self, outer_index: u16, inner_index: u16) -> Option<f32> {
1427 self.gdef.and_then(|gdef|
1428 gdef.variation_delta(outer_index, inner_index, self.coordinates.as_slice()))
1429 }
1430
1431 pub fn kerning_subtables(&self) -> kern::Subtables {
1439 self.kern.unwrap_or_default()
1440 }
1441
1442 #[inline]
1495 pub fn outline_glyph(
1496 &self,
1497 glyph_id: GlyphId,
1498 builder: &mut dyn OutlineBuilder,
1499 ) -> Option<Rect> {
1500 #[cfg(feature = "variable-fonts")] {
1501 if let Some(ref gvar_table) = self.gvar {
1502 return gvar::outline(self.loca?, self.glyf?, gvar_table, self.coords(), glyph_id, builder);
1503 }
1504 }
1505
1506 if let Some(glyf_table) = self.glyf {
1507 return glyf::outline(self.loca?, glyf_table, glyph_id, builder);
1508 }
1509
1510 if let Some(ref metadata) = self.cff1 {
1511 return cff1::outline(metadata, glyph_id, builder);
1512 }
1513
1514 #[cfg(feature = "variable-fonts")] {
1515 if let Some(ref metadata) = self.cff2 {
1516 return cff2::outline(metadata, self.coords(), glyph_id, builder);
1517 }
1518 }
1519
1520 None
1521 }
1522
1523 #[inline]
1535 pub fn glyph_bounding_box(&self, glyph_id: GlyphId) -> Option<Rect> {
1536 #[cfg(feature = "variable-fonts")]
1537 {
1538 if !self.is_variable() {
1539 if let Some(glyf_table) = self.glyf {
1540 return glyf::glyph_bbox(self.loca?, glyf_table, glyph_id);
1541 }
1542 }
1543 }
1544
1545 #[cfg(not(feature = "variable-fonts"))]
1546 {
1547 if let Some(glyf_table) = self.glyf {
1548 return glyf::glyph_bbox(self.loca?, glyf_table, glyph_id);
1549 }
1550 }
1551
1552 self.outline_glyph(glyph_id, &mut DummyOutline)
1553 }
1554
1555 #[inline]
1557 pub fn global_bounding_box(&self) -> Rect {
1558 head::global_bbox(self.head).unwrap()
1560 }
1561
1562 #[inline]
1584 pub fn glyph_raster_image(&self, glyph_id: GlyphId, pixels_per_em: u16) -> Option<RasterGlyphImage> {
1585 if let Some(sbix_data) = self.sbix {
1586 return sbix::parse(sbix_data, self.number_of_glyphs, glyph_id, pixels_per_em, 0);
1587 }
1588
1589 if let (Some(cblc_data), Some(cbdt_data)) = (self.cblc, self.cbdt) {
1590 let location = cblc::find_location(cblc_data, glyph_id, pixels_per_em)?;
1591 return cbdt::parse(cbdt_data, location);
1592 }
1593
1594 None
1595 }
1596
1597 #[inline]
1609 pub fn glyph_svg_image(&self, glyph_id: GlyphId) -> Option<&'a [u8]> {
1610 self.svg_.and_then(|svg_data| svg::parse(svg_data, glyph_id))
1611 }
1612
1613 #[cfg(feature = "variable-fonts")]
1615 #[inline]
1616 pub fn variation_axes(&self) -> VariationAxes {
1617 self.fvar.map(|fvar| fvar.axes()).unwrap_or_default()
1618 }
1619
1620 #[cfg(feature = "variable-fonts")]
1630 pub fn set_variation(&mut self, axis: Tag, value: f32) -> Option<()> {
1631 if !self.is_variable() {
1632 return None;
1633 }
1634
1635 let v = self.variation_axes().enumerate().find(|(_, a)| a.tag == axis);
1636 if let Some((idx, a)) = v {
1637 if idx >= MAX_VAR_COORDS {
1638 return None;
1639 }
1640
1641 self.coordinates.data[idx] = a.normalized_value(value);
1642 } else {
1643 return None;
1644 }
1645
1646 if let Some(avar) = self.avar {
1648 let _ = avar.map_coordinates(self.coordinates.as_mut_slice());
1650 }
1651
1652 Some(())
1653 }
1654
1655 #[cfg(feature = "variable-fonts")]
1657 #[inline]
1658 pub fn variation_coordinates(&self) -> &[NormalizedCoordinate] {
1659 self.coordinates.as_slice()
1660 }
1661
1662 #[cfg(feature = "variable-fonts")]
1664 #[inline]
1665 pub fn has_non_default_variation_coordinates(&self) -> bool {
1666 self.coordinates.as_slice().iter().any(|c| c.0 != 0)
1667 }
1668
1669 #[cfg(feature = "variable-fonts")]
1670 #[inline]
1671 fn metrics_var_offset(&self, tag: Tag) -> f32 {
1672 self.mvar.and_then(|table| table.metrics_offset(tag, self.coords())).unwrap_or(0.0)
1673 }
1674
1675 #[inline]
1676 fn apply_metrics_variation(&self, tag: Tag, mut value: i16) -> i16 {
1677 self.apply_metrics_variation_to(tag, &mut value);
1678 value
1679 }
1680
1681
1682 #[cfg(feature = "variable-fonts")]
1683 #[inline]
1684 fn apply_metrics_variation_to(&self, tag: Tag, value: &mut i16) {
1685 if self.is_variable() {
1686 let v = f32::from(*value) + self.metrics_var_offset(tag);
1687 if let Some(v) = i16::try_num_from(v) {
1689 *value = v;
1690 }
1691 }
1692 }
1693
1694 #[cfg(not(feature = "variable-fonts"))]
1695 #[inline]
1696 fn apply_metrics_variation_to(&self, _: Tag, _: &mut i16) {
1697 }
1698
1699 #[cfg(feature = "variable-fonts")]
1700 #[inline]
1701 fn coords(&self) -> &[NormalizedCoordinate] {
1702 self.coordinates.as_slice()
1703 }
1704}
1705
1706impl fmt::Debug for Face<'_> {
1707 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1708 write!(f, "Face()")
1709 }
1710}
1711
1712#[inline]
1716pub fn fonts_in_collection(data: &[u8]) -> Option<u32> {
1717 let mut s = Stream::new(data);
1718 if s.read::<Magic>()? != Magic::FontCollection {
1719 return None;
1720 }
1721
1722 s.skip::<u32>(); s.read::<u32>()
1724}
1725
1726
1727#[cfg(test)]
1728mod tests {
1729 use super::*;
1730
1731 #[test]
1732 fn empty_font() {
1733 assert_eq!(Face::from_slice(&[], 0).unwrap_err(),
1734 FaceParsingError::UnknownMagic);
1735 }
1736
1737 #[test]
1738 fn zero_tables() {
1739 let data = &[
1740 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1746
1747 assert_eq!(Face::from_slice(data, 0).unwrap_err(),
1748 FaceParsingError::NoHeadTable);
1749 }
1750
1751 #[test]
1752 fn tables_count_overflow() {
1753 let data = &[
1754 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1760
1761 assert_eq!(Face::from_slice(data, 0).unwrap_err(),
1762 FaceParsingError::MalformedFont);
1763 }
1764
1765 #[test]
1766 fn empty_font_collection() {
1767 let data = &[
1768 0x74, 0x74, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1773
1774 assert_eq!(fonts_in_collection(data), Some(0));
1775 assert_eq!(Face::from_slice(data, 0).unwrap_err(),
1776 FaceParsingError::FaceIndexOutOfBounds);
1777 }
1778
1779 #[test]
1780 fn font_collection_num_fonts_overflow() {
1781 let data = &[
1782 0x74, 0x74, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, ];
1787
1788 assert_eq!(fonts_in_collection(data), Some(std::u32::MAX));
1789 assert_eq!(Face::from_slice(data, 0).unwrap_err(),
1790 FaceParsingError::MalformedFont);
1791 }
1792
1793 #[test]
1794 fn font_index_overflow() {
1795 let data = &[
1796 0x74, 0x74, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, ];
1802
1803 assert_eq!(fonts_in_collection(data), Some(1));
1804 assert_eq!(Face::from_slice(data, std::u32::MAX).unwrap_err(),
1805 FaceParsingError::FaceIndexOutOfBounds);
1806 }
1807}