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