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