1/*!
2A high-level, safe, zero-allocation TrueType font parser.
3
4Supports [TrueType](https://docs.microsoft.com/en-us/typography/truetype/),
5[OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/)
6and [AAT](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)
7fonts.
8
9Font parsing starts with a [`Face`].
10
11## Features
12
13- A high-level API for most common properties, hiding all parsing and data resolving logic.
14- A low-level, but safe API to access TrueType tables data.
15- Highly configurable. You can disable most of the features, reducing binary size.
16 You can also parse TrueType tables separately, without loading the whole font/face.
17- Zero heap allocations.
18- Zero unsafe.
19- Zero dependencies.
20- `no_std`/WASM compatible.
21- Fast.
22- Stateless. All parsing methods are immutable.
23- Simple and maintainable code (no magic numbers).
24
25## Safety
26
27- The library must not panic. Any panic considered as a critical bug and should be reported.
28- The library forbids unsafe code.
29- No heap allocations, so crash due to OOM is not possible.
30- All recursive methods have a depth limit.
31- Technically, should use less than 64KiB of stack in worst case scenario.
32- Most of arithmetic operations are checked.
33- Most of numeric casts are checked.
34*/
35
36#![no_std]
37#![forbid(unsafe_code)]
38#![warn(missing_docs)]
39#![warn(missing_copy_implementations)]
40#![warn(missing_debug_implementations)]
41#![allow(clippy::get_first)] // we use it for readability
42#![allow(clippy::identity_op)] // we use it for readability
43#![allow(clippy::too_many_arguments)]
44#![allow(clippy::collapsible_else_if)]
45#![allow(clippy::field_reassign_with_default)]
46#![allow(clippy::upper_case_acronyms)]
47
48#[cfg(feature = "std")]
49#[macro_use]
50extern crate std;
51
52macro_rules! try_opt_or {
53 ($value:expr, $ret:expr) => {
54 match $value {
55 Some(v) => v,
56 None => return $ret,
57 }
58 };
59}
60
61#[cfg(feature = "apple-layout")]
62mod aat;
63#[cfg(feature = "opentype-layout")]
64mod ggg;
65mod parser;
66mod tables;
67#[cfg(feature = "variable-fonts")]
68mod var_store;
69
70use head::IndexToLocationFormat;
71pub use parser::{Fixed, FromData, LazyArray16, LazyArray32, LazyArrayIter16, LazyArrayIter32};
72use parser::{NumFrom, Offset, Offset32, Stream, TryNumFrom};
73
74#[cfg(feature = "variable-fonts")]
75pub use fvar::VariationAxis;
76
77pub use name::{name_id, PlatformId};
78pub use os2::{Permissions, ScriptMetrics, Style, UnicodeRanges, Weight, Width};
79pub use tables::CFFError;
80#[cfg(feature = "apple-layout")]
81pub use tables::{ankr, feat, kerx, morx, trak};
82#[cfg(feature = "variable-fonts")]
83pub use tables::{avar, cff2, fvar, gvar, hvar, mvar};
84pub use tables::{cbdt, cblc, cff1 as cff, vhea};
85pub use tables::{
86 cmap, glyf, head, hhea, hmtx, kern, loca, maxp, name, os2, post, sbix, svg, vorg,
87};
88#[cfg(feature = "opentype-layout")]
89pub use tables::{gdef, gpos, gsub, math};
90
91#[cfg(feature = "opentype-layout")]
92pub mod opentype_layout {
93 //! This module contains
94 //! [OpenType Layout](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#overview)
95 //! supplementary tables implementation.
96 pub use crate::ggg::*;
97}
98
99#[cfg(feature = "apple-layout")]
100pub mod apple_layout {
101 //! This module contains
102 //! [Apple Advanced Typography Layout](
103 //! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html)
104 //! supplementary tables implementation.
105 pub use crate::aat::*;
106}
107
108/// A type-safe wrapper for glyph ID.
109#[repr(transparent)]
110#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Debug, Hash)]
111pub struct GlyphId(pub u16);
112
113impl FromData for GlyphId {
114 const SIZE: usize = 2;
115
116 #[inline]
117 fn parse(data: &[u8]) -> Option<Self> {
118 u16::parse(data).map(GlyphId)
119 }
120}
121
122/// A TrueType font magic.
123///
124/// https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
125#[derive(Clone, Copy, PartialEq, Debug)]
126enum Magic {
127 TrueType,
128 OpenType,
129 FontCollection,
130}
131
132impl FromData for Magic {
133 const SIZE: usize = 4;
134
135 #[inline]
136 fn parse(data: &[u8]) -> Option<Self> {
137 match u32::parse(data)? {
138 0x00010000 | 0x74727565 => Some(Magic::TrueType),
139 0x4F54544F => Some(Magic::OpenType),
140 0x74746366 => Some(Magic::FontCollection),
141 _ => None,
142 }
143 }
144}
145
146/// A variation coordinate in a normalized coordinate system.
147///
148/// Basically any number in a -1.0..1.0 range.
149/// Where 0 is a default value.
150///
151/// The number is stored as f2.16
152#[repr(transparent)]
153#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
154pub struct NormalizedCoordinate(i16);
155
156impl From<i16> for NormalizedCoordinate {
157 /// Creates a new coordinate.
158 ///
159 /// The provided number will be clamped to the -16384..16384 range.
160 #[inline]
161 fn from(n: i16) -> Self {
162 NormalizedCoordinate(parser::i16_bound(min:-16384, val:n, max:16384))
163 }
164}
165
166impl From<f32> for NormalizedCoordinate {
167 /// Creates a new coordinate.
168 ///
169 /// The provided number will be clamped to the -1.0..1.0 range.
170 #[inline]
171 fn from(n: f32) -> Self {
172 NormalizedCoordinate((parser::f32_bound(min:-1.0, val:n, max:1.0) * 16384.0) as i16)
173 }
174}
175
176impl NormalizedCoordinate {
177 /// Returns the coordinate value as f2.14.
178 #[inline]
179 pub fn get(self) -> i16 {
180 self.0
181 }
182}
183
184/// A font variation value.
185///
186/// # Example
187///
188/// ```
189/// use ttf_parser::{Variation, Tag};
190///
191/// Variation { axis: Tag::from_bytes(b"wght"), value: 500.0 };
192/// ```
193#[derive(Clone, Copy, PartialEq, Debug)]
194pub struct Variation {
195 /// An axis tag name.
196 pub axis: Tag,
197 /// An axis value.
198 pub value: f32,
199}
200
201/// A 4-byte tag.
202#[repr(transparent)]
203#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
204pub struct Tag(pub u32);
205
206impl Tag {
207 /// Creates a `Tag` from bytes.
208 ///
209 /// # Example
210 ///
211 /// ```rust
212 /// println!("{}", ttf_parser::Tag::from_bytes(b"name"));
213 /// ```
214 #[inline]
215 pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
216 Tag(((bytes[0] as u32) << 24)
217 | ((bytes[1] as u32) << 16)
218 | ((bytes[2] as u32) << 8)
219 | (bytes[3] as u32))
220 }
221
222 /// Creates a `Tag` from bytes.
223 ///
224 /// In case of empty data will return `Tag` set to 0.
225 ///
226 /// When `bytes` are shorter than 4, will set missing bytes to ` `.
227 ///
228 /// Data after first 4 bytes is ignored.
229 #[inline]
230 pub fn from_bytes_lossy(bytes: &[u8]) -> Self {
231 if bytes.is_empty() {
232 return Tag::from_bytes(&[0, 0, 0, 0]);
233 }
234
235 let mut iter = bytes.iter().cloned().chain(core::iter::repeat(b' '));
236 Tag::from_bytes(&[
237 iter.next().unwrap(),
238 iter.next().unwrap(),
239 iter.next().unwrap(),
240 iter.next().unwrap(),
241 ])
242 }
243
244 /// Returns tag as 4-element byte array.
245 #[inline]
246 pub const fn to_bytes(self) -> [u8; 4] {
247 [
248 (self.0 >> 24 & 0xff) as u8,
249 (self.0 >> 16 & 0xff) as u8,
250 (self.0 >> 8 & 0xff) as u8,
251 (self.0 >> 0 & 0xff) as u8,
252 ]
253 }
254
255 /// Returns tag as 4-element byte array.
256 #[inline]
257 pub const fn to_chars(self) -> [char; 4] {
258 [
259 (self.0 >> 24 & 0xff) as u8 as char,
260 (self.0 >> 16 & 0xff) as u8 as char,
261 (self.0 >> 8 & 0xff) as u8 as char,
262 (self.0 >> 0 & 0xff) as u8 as char,
263 ]
264 }
265
266 /// Checks if tag is null / `[0, 0, 0, 0]`.
267 #[inline]
268 pub const fn is_null(&self) -> bool {
269 self.0 == 0
270 }
271
272 /// Returns tag value as `u32` number.
273 #[inline]
274 pub const fn as_u32(&self) -> u32 {
275 self.0
276 }
277}
278
279impl core::fmt::Debug for Tag {
280 #[inline]
281 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
282 write!(f, "Tag({})", self)
283 }
284}
285
286impl core::fmt::Display for Tag {
287 #[inline]
288 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
289 let b: [char; 4] = self.to_chars();
290 write!(
291 f,
292 "{}{}{}{}",
293 b.get(0).unwrap_or(&' '),
294 b.get(1).unwrap_or(&' '),
295 b.get(2).unwrap_or(&' '),
296 b.get(3).unwrap_or(&' ')
297 )
298 }
299}
300
301impl FromData for Tag {
302 const SIZE: usize = 4;
303
304 #[inline]
305 fn parse(data: &[u8]) -> Option<Self> {
306 u32::parse(data).map(Tag)
307 }
308}
309
310/// A line metrics.
311///
312/// Used for underline and strikeout.
313#[repr(C)]
314#[derive(Clone, Copy, PartialEq, Eq, Debug)]
315pub struct LineMetrics {
316 /// Line position.
317 pub position: i16,
318
319 /// Line thickness.
320 pub thickness: i16,
321}
322
323/// A rectangle.
324///
325/// Doesn't guarantee that `x_min` <= `x_max` and/or `y_min` <= `y_max`.
326#[repr(C)]
327#[allow(missing_docs)]
328#[derive(Clone, Copy, PartialEq, Eq, Debug)]
329pub struct Rect {
330 pub x_min: i16,
331 pub y_min: i16,
332 pub x_max: i16,
333 pub y_max: i16,
334}
335
336impl Rect {
337 /// Returns rect's width.
338 #[inline]
339 pub fn width(&self) -> i16 {
340 self.x_max - self.x_min
341 }
342
343 /// Returns rect's height.
344 #[inline]
345 pub fn height(&self) -> i16 {
346 self.y_max - self.y_min
347 }
348}
349
350#[derive(Clone, Copy, Debug)]
351pub(crate) struct BBox {
352 x_min: f32,
353 y_min: f32,
354 x_max: f32,
355 y_max: f32,
356}
357
358impl BBox {
359 #[inline]
360 fn new() -> Self {
361 BBox {
362 x_min: core::f32::MAX,
363 y_min: core::f32::MAX,
364 x_max: core::f32::MIN,
365 y_max: core::f32::MIN,
366 }
367 }
368
369 #[inline]
370 fn is_default(&self) -> bool {
371 self.x_min == core::f32::MAX
372 && self.y_min == core::f32::MAX
373 && self.x_max == core::f32::MIN
374 && self.y_max == core::f32::MIN
375 }
376
377 #[inline]
378 fn extend_by(&mut self, x: f32, y: f32) {
379 self.x_min = self.x_min.min(x);
380 self.y_min = self.y_min.min(y);
381 self.x_max = self.x_max.max(x);
382 self.y_max = self.y_max.max(y);
383 }
384
385 #[inline]
386 fn to_rect(self) -> Option<Rect> {
387 Some(Rect {
388 x_min: i16::try_num_from(self.x_min)?,
389 y_min: i16::try_num_from(self.y_min)?,
390 x_max: i16::try_num_from(self.x_max)?,
391 y_max: i16::try_num_from(self.y_max)?,
392 })
393 }
394}
395
396/// A trait for glyph outline construction.
397pub trait OutlineBuilder {
398 /// Appends a MoveTo segment.
399 ///
400 /// Start of a contour.
401 fn move_to(&mut self, x: f32, y: f32);
402
403 /// Appends a LineTo segment.
404 fn line_to(&mut self, x: f32, y: f32);
405
406 /// Appends a QuadTo segment.
407 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32);
408
409 /// Appends a CurveTo segment.
410 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32);
411
412 /// Appends a ClosePath segment.
413 ///
414 /// End of a contour.
415 fn close(&mut self);
416}
417
418struct DummyOutline;
419impl OutlineBuilder for DummyOutline {
420 fn move_to(&mut self, _: f32, _: f32) {}
421 fn line_to(&mut self, _: f32, _: f32) {}
422 fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {}
423 fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {}
424 fn close(&mut self) {}
425}
426
427/// A glyph raster image format.
428#[allow(missing_docs)]
429#[derive(Clone, Copy, PartialEq, Eq, Debug)]
430pub enum RasterImageFormat {
431 PNG,
432}
433
434/// A glyph's raster image.
435///
436/// Note, that glyph metrics are in pixels and not in font units.
437#[derive(Clone, Copy, PartialEq, Eq, Debug)]
438pub struct RasterGlyphImage<'a> {
439 /// Horizontal offset.
440 pub x: i16,
441
442 /// Vertical offset.
443 pub y: i16,
444
445 /// Image width.
446 ///
447 /// It doesn't guarantee that this value is the same as set in the `data`.
448 pub width: u16,
449
450 /// Image height.
451 ///
452 /// It doesn't guarantee that this value is the same as set in the `data`.
453 pub height: u16,
454
455 /// A pixels per em of the selected strike.
456 pub pixels_per_em: u16,
457
458 /// An image format.
459 pub format: RasterImageFormat,
460
461 /// A raw image data. It's up to the caller to decode it.
462 pub data: &'a [u8],
463}
464
465/// A raw table record.
466#[derive(Clone, Copy, Debug)]
467#[allow(missing_docs)]
468pub struct TableRecord {
469 pub tag: Tag,
470 #[allow(dead_code)]
471 pub check_sum: u32,
472 pub offset: u32,
473 pub length: u32,
474}
475
476impl FromData for TableRecord {
477 const SIZE: usize = 16;
478
479 #[inline]
480 fn parse(data: &[u8]) -> Option<Self> {
481 let mut s: Stream<'_> = Stream::new(data);
482 Some(TableRecord {
483 tag: s.read::<Tag>()?,
484 check_sum: s.read::<u32>()?,
485 offset: s.read::<u32>()?,
486 length: s.read::<u32>()?,
487 })
488 }
489}
490
491#[cfg(feature = "variable-fonts")]
492const MAX_VAR_COORDS: usize = 32;
493
494#[cfg(feature = "variable-fonts")]
495#[derive(Clone, Default)]
496struct VarCoords {
497 data: [NormalizedCoordinate; MAX_VAR_COORDS],
498 len: u8,
499}
500
501#[cfg(feature = "variable-fonts")]
502impl VarCoords {
503 #[inline]
504 fn as_slice(&self) -> &[NormalizedCoordinate] {
505 &self.data[0..usize::from(self.len)]
506 }
507
508 #[inline]
509 fn as_mut_slice(&mut self) -> &mut [NormalizedCoordinate] {
510 let end: usize = usize::from(self.len);
511 &mut self.data[0..end]
512 }
513}
514
515/// A list of font face parsing errors.
516#[derive(Clone, Copy, PartialEq, Eq, Debug)]
517pub enum FaceParsingError {
518 /// An attempt to read out of bounds detected.
519 ///
520 /// Should occur only on malformed fonts.
521 MalformedFont,
522
523 /// Face data must start with `0x00010000`, `0x74727565`, `0x4F54544F` or `0x74746366`.
524 UnknownMagic,
525
526 /// The face index is larger than the number of faces in the font.
527 FaceIndexOutOfBounds,
528
529 /// The `head` table is missing or malformed.
530 NoHeadTable,
531
532 /// The `hhea` table is missing or malformed.
533 NoHheaTable,
534
535 /// The `maxp` table is missing or malformed.
536 NoMaxpTable,
537}
538
539impl core::fmt::Display for FaceParsingError {
540 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
541 match self {
542 FaceParsingError::MalformedFont => write!(f, "malformed font"),
543 FaceParsingError::UnknownMagic => write!(f, "unknown magic"),
544 FaceParsingError::FaceIndexOutOfBounds => write!(f, "face index is out of bounds"),
545 FaceParsingError::NoHeadTable => write!(f, "the head table is missing or malformed"),
546 FaceParsingError::NoHheaTable => write!(f, "the hhea table is missing or malformed"),
547 FaceParsingError::NoMaxpTable => write!(f, "the maxp table is missing or malformed"),
548 }
549 }
550}
551
552#[cfg(feature = "std")]
553impl std::error::Error for FaceParsingError {}
554
555/// A raw font face.
556///
557/// You are probably looking for [`Face`]. This is a low-level type.
558///
559/// Unlike [`Face`], [`RawFace`] parses only face table records.
560/// Meaning all you can get from this type is a raw (`&[u8]`) data of a requested table.
561/// Then you can either parse just a singe table from a font/face or populate [`RawFaceTables`]
562/// manually before passing it to [`Face::from_raw_tables`].
563#[derive(Clone, Copy)]
564pub struct RawFace<'a> {
565 /// The input font file data.
566 pub data: &'a [u8],
567 /// An array of table records.
568 pub table_records: LazyArray16<'a, TableRecord>,
569}
570
571impl<'a> RawFace<'a> {
572 /// Creates a new [`RawFace`] from a raw data.
573 ///
574 /// `index` indicates the specific font face in a font collection.
575 /// Use [`fonts_in_collection`] to get the total number of font faces.
576 /// Set to 0 if unsure.
577 ///
578 /// While we do reuse [`FaceParsingError`], `No*Table` errors will not be throws.
579 #[deprecated(since = "0.16.0", note = "use `parse` instead")]
580 pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
581 Self::parse(data, index)
582 }
583
584 /// Creates a new [`RawFace`] from a raw data.
585 ///
586 /// `index` indicates the specific font face in a font collection.
587 /// Use [`fonts_in_collection`] to get the total number of font faces.
588 /// Set to 0 if unsure.
589 ///
590 /// While we do reuse [`FaceParsingError`], `No*Table` errors will not be throws.
591 pub fn parse(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
592 // https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
593
594 let mut s = Stream::new(data);
595
596 // Read **font** magic.
597 let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
598 if magic == Magic::FontCollection {
599 s.skip::<u32>(); // version
600 let number_of_faces = s.read::<u32>().ok_or(FaceParsingError::MalformedFont)?;
601 let offsets = s
602 .read_array32::<Offset32>(number_of_faces)
603 .ok_or(FaceParsingError::MalformedFont)?;
604
605 let face_offset = offsets
606 .get(index)
607 .ok_or(FaceParsingError::FaceIndexOutOfBounds)?;
608 // Face offset is from the start of the font data,
609 // so we have to adjust it to the current parser offset.
610 let face_offset = face_offset
611 .to_usize()
612 .checked_sub(s.offset())
613 .ok_or(FaceParsingError::MalformedFont)?;
614 s.advance_checked(face_offset)
615 .ok_or(FaceParsingError::MalformedFont)?;
616
617 // Read **face** magic.
618 // Each face in a font collection also starts with a magic.
619 let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
620 // And face in a font collection can't be another collection.
621 if magic == Magic::FontCollection {
622 return Err(FaceParsingError::UnknownMagic);
623 }
624 } else {
625 // When reading from a regular font (not a collection) disallow index to be non-zero
626 // Basically treat the font as a one-element collection
627 if index != 0 {
628 return Err(FaceParsingError::FaceIndexOutOfBounds);
629 }
630 }
631
632 let num_tables = s.read::<u16>().ok_or(FaceParsingError::MalformedFont)?;
633 s.advance(6); // searchRange (u16) + entrySelector (u16) + rangeShift (u16)
634 let table_records = s
635 .read_array16::<TableRecord>(num_tables)
636 .ok_or(FaceParsingError::MalformedFont)?;
637
638 Ok(RawFace {
639 data,
640 table_records,
641 })
642 }
643
644 /// Returns the raw data of a selected table.
645 pub fn table(&self, tag: Tag) -> Option<&'a [u8]> {
646 let (_, table) = self
647 .table_records
648 .binary_search_by(|record| record.tag.cmp(&tag))?;
649 let offset = usize::num_from(table.offset);
650 let length = usize::num_from(table.length);
651 let end = offset.checked_add(length)?;
652 self.data.get(offset..end)
653 }
654}
655
656impl core::fmt::Debug for RawFace<'_> {
657 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
658 write!(f, "RawFace {{ ... }}")
659 }
660}
661
662/// A list of all supported tables as raw data.
663///
664/// This type should be used in tandem with
665/// [`Face::from_raw_tables()`](struct.Face.html#method.from_raw_tables).
666///
667/// This allows loading font faces not only from TrueType font files,
668/// but from any source. Mainly used for parsing WOFF.
669#[allow(missing_docs)]
670#[allow(missing_debug_implementations)]
671#[derive(Clone, Default)]
672pub struct RawFaceTables<'a> {
673 // Mandatory tables.
674 pub head: &'a [u8],
675 pub hhea: &'a [u8],
676 pub maxp: &'a [u8],
677
678 pub cbdt: Option<&'a [u8]>,
679 pub cblc: Option<&'a [u8]>,
680 pub cff: Option<&'a [u8]>,
681 pub cmap: Option<&'a [u8]>,
682 pub glyf: Option<&'a [u8]>,
683 pub hmtx: Option<&'a [u8]>,
684 pub kern: Option<&'a [u8]>,
685 pub loca: Option<&'a [u8]>,
686 pub name: Option<&'a [u8]>,
687 pub os2: Option<&'a [u8]>,
688 pub post: Option<&'a [u8]>,
689 pub sbix: Option<&'a [u8]>,
690 pub svg: Option<&'a [u8]>,
691 pub vhea: Option<&'a [u8]>,
692 pub vmtx: Option<&'a [u8]>,
693 pub vorg: Option<&'a [u8]>,
694
695 #[cfg(feature = "opentype-layout")]
696 pub gdef: Option<&'a [u8]>,
697 #[cfg(feature = "opentype-layout")]
698 pub gpos: Option<&'a [u8]>,
699 #[cfg(feature = "opentype-layout")]
700 pub gsub: Option<&'a [u8]>,
701 #[cfg(feature = "opentype-layout")]
702 pub math: Option<&'a [u8]>,
703
704 #[cfg(feature = "apple-layout")]
705 pub ankr: Option<&'a [u8]>,
706 #[cfg(feature = "apple-layout")]
707 pub feat: Option<&'a [u8]>,
708 #[cfg(feature = "apple-layout")]
709 pub kerx: Option<&'a [u8]>,
710 #[cfg(feature = "apple-layout")]
711 pub morx: Option<&'a [u8]>,
712 #[cfg(feature = "apple-layout")]
713 pub trak: Option<&'a [u8]>,
714
715 #[cfg(feature = "variable-fonts")]
716 pub avar: Option<&'a [u8]>,
717 #[cfg(feature = "variable-fonts")]
718 pub cff2: Option<&'a [u8]>,
719 #[cfg(feature = "variable-fonts")]
720 pub fvar: Option<&'a [u8]>,
721 #[cfg(feature = "variable-fonts")]
722 pub gvar: Option<&'a [u8]>,
723 #[cfg(feature = "variable-fonts")]
724 pub hvar: Option<&'a [u8]>,
725 #[cfg(feature = "variable-fonts")]
726 pub mvar: Option<&'a [u8]>,
727 #[cfg(feature = "variable-fonts")]
728 pub vvar: Option<&'a [u8]>,
729}
730
731/// Parsed face tables.
732///
733/// Unlike [`Face`], provides a low-level parsing abstraction over TrueType tables.
734/// Useful when you need a direct access to tables data.
735///
736/// Also, used when high-level API is problematic to implement.
737/// A good example would be OpenType layout tables (GPOS/GSUB).
738#[allow(missing_docs)]
739#[allow(missing_debug_implementations)]
740#[derive(Clone)]
741pub struct FaceTables<'a> {
742 // Mandatory tables.
743 pub head: head::Table,
744 pub hhea: hhea::Table,
745 pub maxp: maxp::Table,
746
747 pub cbdt: Option<cbdt::Table<'a>>,
748 pub cff: Option<cff::Table<'a>>,
749 pub cmap: Option<cmap::Table<'a>>,
750 pub glyf: Option<glyf::Table<'a>>,
751 pub hmtx: Option<hmtx::Table<'a>>,
752 pub kern: Option<kern::Table<'a>>,
753 pub name: Option<name::Table<'a>>,
754 pub os2: Option<os2::Table<'a>>,
755 pub post: Option<post::Table<'a>>,
756 pub sbix: Option<sbix::Table<'a>>,
757 pub svg: Option<svg::Table<'a>>,
758 pub vhea: Option<vhea::Table>,
759 pub vmtx: Option<hmtx::Table<'a>>,
760 pub vorg: Option<vorg::Table<'a>>,
761
762 #[cfg(feature = "opentype-layout")]
763 pub gdef: Option<gdef::Table<'a>>,
764 #[cfg(feature = "opentype-layout")]
765 pub gpos: Option<opentype_layout::LayoutTable<'a>>,
766 #[cfg(feature = "opentype-layout")]
767 pub gsub: Option<opentype_layout::LayoutTable<'a>>,
768 #[cfg(feature = "opentype-layout")]
769 pub math: Option<math::Table<'a>>,
770
771 #[cfg(feature = "apple-layout")]
772 pub ankr: Option<ankr::Table<'a>>,
773 #[cfg(feature = "apple-layout")]
774 pub feat: Option<feat::Table<'a>>,
775 #[cfg(feature = "apple-layout")]
776 pub kerx: Option<kerx::Table<'a>>,
777 #[cfg(feature = "apple-layout")]
778 pub morx: Option<morx::Table<'a>>,
779 #[cfg(feature = "apple-layout")]
780 pub trak: Option<trak::Table<'a>>,
781
782 #[cfg(feature = "variable-fonts")]
783 pub avar: Option<avar::Table<'a>>,
784 #[cfg(feature = "variable-fonts")]
785 pub cff2: Option<cff2::Table<'a>>,
786 #[cfg(feature = "variable-fonts")]
787 pub fvar: Option<fvar::Table<'a>>,
788 #[cfg(feature = "variable-fonts")]
789 pub gvar: Option<gvar::Table<'a>>,
790 #[cfg(feature = "variable-fonts")]
791 pub hvar: Option<hvar::Table<'a>>,
792 #[cfg(feature = "variable-fonts")]
793 pub mvar: Option<mvar::Table<'a>>,
794 #[cfg(feature = "variable-fonts")]
795 pub vvar: Option<hvar::Table<'a>>,
796}
797
798/// A font face.
799///
800/// Provides a high-level API for working with TrueType fonts.
801/// If you're not familiar with how TrueType works internally, you should use this type.
802/// If you do know and want a bit more low-level access - checkout [`FaceTables`].
803///
804/// Note that `Face` doesn't own the font data and doesn't allocate anything in heap.
805/// Therefore you cannot "store" it. The idea is that you should parse the `Face`
806/// when needed, get required data and forget about it.
807/// That's why the initial parsing is highly optimized and should not become a bottleneck.
808///
809/// If you still want to store `Face` - checkout
810/// [owned_ttf_parser](https://crates.io/crates/owned_ttf_parser). Requires `unsafe`.
811///
812/// While `Face` is technically copyable, we disallow it because it's almost 2KB big.
813#[derive(Clone)]
814pub struct Face<'a> {
815 raw_face: RawFace<'a>,
816 tables: FaceTables<'a>, // Parsed tables.
817 #[cfg(feature = "variable-fonts")]
818 coordinates: VarCoords,
819}
820
821impl<'a> Face<'a> {
822 /// Creates a new [`Face`] from a raw data.
823 ///
824 /// `index` indicates the specific font face in a font collection.
825 /// Use [`fonts_in_collection`] to get the total number of font faces.
826 /// Set to 0 if unsure.
827 ///
828 /// This method will do some parsing and sanitization,
829 /// but in general can be considered free. No significant performance overhead.
830 ///
831 /// Required tables: `head`, `hhea` and `maxp`.
832 ///
833 /// If an optional table has invalid data it will be skipped.
834 #[deprecated(since = "0.16.0", note = "use `parse` instead")]
835 pub fn from_slice(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
836 Self::parse(data, index)
837 }
838
839 /// Creates a new [`Face`] from a raw data.
840 ///
841 /// `index` indicates the specific font face in a font collection.
842 /// Use [`fonts_in_collection`] to get the total number of font faces.
843 /// Set to 0 if unsure.
844 ///
845 /// This method will do some parsing and sanitization,
846 /// but in general can be considered free. No significant performance overhead.
847 ///
848 /// Required tables: `head`, `hhea` and `maxp`.
849 ///
850 /// If an optional table has invalid data it will be skipped.
851 pub fn parse(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
852 let raw_face = RawFace::parse(data, index)?;
853 let raw_tables = Self::collect_tables(raw_face);
854
855 #[allow(unused_mut)]
856 let mut face = Face {
857 raw_face,
858 #[cfg(feature = "variable-fonts")]
859 coordinates: VarCoords::default(),
860 tables: Self::parse_tables(raw_tables)?,
861 };
862
863 #[cfg(feature = "variable-fonts")]
864 {
865 if let Some(ref fvar) = face.tables.fvar {
866 face.coordinates.len = fvar.axes.len().min(MAX_VAR_COORDS as u16) as u8;
867 }
868 }
869
870 Ok(face)
871 }
872
873 fn collect_tables(raw_face: RawFace<'a>) -> RawFaceTables<'a> {
874 let mut tables = RawFaceTables::default();
875
876 for record in raw_face.table_records {
877 let start = usize::num_from(record.offset);
878 let end = match start.checked_add(usize::num_from(record.length)) {
879 Some(v) => v,
880 None => continue,
881 };
882
883 let table_data = raw_face.data.get(start..end);
884 match &record.tag.to_bytes() {
885 b"CBDT" => tables.cbdt = table_data,
886 b"CBLC" => tables.cblc = table_data,
887 b"CFF " => tables.cff = table_data,
888 #[cfg(feature = "variable-fonts")]
889 b"CFF2" => tables.cff2 = table_data,
890 #[cfg(feature = "opentype-layout")]
891 b"GDEF" => tables.gdef = table_data,
892 #[cfg(feature = "opentype-layout")]
893 b"GPOS" => tables.gpos = table_data,
894 #[cfg(feature = "opentype-layout")]
895 b"GSUB" => tables.gsub = table_data,
896 #[cfg(feature = "opentype-layout")]
897 b"MATH" => tables.math = table_data,
898 #[cfg(feature = "variable-fonts")]
899 b"HVAR" => tables.hvar = table_data,
900 #[cfg(feature = "variable-fonts")]
901 b"MVAR" => tables.mvar = table_data,
902 b"OS/2" => tables.os2 = table_data,
903 b"SVG " => tables.svg = table_data,
904 b"VORG" => tables.vorg = table_data,
905 #[cfg(feature = "variable-fonts")]
906 b"VVAR" => tables.vvar = table_data,
907 #[cfg(feature = "apple-layout")]
908 b"ankr" => tables.ankr = table_data,
909 #[cfg(feature = "variable-fonts")]
910 b"avar" => tables.avar = table_data,
911 b"cmap" => tables.cmap = table_data,
912 #[cfg(feature = "apple-layout")]
913 b"feat" => tables.feat = table_data,
914 #[cfg(feature = "variable-fonts")]
915 b"fvar" => tables.fvar = table_data,
916 b"glyf" => tables.glyf = table_data,
917 #[cfg(feature = "variable-fonts")]
918 b"gvar" => tables.gvar = table_data,
919 b"head" => tables.head = table_data.unwrap_or_default(),
920 b"hhea" => tables.hhea = table_data.unwrap_or_default(),
921 b"hmtx" => tables.hmtx = table_data,
922 b"kern" => tables.kern = table_data,
923 #[cfg(feature = "apple-layout")]
924 b"kerx" => tables.kerx = table_data,
925 b"loca" => tables.loca = table_data,
926 b"maxp" => tables.maxp = table_data.unwrap_or_default(),
927 #[cfg(feature = "apple-layout")]
928 b"morx" => tables.morx = table_data,
929 b"name" => tables.name = table_data,
930 b"post" => tables.post = table_data,
931 b"sbix" => tables.sbix = table_data,
932 #[cfg(feature = "apple-layout")]
933 b"trak" => tables.trak = table_data,
934 b"vhea" => tables.vhea = table_data,
935 b"vmtx" => tables.vmtx = table_data,
936 _ => {}
937 }
938 }
939
940 tables
941 }
942
943 /// Creates a new [`Face`] from provided [`RawFaceTables`].
944 pub fn from_raw_tables(raw_tables: RawFaceTables<'a>) -> Result<Self, FaceParsingError> {
945 #[allow(unused_mut)]
946 let mut face = Face {
947 raw_face: RawFace {
948 data: &[],
949 table_records: LazyArray16::default(),
950 },
951 #[cfg(feature = "variable-fonts")]
952 coordinates: VarCoords::default(),
953 tables: Self::parse_tables(raw_tables)?,
954 };
955
956 #[cfg(feature = "variable-fonts")]
957 {
958 if let Some(ref fvar) = face.tables.fvar {
959 face.coordinates.len = fvar.axes.len().min(MAX_VAR_COORDS as u16) as u8;
960 }
961 }
962
963 Ok(face)
964 }
965
966 fn parse_tables(raw_tables: RawFaceTables<'a>) -> Result<FaceTables<'a>, FaceParsingError> {
967 let head = head::Table::parse(raw_tables.head).ok_or(FaceParsingError::NoHeadTable)?;
968 let hhea = hhea::Table::parse(raw_tables.hhea).ok_or(FaceParsingError::NoHheaTable)?;
969 let maxp = maxp::Table::parse(raw_tables.maxp).ok_or(FaceParsingError::NoMaxpTable)?;
970
971 let hmtx = raw_tables.hmtx.and_then(|data| {
972 hmtx::Table::parse(hhea.number_of_metrics, maxp.number_of_glyphs, data)
973 });
974
975 let vhea = raw_tables.vhea.and_then(vhea::Table::parse);
976 let vmtx = if let Some(vhea) = vhea {
977 raw_tables.vmtx.and_then(|data| {
978 hmtx::Table::parse(vhea.number_of_metrics, maxp.number_of_glyphs, data)
979 })
980 } else {
981 None
982 };
983
984 let loca = raw_tables.loca.and_then(|data| {
985 loca::Table::parse(maxp.number_of_glyphs, head.index_to_location_format, data)
986 });
987 let glyf = if let Some(loca) = loca {
988 raw_tables
989 .glyf
990 .and_then(|data| glyf::Table::parse(loca, data))
991 } else {
992 None
993 };
994
995 let cbdt = if let Some(cblc) = raw_tables.cblc.and_then(cblc::Table::parse) {
996 raw_tables
997 .cbdt
998 .and_then(|data| cbdt::Table::parse(cblc, data))
999 } else {
1000 None
1001 };
1002
1003 Ok(FaceTables {
1004 head,
1005 hhea,
1006 maxp,
1007
1008 cbdt,
1009 cff: raw_tables.cff.and_then(cff::Table::parse),
1010 cmap: raw_tables.cmap.and_then(cmap::Table::parse),
1011 glyf,
1012 hmtx,
1013 kern: raw_tables.kern.and_then(kern::Table::parse),
1014 name: raw_tables.name.and_then(name::Table::parse),
1015 os2: raw_tables.os2.and_then(os2::Table::parse),
1016 post: raw_tables.post.and_then(post::Table::parse),
1017 sbix: raw_tables
1018 .sbix
1019 .and_then(|data| sbix::Table::parse(maxp.number_of_glyphs, data)),
1020 svg: raw_tables.svg.and_then(svg::Table::parse),
1021 vhea: raw_tables.vhea.and_then(vhea::Table::parse),
1022 vmtx,
1023 vorg: raw_tables.vorg.and_then(vorg::Table::parse),
1024
1025 #[cfg(feature = "opentype-layout")]
1026 gdef: raw_tables.gdef.and_then(gdef::Table::parse),
1027 #[cfg(feature = "opentype-layout")]
1028 gpos: raw_tables
1029 .gpos
1030 .and_then(opentype_layout::LayoutTable::parse),
1031 #[cfg(feature = "opentype-layout")]
1032 gsub: raw_tables
1033 .gsub
1034 .and_then(opentype_layout::LayoutTable::parse),
1035 #[cfg(feature = "opentype-layout")]
1036 math: raw_tables.math.and_then(math::Table::parse),
1037
1038 #[cfg(feature = "apple-layout")]
1039 ankr: raw_tables
1040 .ankr
1041 .and_then(|data| ankr::Table::parse(maxp.number_of_glyphs, data)),
1042 #[cfg(feature = "apple-layout")]
1043 feat: raw_tables.feat.and_then(feat::Table::parse),
1044 #[cfg(feature = "apple-layout")]
1045 kerx: raw_tables
1046 .kerx
1047 .and_then(|data| kerx::Table::parse(maxp.number_of_glyphs, data)),
1048 #[cfg(feature = "apple-layout")]
1049 morx: raw_tables
1050 .morx
1051 .and_then(|data| morx::Table::parse(maxp.number_of_glyphs, data)),
1052 #[cfg(feature = "apple-layout")]
1053 trak: raw_tables.trak.and_then(trak::Table::parse),
1054
1055 #[cfg(feature = "variable-fonts")]
1056 avar: raw_tables.avar.and_then(avar::Table::parse),
1057 #[cfg(feature = "variable-fonts")]
1058 cff2: raw_tables.cff2.and_then(cff2::Table::parse),
1059 #[cfg(feature = "variable-fonts")]
1060 fvar: raw_tables.fvar.and_then(fvar::Table::parse),
1061 #[cfg(feature = "variable-fonts")]
1062 gvar: raw_tables.gvar.and_then(gvar::Table::parse),
1063 #[cfg(feature = "variable-fonts")]
1064 hvar: raw_tables.hvar.and_then(hvar::Table::parse),
1065 #[cfg(feature = "variable-fonts")]
1066 mvar: raw_tables.mvar.and_then(mvar::Table::parse),
1067 #[cfg(feature = "variable-fonts")]
1068 vvar: raw_tables.vvar.and_then(hvar::Table::parse),
1069 })
1070 }
1071
1072 /// Returns low-level face tables.
1073 #[inline]
1074 pub fn tables(&self) -> &FaceTables<'a> {
1075 &self.tables
1076 }
1077
1078 /// Returns the `RawFace` used to create this `Face`.
1079 ///
1080 /// Useful if you want to parse the data manually.
1081 ///
1082 /// Available only for faces created using [`Face::parse()`](struct.Face.html#method.parse).
1083 #[inline]
1084 pub fn raw_face(&self) -> &RawFace<'a> {
1085 &self.raw_face
1086 }
1087
1088 /// Returns the raw data of a selected table.
1089 ///
1090 /// Useful if you want to parse the data manually.
1091 ///
1092 /// Available only for faces created using [`Face::parse()`](struct.Face.html#method.parse).
1093 #[deprecated(since = "0.16.0", note = "use `self.raw_face().table()` instead")]
1094 #[inline]
1095 pub fn table_data(&self, tag: Tag) -> Option<&'a [u8]> {
1096 self.raw_face.table(tag)
1097 }
1098
1099 /// Returns a list of names.
1100 ///
1101 /// Contains face name and other strings.
1102 #[inline]
1103 pub fn names(&self) -> name::Names<'a> {
1104 self.tables.name.unwrap_or_default().names
1105 }
1106
1107 /// Checks that face is marked as *Regular*.
1108 ///
1109 /// Returns `false` when OS/2 table is not present.
1110 #[inline]
1111 pub fn is_regular(&self) -> bool {
1112 self.tables
1113 .os2
1114 .map(|s| s.style() == Style::Normal)
1115 .unwrap_or(false)
1116 }
1117
1118 /// Checks that face is marked as *Italic*.
1119 ///
1120 /// Returns `false` when OS/2 table is not present.
1121 #[inline]
1122 pub fn is_italic(&self) -> bool {
1123 self.tables
1124 .os2
1125 .map(|s| s.style() == Style::Italic)
1126 .unwrap_or(false)
1127 }
1128
1129 /// Checks that face is marked as *Bold*.
1130 ///
1131 /// Returns `false` when OS/2 table is not present.
1132 #[inline]
1133 pub fn is_bold(&self) -> bool {
1134 try_opt_or!(self.tables.os2, false).is_bold()
1135 }
1136
1137 /// Checks that face is marked as *Oblique*.
1138 ///
1139 /// Returns `false` when OS/2 table is not present or when its version is < 4.
1140 #[inline]
1141 pub fn is_oblique(&self) -> bool {
1142 self.tables
1143 .os2
1144 .map(|s| s.style() == Style::Oblique)
1145 .unwrap_or(false)
1146 }
1147
1148 /// Returns face style.
1149 #[inline]
1150 pub fn style(&self) -> Style {
1151 try_opt_or!(self.tables.os2, Style::Normal).style()
1152 }
1153
1154 /// Checks that face is marked as *Monospaced*.
1155 ///
1156 /// Returns `false` when `post` table is not present.
1157 #[inline]
1158 pub fn is_monospaced(&self) -> bool {
1159 try_opt_or!(self.tables.post, false).is_monospaced
1160 }
1161
1162 /// Checks that face is variable.
1163 ///
1164 /// Simply checks the presence of a `fvar` table.
1165 #[inline]
1166 pub fn is_variable(&self) -> bool {
1167 #[cfg(feature = "variable-fonts")]
1168 {
1169 // `fvar::Table::parse` already checked that `axisCount` is non-zero.
1170 self.tables.fvar.is_some()
1171 }
1172
1173 #[cfg(not(feature = "variable-fonts"))]
1174 {
1175 false
1176 }
1177 }
1178
1179 /// Returns face's weight.
1180 ///
1181 /// Returns `Weight::Normal` when OS/2 table is not present.
1182 #[inline]
1183 pub fn weight(&self) -> Weight {
1184 try_opt_or!(self.tables.os2, Weight::default()).weight()
1185 }
1186
1187 /// Returns face's width.
1188 ///
1189 /// Returns `Width::Normal` when OS/2 table is not present or when value is invalid.
1190 #[inline]
1191 pub fn width(&self) -> Width {
1192 try_opt_or!(self.tables.os2, Width::default()).width()
1193 }
1194
1195 /// Returns face's italic angle.
1196 ///
1197 /// Returns `None` when `post` table is not present.
1198 #[inline]
1199 pub fn italic_angle(&self) -> Option<f32> {
1200 self.tables.post.map(|table| table.italic_angle)
1201 }
1202
1203 // Read https://github.com/freetype/freetype/blob/49270c17011491227ec7bd3fb73ede4f674aa065/src/sfnt/sfobjs.c#L1279
1204 // to learn more about the logic behind the following functions.
1205
1206 /// Returns a horizontal face ascender.
1207 ///
1208 /// This method is affected by variation axes.
1209 #[inline]
1210 pub fn ascender(&self) -> i16 {
1211 if let Some(os_2) = self.tables.os2 {
1212 if os_2.use_typographic_metrics() {
1213 let value = os_2.typographic_ascender();
1214 return self.apply_metrics_variation(Tag::from_bytes(b"hasc"), value);
1215 }
1216 }
1217
1218 let mut value = self.tables.hhea.ascender;
1219 if value == 0 {
1220 if let Some(os_2) = self.tables.os2 {
1221 value = os_2.typographic_ascender();
1222 if value == 0 {
1223 value = os_2.windows_ascender();
1224 value = self.apply_metrics_variation(Tag::from_bytes(b"hcla"), value);
1225 } else {
1226 value = self.apply_metrics_variation(Tag::from_bytes(b"hasc"), value);
1227 }
1228 }
1229 }
1230
1231 value
1232 }
1233
1234 /// Returns a horizontal face descender.
1235 ///
1236 /// This method is affected by variation axes.
1237 #[inline]
1238 pub fn descender(&self) -> i16 {
1239 if let Some(os_2) = self.tables.os2 {
1240 if os_2.use_typographic_metrics() {
1241 let value = os_2.typographic_descender();
1242 return self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), value);
1243 }
1244 }
1245
1246 let mut value = self.tables.hhea.descender;
1247 if value == 0 {
1248 if let Some(os_2) = self.tables.os2 {
1249 value = os_2.typographic_descender();
1250 if value == 0 {
1251 value = os_2.windows_descender();
1252 value = self.apply_metrics_variation(Tag::from_bytes(b"hcld"), value);
1253 } else {
1254 value = self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), value);
1255 }
1256 }
1257 }
1258
1259 value
1260 }
1261
1262 /// Returns face's height.
1263 ///
1264 /// This method is affected by variation axes.
1265 #[inline]
1266 pub fn height(&self) -> i16 {
1267 self.ascender() - self.descender()
1268 }
1269
1270 /// Returns a horizontal face line gap.
1271 ///
1272 /// This method is affected by variation axes.
1273 #[inline]
1274 pub fn line_gap(&self) -> i16 {
1275 if let Some(os_2) = self.tables.os2 {
1276 if os_2.use_typographic_metrics() {
1277 let value = os_2.typographic_line_gap();
1278 return self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), value);
1279 }
1280 }
1281
1282 let mut value = self.tables.hhea.line_gap;
1283 // For line gap, we have to check that ascender or descender are 0, not line gap itself.
1284 if self.tables.hhea.ascender == 0 || self.tables.hhea.descender == 0 {
1285 if let Some(os_2) = self.tables.os2 {
1286 if os_2.typographic_ascender() != 0 || os_2.typographic_descender() != 0 {
1287 value = os_2.typographic_line_gap();
1288 value = self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), value);
1289 } else {
1290 value = 0;
1291 }
1292 }
1293 }
1294
1295 value
1296 }
1297
1298 /// Returns a horizontal typographic face ascender.
1299 ///
1300 /// Prefer `Face::ascender` unless you explicitly want this. This is a more
1301 /// low-level alternative.
1302 ///
1303 /// This method is affected by variation axes.
1304 ///
1305 /// Returns `None` when OS/2 table is not present.
1306 #[inline]
1307 pub fn typographic_ascender(&self) -> Option<i16> {
1308 self.tables.os2.map(|table| {
1309 let v = table.typographic_ascender();
1310 self.apply_metrics_variation(Tag::from_bytes(b"hasc"), v)
1311 })
1312 }
1313
1314 /// Returns a horizontal typographic face descender.
1315 ///
1316 /// Prefer `Face::descender` unless you explicitly want this. This is a more
1317 /// low-level alternative.
1318 ///
1319 /// This method is affected by variation axes.
1320 ///
1321 /// Returns `None` when OS/2 table is not present.
1322 #[inline]
1323 pub fn typographic_descender(&self) -> Option<i16> {
1324 self.tables.os2.map(|table| {
1325 let v = table.typographic_descender();
1326 self.apply_metrics_variation(Tag::from_bytes(b"hdsc"), v)
1327 })
1328 }
1329
1330 /// Returns a horizontal typographic face line gap.
1331 ///
1332 /// Prefer `Face::line_gap` unless you explicitly want this. This is a more
1333 /// low-level alternative.
1334 ///
1335 /// This method is affected by variation axes.
1336 ///
1337 /// Returns `None` when OS/2 table is not present.
1338 #[inline]
1339 pub fn typographic_line_gap(&self) -> Option<i16> {
1340 self.tables.os2.map(|table| {
1341 let v = table.typographic_line_gap();
1342 self.apply_metrics_variation(Tag::from_bytes(b"hlgp"), v)
1343 })
1344 }
1345
1346 /// Returns a vertical face ascender.
1347 ///
1348 /// This method is affected by variation axes.
1349 #[inline]
1350 pub fn vertical_ascender(&self) -> Option<i16> {
1351 self.tables
1352 .vhea
1353 .map(|vhea| vhea.ascender)
1354 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vasc"), v))
1355 }
1356
1357 /// Returns a vertical face descender.
1358 ///
1359 /// This method is affected by variation axes.
1360 #[inline]
1361 pub fn vertical_descender(&self) -> Option<i16> {
1362 self.tables
1363 .vhea
1364 .map(|vhea| vhea.descender)
1365 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vdsc"), v))
1366 }
1367
1368 /// Returns a vertical face height.
1369 ///
1370 /// This method is affected by variation axes.
1371 #[inline]
1372 pub fn vertical_height(&self) -> Option<i16> {
1373 Some(self.vertical_ascender()? - self.vertical_descender()?)
1374 }
1375
1376 /// Returns a vertical face line gap.
1377 ///
1378 /// This method is affected by variation axes.
1379 #[inline]
1380 pub fn vertical_line_gap(&self) -> Option<i16> {
1381 self.tables
1382 .vhea
1383 .map(|vhea| vhea.line_gap)
1384 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"vlgp"), v))
1385 }
1386
1387 /// Returns face's units per EM.
1388 ///
1389 /// Guarantee to be in a 16..=16384 range.
1390 #[inline]
1391 pub fn units_per_em(&self) -> u16 {
1392 self.tables.head.units_per_em
1393 }
1394
1395 /// Returns face's x height.
1396 ///
1397 /// This method is affected by variation axes.
1398 ///
1399 /// Returns `None` when OS/2 table is not present or when its version is < 2.
1400 #[inline]
1401 pub fn x_height(&self) -> Option<i16> {
1402 self.tables
1403 .os2
1404 .and_then(|os_2| os_2.x_height())
1405 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"xhgt"), v))
1406 }
1407
1408 /// Returns face's capital height.
1409 ///
1410 /// This method is affected by variation axes.
1411 ///
1412 /// Returns `None` when OS/2 table is not present or when its version is < 2.
1413 #[inline]
1414 pub fn capital_height(&self) -> Option<i16> {
1415 self.tables
1416 .os2
1417 .and_then(|os_2| os_2.capital_height())
1418 .map(|v| self.apply_metrics_variation(Tag::from_bytes(b"cpht"), v))
1419 }
1420
1421 /// Returns face's underline metrics.
1422 ///
1423 /// This method is affected by variation axes.
1424 ///
1425 /// Returns `None` when `post` table is not present.
1426 #[inline]
1427 pub fn underline_metrics(&self) -> Option<LineMetrics> {
1428 let mut metrics = self.tables.post?.underline_metrics;
1429
1430 if self.is_variable() {
1431 self.apply_metrics_variation_to(Tag::from_bytes(b"undo"), &mut metrics.position);
1432 self.apply_metrics_variation_to(Tag::from_bytes(b"unds"), &mut metrics.thickness);
1433 }
1434
1435 Some(metrics)
1436 }
1437
1438 /// Returns face's strikeout metrics.
1439 ///
1440 /// This method is affected by variation axes.
1441 ///
1442 /// Returns `None` when OS/2 table is not present.
1443 #[inline]
1444 pub fn strikeout_metrics(&self) -> Option<LineMetrics> {
1445 let mut metrics = self.tables.os2?.strikeout_metrics();
1446
1447 if self.is_variable() {
1448 self.apply_metrics_variation_to(Tag::from_bytes(b"stro"), &mut metrics.position);
1449 self.apply_metrics_variation_to(Tag::from_bytes(b"strs"), &mut metrics.thickness);
1450 }
1451
1452 Some(metrics)
1453 }
1454
1455 /// Returns face's subscript metrics.
1456 ///
1457 /// This method is affected by variation axes.
1458 ///
1459 /// Returns `None` when OS/2 table is not present.
1460 #[inline]
1461 pub fn subscript_metrics(&self) -> Option<ScriptMetrics> {
1462 let mut metrics = self.tables.os2?.subscript_metrics();
1463
1464 if self.is_variable() {
1465 self.apply_metrics_variation_to(Tag::from_bytes(b"sbxs"), &mut metrics.x_size);
1466 self.apply_metrics_variation_to(Tag::from_bytes(b"sbys"), &mut metrics.y_size);
1467 self.apply_metrics_variation_to(Tag::from_bytes(b"sbxo"), &mut metrics.x_offset);
1468 self.apply_metrics_variation_to(Tag::from_bytes(b"sbyo"), &mut metrics.y_offset);
1469 }
1470
1471 Some(metrics)
1472 }
1473
1474 /// Returns face's superscript metrics.
1475 ///
1476 /// This method is affected by variation axes.
1477 ///
1478 /// Returns `None` when OS/2 table is not present.
1479 #[inline]
1480 pub fn superscript_metrics(&self) -> Option<ScriptMetrics> {
1481 let mut metrics = self.tables.os2?.superscript_metrics();
1482
1483 if self.is_variable() {
1484 self.apply_metrics_variation_to(Tag::from_bytes(b"spxs"), &mut metrics.x_size);
1485 self.apply_metrics_variation_to(Tag::from_bytes(b"spys"), &mut metrics.y_size);
1486 self.apply_metrics_variation_to(Tag::from_bytes(b"spxo"), &mut metrics.x_offset);
1487 self.apply_metrics_variation_to(Tag::from_bytes(b"spyo"), &mut metrics.y_offset);
1488 }
1489
1490 Some(metrics)
1491 }
1492
1493 /// Returns face permissions.
1494 ///
1495 /// Returns `None` in case of a malformed value.
1496 #[inline]
1497 pub fn permissions(&self) -> Option<Permissions> {
1498 self.tables.os2?.permissions()
1499 }
1500
1501 /// Checks if the face subsetting is allowed.
1502 #[inline]
1503 pub fn is_subsetting_allowed(&self) -> bool {
1504 self.tables
1505 .os2
1506 .map(|t| t.is_subsetting_allowed())
1507 .unwrap_or(false)
1508 }
1509
1510 /// Checks if the face bitmaps embedding is allowed.
1511 #[inline]
1512 pub fn is_bitmap_embedding_allowed(&self) -> bool {
1513 self.tables
1514 .os2
1515 .map(|t| t.is_bitmap_embedding_allowed())
1516 .unwrap_or(false)
1517 }
1518
1519 /// Checks if the face bitmaps embedding is allowed.
1520 #[inline]
1521 pub fn unicode_ranges(&self) -> UnicodeRanges {
1522 self.tables
1523 .os2
1524 .map(|t| t.unicode_ranges())
1525 .unwrap_or_default()
1526 }
1527
1528 /// Returns a total number of glyphs in the face.
1529 ///
1530 /// Never zero.
1531 ///
1532 /// The value was already parsed, so this function doesn't involve any parsing.
1533 #[inline]
1534 pub fn number_of_glyphs(&self) -> u16 {
1535 self.tables.maxp.number_of_glyphs.get()
1536 }
1537
1538 /// Resolves a Glyph ID for a code point.
1539 ///
1540 /// Returns `None` instead of `0` when glyph is not found.
1541 ///
1542 /// All subtable formats except Mixed Coverage (8) are supported.
1543 ///
1544 /// If you need a more low-level control, prefer `Face::tables().cmap`.
1545 #[inline]
1546 pub fn glyph_index(&self, code_point: char) -> Option<GlyphId> {
1547 for subtable in self.tables.cmap?.subtables {
1548 if !subtable.is_unicode() {
1549 continue;
1550 }
1551
1552 if let Some(id) = subtable.glyph_index(u32::from(code_point)) {
1553 return Some(id);
1554 }
1555 }
1556
1557 None
1558 }
1559
1560 /// Resolves a Glyph ID for a glyph name.
1561 ///
1562 /// Uses the `post` and `CFF` tables as sources.
1563 ///
1564 /// Returns `None` when no name is associated with a `glyph`.
1565 #[cfg(feature = "glyph-names")]
1566 #[inline]
1567 pub fn glyph_index_by_name(&self, name: &str) -> Option<GlyphId> {
1568 if let Some(name) = self
1569 .tables
1570 .post
1571 .and_then(|post| post.glyph_index_by_name(name))
1572 {
1573 return Some(name);
1574 }
1575
1576 if let Some(name) = self
1577 .tables
1578 .cff
1579 .as_ref()
1580 .and_then(|cff| cff.glyph_index_by_name(name))
1581 {
1582 return Some(name);
1583 }
1584
1585 None
1586 }
1587
1588 /// Resolves a variation of a Glyph ID from two code points.
1589 ///
1590 /// Implemented according to
1591 /// [Unicode Variation Sequences](
1592 /// https://docs.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences).
1593 ///
1594 /// Returns `None` instead of `0` when glyph is not found.
1595 #[inline]
1596 pub fn glyph_variation_index(&self, code_point: char, variation: char) -> Option<GlyphId> {
1597 for subtable in self.tables.cmap?.subtables {
1598 if let cmap::Format::UnicodeVariationSequences(ref table) = subtable.format {
1599 return match table.glyph_index(u32::from(code_point), u32::from(variation))? {
1600 cmap::GlyphVariationResult::Found(v) => Some(v),
1601 cmap::GlyphVariationResult::UseDefault => self.glyph_index(code_point),
1602 };
1603 }
1604 }
1605
1606 None
1607 }
1608
1609 /// Returns glyph's horizontal advance.
1610 ///
1611 /// This method is affected by variation axes.
1612 #[inline]
1613 pub fn glyph_hor_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1614 #[cfg(feature = "variable-fonts")]
1615 {
1616 let mut advance = self.tables.hmtx?.advance(glyph_id)? as f32;
1617
1618 if self.is_variable() {
1619 // Ignore variation offset when `hvar` is not set.
1620 if let Some(hvar) = self.tables.hvar {
1621 if let Some(offset) = hvar.advance_offset(glyph_id, self.coords()) {
1622 // We can't use `round()` in `no_std`, so this is the next best thing.
1623 advance += offset + 0.5;
1624 }
1625 }
1626 }
1627
1628 u16::try_num_from(advance)
1629 }
1630
1631 #[cfg(not(feature = "variable-fonts"))]
1632 {
1633 self.tables.hmtx?.advance(glyph_id)
1634 }
1635 }
1636
1637 /// Returns glyph's vertical advance.
1638 ///
1639 /// This method is affected by variation axes.
1640 #[inline]
1641 pub fn glyph_ver_advance(&self, glyph_id: GlyphId) -> Option<u16> {
1642 #[cfg(feature = "variable-fonts")]
1643 {
1644 let mut advance = self.tables.vmtx?.advance(glyph_id)? as f32;
1645
1646 if self.is_variable() {
1647 // Ignore variation offset when `vvar` is not set.
1648 if let Some(vvar) = self.tables.vvar {
1649 if let Some(offset) = vvar.advance_offset(glyph_id, self.coords()) {
1650 // We can't use `round()` in `no_std`, so this is the next best thing.
1651 advance += offset + 0.5;
1652 }
1653 }
1654 }
1655
1656 u16::try_num_from(advance)
1657 }
1658
1659 #[cfg(not(feature = "variable-fonts"))]
1660 {
1661 self.tables.vmtx?.advance(glyph_id)
1662 }
1663 }
1664
1665 /// Returns glyph's horizontal side bearing.
1666 ///
1667 /// This method is affected by variation axes.
1668 #[inline]
1669 pub fn glyph_hor_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1670 #[cfg(feature = "variable-fonts")]
1671 {
1672 let mut bearing = self.tables.hmtx?.side_bearing(glyph_id)? as f32;
1673
1674 if self.is_variable() {
1675 // Ignore variation offset when `hvar` is not set.
1676 if let Some(hvar) = self.tables.hvar {
1677 if let Some(offset) = hvar.side_bearing_offset(glyph_id, self.coords()) {
1678 // We can't use `round()` in `no_std`, so this is the next best thing.
1679 bearing += offset + 0.5;
1680 }
1681 }
1682 }
1683
1684 i16::try_num_from(bearing)
1685 }
1686
1687 #[cfg(not(feature = "variable-fonts"))]
1688 {
1689 self.tables.hmtx?.side_bearing(glyph_id)
1690 }
1691 }
1692
1693 /// Returns glyph's vertical side bearing.
1694 ///
1695 /// This method is affected by variation axes.
1696 #[inline]
1697 pub fn glyph_ver_side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
1698 #[cfg(feature = "variable-fonts")]
1699 {
1700 let mut bearing = self.tables.vmtx?.side_bearing(glyph_id)? as f32;
1701
1702 if self.is_variable() {
1703 // Ignore variation offset when `vvar` is not set.
1704 if let Some(vvar) = self.tables.vvar {
1705 if let Some(offset) = vvar.side_bearing_offset(glyph_id, self.coords()) {
1706 // We can't use `round()` in `no_std`, so this is the next best thing.
1707 bearing += offset + 0.5;
1708 }
1709 }
1710 }
1711
1712 i16::try_num_from(bearing)
1713 }
1714
1715 #[cfg(not(feature = "variable-fonts"))]
1716 {
1717 self.tables.vmtx?.side_bearing(glyph_id)
1718 }
1719 }
1720
1721 /// Returns glyph's vertical origin according to
1722 /// [Vertical Origin Table](https://docs.microsoft.com/en-us/typography/opentype/spec/vorg).
1723 pub fn glyph_y_origin(&self, glyph_id: GlyphId) -> Option<i16> {
1724 self.tables.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))
1725 }
1726
1727 /// Returns glyph's name.
1728 ///
1729 /// Uses the `post` and `CFF` tables as sources.
1730 ///
1731 /// Returns `None` when no name is associated with a `glyph`.
1732 #[cfg(feature = "glyph-names")]
1733 #[inline]
1734 pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&str> {
1735 if let Some(name) = self.tables.post.and_then(|post| post.glyph_name(glyph_id)) {
1736 return Some(name);
1737 }
1738
1739 if let Some(name) = self
1740 .tables
1741 .cff
1742 .as_ref()
1743 .and_then(|cff1| cff1.glyph_name(glyph_id))
1744 {
1745 return Some(name);
1746 }
1747
1748 None
1749 }
1750
1751 /// Outlines a glyph and returns its tight bounding box.
1752 ///
1753 /// **Warning**: since `ttf-parser` is a pull parser,
1754 /// `OutlineBuilder` will emit segments even when outline is partially malformed.
1755 /// You must check `outline_glyph()` result before using
1756 /// `OutlineBuilder`'s output.
1757 ///
1758 /// `gvar`, `glyf`, `CFF` and `CFF2` tables are supported.
1759 /// And they will be accesses in this specific order.
1760 ///
1761 /// This method is affected by variation axes.
1762 ///
1763 /// Returns `None` when glyph has no outline or on error.
1764 ///
1765 /// # Example
1766 ///
1767 /// ```
1768 /// use std::fmt::Write;
1769 /// use ttf_parser;
1770 ///
1771 /// struct Builder(String);
1772 ///
1773 /// impl ttf_parser::OutlineBuilder for Builder {
1774 /// fn move_to(&mut self, x: f32, y: f32) {
1775 /// write!(&mut self.0, "M {} {} ", x, y).unwrap();
1776 /// }
1777 ///
1778 /// fn line_to(&mut self, x: f32, y: f32) {
1779 /// write!(&mut self.0, "L {} {} ", x, y).unwrap();
1780 /// }
1781 ///
1782 /// fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
1783 /// write!(&mut self.0, "Q {} {} {} {} ", x1, y1, x, y).unwrap();
1784 /// }
1785 ///
1786 /// fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
1787 /// write!(&mut self.0, "C {} {} {} {} {} {} ", x1, y1, x2, y2, x, y).unwrap();
1788 /// }
1789 ///
1790 /// fn close(&mut self) {
1791 /// write!(&mut self.0, "Z ").unwrap();
1792 /// }
1793 /// }
1794 ///
1795 /// let data = std::fs::read("tests/fonts/demo.ttf").unwrap();
1796 /// let face = ttf_parser::Face::parse(&data, 0).unwrap();
1797 /// let mut builder = Builder(String::new());
1798 /// let bbox = face.outline_glyph(ttf_parser::GlyphId(1), &mut builder).unwrap();
1799 /// assert_eq!(builder.0, "M 173 267 L 369 267 L 270 587 L 173 267 Z M 6 0 L 224 656 \
1800 /// L 320 656 L 541 0 L 452 0 L 390 200 L 151 200 L 85 0 L 6 0 Z ");
1801 /// assert_eq!(bbox, ttf_parser::Rect { x_min: 6, y_min: 0, x_max: 541, y_max: 656 });
1802 /// ```
1803 #[inline]
1804 pub fn outline_glyph(
1805 &self,
1806 glyph_id: GlyphId,
1807 builder: &mut dyn OutlineBuilder,
1808 ) -> Option<Rect> {
1809 #[cfg(feature = "variable-fonts")]
1810 {
1811 if let Some(ref gvar) = self.tables.gvar {
1812 return gvar.outline(self.tables.glyf?, self.coords(), glyph_id, builder);
1813 }
1814 }
1815
1816 if let Some(table) = self.tables.glyf {
1817 return table.outline(glyph_id, builder);
1818 }
1819
1820 if let Some(ref cff) = self.tables.cff {
1821 return cff.outline(glyph_id, builder).ok();
1822 }
1823
1824 #[cfg(feature = "variable-fonts")]
1825 {
1826 if let Some(ref cff2) = self.tables.cff2 {
1827 return cff2.outline(self.coords(), glyph_id, builder).ok();
1828 }
1829 }
1830
1831 None
1832 }
1833
1834 /// Returns a tight glyph bounding box.
1835 ///
1836 /// This is just a shorthand for `outline_glyph()` since only the `glyf` table stores
1837 /// a bounding box. We ignore `glyf` table bboxes because they can be malformed.
1838 /// In case of CFF and variable fonts we have to actually outline
1839 /// a glyph to find it's bounding box.
1840 ///
1841 /// When a glyph is defined by a raster or a vector image,
1842 /// that can be obtained via `glyph_image()`,
1843 /// the bounding box must be calculated manually and this method will return `None`.
1844 ///
1845 /// Note: the returned bbox is not validated in any way. A font file can have a glyph bbox
1846 /// set to zero/negative width and/or height and this is perfectly ok.
1847 /// For calculated bboxes, zero width and/or height is also perfectly fine.
1848 ///
1849 /// This method is affected by variation axes.
1850 #[inline]
1851 pub fn glyph_bounding_box(&self, glyph_id: GlyphId) -> Option<Rect> {
1852 self.outline_glyph(glyph_id, &mut DummyOutline)
1853 }
1854
1855 /// Returns a bounding box that large enough to enclose any glyph from the face.
1856 #[inline]
1857 pub fn global_bounding_box(&self) -> Rect {
1858 self.tables.head.global_bbox
1859 }
1860
1861 /// Returns a reference to a glyph's raster image.
1862 ///
1863 /// A font can define a glyph using a raster or a vector image instead of a simple outline.
1864 /// Which is primarily used for emojis. This method should be used to access raster images.
1865 ///
1866 /// `pixels_per_em` allows selecting a preferred image size. The chosen size will
1867 /// be closer to an upper one. So when font has 64px and 96px images and `pixels_per_em`
1868 /// is set to 72, 96px image will be returned.
1869 /// To get the largest image simply use `std::u16::MAX`.
1870 ///
1871 /// Note that this method will return an encoded image. It should be decoded
1872 /// by the caller. We don't validate or preprocess it in any way.
1873 ///
1874 /// Currently, only PNG images are supported.
1875 ///
1876 /// Also, a font can contain both: images and outlines. So when this method returns `None`
1877 /// you should also try `outline_glyph()` afterwards.
1878 ///
1879 /// There are multiple ways an image can be stored in a TrueType font
1880 /// and this method supports only `sbix`, `CBLC`+`CBDT`.
1881 /// Font's tables be accesses in this specific order.
1882 #[inline]
1883 pub fn glyph_raster_image(
1884 &self,
1885 glyph_id: GlyphId,
1886 pixels_per_em: u16,
1887 ) -> Option<RasterGlyphImage> {
1888 if let Some(table) = self.tables.sbix {
1889 if let Some(strike) = table.best_strike(pixels_per_em) {
1890 return strike.get(glyph_id);
1891 }
1892 }
1893
1894 if let Some(cbdt) = self.tables.cbdt {
1895 return cbdt.get(glyph_id, pixels_per_em);
1896 }
1897
1898 None
1899 }
1900
1901 /// Returns a reference to a glyph's SVG image.
1902 ///
1903 /// A font can define a glyph using a raster or a vector image instead of a simple outline.
1904 /// Which is primarily used for emojis. This method should be used to access SVG images.
1905 ///
1906 /// Note that this method will return just an SVG data. It should be rendered
1907 /// or even decompressed (in case of SVGZ) by the caller.
1908 /// We don't validate or preprocess it in any way.
1909 ///
1910 /// Also, a font can contain both: images and outlines. So when this method returns `None`
1911 /// you should also try `outline_glyph()` afterwards.
1912 #[inline]
1913 pub fn glyph_svg_image(&self, glyph_id: GlyphId) -> Option<&'a [u8]> {
1914 self.tables.svg.and_then(|svg| svg.documents.find(glyph_id))
1915 }
1916
1917 /// Returns an iterator over variation axes.
1918 #[cfg(feature = "variable-fonts")]
1919 #[inline]
1920 pub fn variation_axes(&self) -> LazyArray16<'a, VariationAxis> {
1921 self.tables.fvar.map(|fvar| fvar.axes).unwrap_or_default()
1922 }
1923
1924 /// Sets a variation axis coordinate.
1925 ///
1926 /// This is the only mutable method in the library.
1927 /// We can simplify the API a lot by storing the variable coordinates
1928 /// in the face object itself.
1929 ///
1930 /// Since coordinates are stored on the stack, we allow only 32 of them.
1931 ///
1932 /// Returns `None` when face is not variable or doesn't have such axis.
1933 #[cfg(feature = "variable-fonts")]
1934 pub fn set_variation(&mut self, axis: Tag, value: f32) -> Option<()> {
1935 if !self.is_variable() {
1936 return None;
1937 }
1938
1939 let v = self
1940 .variation_axes()
1941 .into_iter()
1942 .enumerate()
1943 .find(|(_, a)| a.tag == axis);
1944 if let Some((idx, a)) = v {
1945 if idx >= MAX_VAR_COORDS {
1946 return None;
1947 }
1948
1949 self.coordinates.data[idx] = a.normalized_value(value);
1950 } else {
1951 return None;
1952 }
1953
1954 // TODO: optimize
1955 if let Some(avar) = self.tables.avar {
1956 // Ignore error.
1957 let _ = avar.map_coordinates(self.coordinates.as_mut_slice());
1958 }
1959
1960 Some(())
1961 }
1962
1963 /// Returns the current normalized variation coordinates.
1964 #[cfg(feature = "variable-fonts")]
1965 #[inline]
1966 pub fn variation_coordinates(&self) -> &[NormalizedCoordinate] {
1967 self.coordinates.as_slice()
1968 }
1969
1970 /// Checks that face has non-default variation coordinates.
1971 #[cfg(feature = "variable-fonts")]
1972 #[inline]
1973 pub fn has_non_default_variation_coordinates(&self) -> bool {
1974 self.coordinates.as_slice().iter().any(|c| c.0 != 0)
1975 }
1976
1977 #[cfg(feature = "variable-fonts")]
1978 #[inline]
1979 fn metrics_var_offset(&self, tag: Tag) -> f32 {
1980 self.tables
1981 .mvar
1982 .and_then(|table| table.metric_offset(tag, self.coords()))
1983 .unwrap_or(0.0)
1984 }
1985
1986 #[inline]
1987 fn apply_metrics_variation(&self, tag: Tag, mut value: i16) -> i16 {
1988 self.apply_metrics_variation_to(tag, &mut value);
1989 value
1990 }
1991
1992 #[cfg(feature = "variable-fonts")]
1993 #[inline]
1994 fn apply_metrics_variation_to(&self, tag: Tag, value: &mut i16) {
1995 if self.is_variable() {
1996 let v = f32::from(*value) + self.metrics_var_offset(tag);
1997 // TODO: Should probably round it, but f32::round is not available in core.
1998 if let Some(v) = i16::try_num_from(v) {
1999 *value = v;
2000 }
2001 }
2002 }
2003
2004 #[cfg(not(feature = "variable-fonts"))]
2005 #[inline]
2006 fn apply_metrics_variation_to(&self, _: Tag, _: &mut i16) {}
2007
2008 #[cfg(feature = "variable-fonts")]
2009 #[inline]
2010 fn coords(&self) -> &[NormalizedCoordinate] {
2011 self.coordinates.as_slice()
2012 }
2013}
2014
2015struct DefaultTableProvider<'a> {
2016 data: &'a [u8],
2017 tables: LazyArrayIter16<'a, TableRecord>,
2018}
2019
2020impl<'a> Iterator for DefaultTableProvider<'a> {
2021 type Item = Result<(Tag, Option<&'a [u8]>), FaceParsingError>;
2022
2023 #[inline]
2024 fn next(&mut self) -> Option<Self::Item> {
2025 self.tables.next().map(|table: TableRecord| {
2026 Ok((table.tag, {
2027 let offset: usize = usize::num_from(table.offset);
2028 let length: usize = usize::num_from(table.length);
2029 let end: usize = offset
2030 .checked_add(length)
2031 .ok_or(err:FaceParsingError::MalformedFont)?;
2032 let range: Range = offset..end;
2033 self.data.get(index:range)
2034 }))
2035 })
2036 }
2037}
2038
2039impl core::fmt::Debug for Face<'_> {
2040 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2041 write!(f, "Face()")
2042 }
2043}
2044
2045/// Returns the number of fonts stored in a TrueType font collection.
2046///
2047/// Returns `None` if a provided data is not a TrueType font collection.
2048#[inline]
2049pub fn fonts_in_collection(data: &[u8]) -> Option<u32> {
2050 let mut s: Stream<'_> = Stream::new(data);
2051 if s.read::<Magic>()? != Magic::FontCollection {
2052 return None;
2053 }
2054
2055 s.skip::<u32>(); // version
2056 s.read::<u32>()
2057}
2058