Skip to main content

typst_library/text/font/
mod.rs

1//! Font handling.
2
3pub mod color;
4
5mod book;
6mod exceptions;
7mod info;
8mod metrics;
9mod tag;
10mod variant;
11mod variations;
12
13pub use self::book::FontBook;
14pub use self::info::{Coverage, FontFlags, FontInfo};
15pub use self::metrics::{
16    FontMetrics, LineMetrics, MathConstants, ScriptMetrics, TextEdgeBounds,
17    VerticalFontMetric,
18};
19pub use self::tag::Tag;
20pub use self::variant::{FontStretch, FontStyle, FontVariant, FontWeight};
21pub use self::variations::{AxisValue, FontAxis, FontVariations, StandardAxes};
22
23use std::cell::OnceCell;
24use std::fmt::{self, Debug, Formatter};
25use std::hash::{Hash, Hasher};
26use std::ops::Deref;
27use std::sync::Arc;
28
29use ttf_parser::{GlyphId, name_id};
30
31use self::exceptions::find_exception;
32use self::info::find_name;
33use crate::foundations::Bytes;
34use crate::layout::{Abs, Em};
35use crate::text::{BottomEdge, TopEdge};
36
37/// An OpenType font.
38///
39/// Values of this type are cheap to clone and hash.
40#[derive(Clone)]
41pub struct Font(Arc<FontInner>);
42
43/// The internal representation of a [`Font`].
44struct FontInner {
45    /// The font's index in the buffer.
46    index: u32,
47    /// Metadata about the font.
48    info: FontInfo,
49    // NOTE: `ttf` references `data`, so it's important for `data` to be
50    // dropped after `ttf` or `ttf` will be left dangling while the data is
51    // dropped. Fields are dropped in declaration order, so `data` needs to be
52    // declared after `ttf`.
53    /// The underlying ttf-parser face.
54    ttf: ttf_parser::Face<'static>,
55    /// The raw font data, possibly shared with other fonts from the same
56    /// collection. The vector's allocation must not move, because `ttf`
57    /// points into it using unsafe code.
58    data: Bytes,
59}
60
61impl Font {
62    /// Parse a font from data and collection index.
63    pub fn new(data: Bytes, index: u32) -> Option<Self> {
64        // Safety:
65        // - The slices's location is stable in memory:
66        //   - We don't move the underlying vector
67        //   - Nobody else can move it since we have a strong ref to the `Arc`.
68        // - The internal 'static lifetime is not leaked because its rewritten
69        //   to the self-lifetime in `ttf()`.
70        let slice: &'static [u8] =
71            unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
72
73        let ttf = ttf_parser::Face::parse(slice, index).ok()?;
74        let info = FontInfo::from_ttf(&ttf)?;
75
76        Some(Self(Arc::new(FontInner { index, info, ttf, data })))
77    }
78
79    /// Parse all fonts in the given data.
80    pub fn iter(data: Bytes) -> impl Iterator<Item = Self> {
81        let count = ttf_parser::fonts_in_collection(&data).unwrap_or(1);
82        (0..count).filter_map(move |index| Self::new(data.clone(), index))
83    }
84
85    /// The underlying buffer.
86    pub fn data(&self) -> &Bytes {
87        &self.0.data
88    }
89
90    /// The font's index in the buffer.
91    pub fn index(&self) -> u32 {
92        self.0.index
93    }
94
95    /// The font's metadata.
96    pub fn info(&self) -> &FontInfo {
97        &self.0.info
98    }
99
100    /// Determine the font's PostScript name.
101    pub fn post_script_name(&self) -> Option<String> {
102        find_name(&self.0.ttf, name_id::POST_SCRIPT_NAME)
103    }
104
105    /// Instantiates the font with specific text properties. The resulting
106    /// type allows access to methods that depend on coordinates.
107    #[comemo::memoize]
108    pub fn instantiate(
109        self,
110        variant: FontVariant,
111        size: Abs,
112        custom: &FontVariations,
113    ) -> FontInstance {
114        let axes = &self.info().axes;
115        let automatic = FontVariations::resolve(axes, variant, size);
116        let full = automatic.chain(custom).normalized();
117        self.instantiate_impl(full)
118    }
119
120    /// Instantiates the font with specific variation coordinates. The resulting
121    /// type allows access to methods that depend on coordinates.
122    #[comemo::memoize]
123    fn instantiate_impl(self, variations: FontVariations) -> FontInstance {
124        let data = self.data();
125        let index = self.index();
126
127        // Safety: See `Self::new`.
128        let slice: &'static [u8] =
129            unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
130
131        let mut rusty = rustybuzz::Face::from_slice(slice, index).unwrap();
132        for &(tag, value) in &variations.0 {
133            rusty.set_variation(tag.into(), value.0);
134        }
135
136        let metrics = FontMetrics::from_ttf(&rusty);
137
138        FontInstance(Arc::new(FontInstanceInner {
139            metrics,
140            rusty,
141            variations,
142            font: self,
143        }))
144    }
145}
146
147impl Debug for Font {
148    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
149        write!(f, "Font({}, {:?})", self.info().family, self.info().variant)
150    }
151}
152
153impl Hash for Font {
154    fn hash<H: Hasher>(&self, state: &mut H) {
155        self.0.data.hash(state);
156        self.0.index.hash(state);
157    }
158}
159
160impl Eq for Font {}
161
162impl PartialEq for Font {
163    fn eq(&self, other: &Self) -> bool {
164        self.0.data == other.0.data && self.0.index == other.0.index
165    }
166}
167
168/// An OpenType font with fixed variation coordinates.
169///
170/// Values of this type are cheap to clone and hash.
171#[derive(Clone)]
172pub struct FontInstance(Arc<FontInstanceInner>);
173
174/// The internal representation of a [`FontInstance`].
175struct FontInstanceInner {
176    /// The font's metrics.
177    metrics: FontMetrics,
178    // NOTE: `rusty` references `font`, so it's important for `font` to be
179    // dropped after `rusty` or `rusty` will be left dangling while the font is
180    // dropped. Fields are dropped in declaration order, so `font` needs to be
181    // declared after `rusty`.
182    /// The underlying rustybuzz face.
183    rusty: rustybuzz::Face<'static>,
184    // The instance's variation coordinates.
185    variations: FontVariations,
186    /// The underlying font.
187    font: Font,
188}
189
190impl FontInstance {
191    /// The instance's underlying font.
192    pub fn font(&self) -> &Font {
193        &self.0.font
194    }
195
196    /// The instance's variation coordinates.
197    pub fn variations(&self) -> &FontVariations {
198        &self.0.variations
199    }
200
201    /// The font's metrics.
202    pub fn metrics(&self) -> &FontMetrics {
203        &self.0.metrics
204    }
205
206    /// The font's math constants.
207    #[inline]
208    pub fn math(&self) -> &MathConstants {
209        self.0.metrics.math.get_or_init(|| MathConstants::new(self))
210    }
211
212    /// The number of font units per one em.
213    pub fn units_per_em(&self) -> f64 {
214        self.0.metrics.units_per_em
215    }
216
217    /// Convert from font units to an em length.
218    pub fn to_em(&self, units: impl Into<f64>) -> Em {
219        Em::from_units(units, self.units_per_em())
220    }
221
222    /// Look up the horizontal advance width of a glyph.
223    pub fn x_advance(&self, glyph: u16) -> Option<Em> {
224        self.0
225            .rusty
226            .glyph_hor_advance(GlyphId(glyph))
227            .map(|units| self.to_em(units))
228    }
229
230    /// Look up the vertical advance width of a glyph.
231    pub fn y_advance(&self, glyph: u16) -> Option<Em> {
232        self.0
233            .rusty
234            .glyph_ver_advance(GlyphId(glyph))
235            .map(|units| self.to_em(units))
236    }
237
238    /// A reference to the underlying `ttf-parser` face.
239    pub fn ttf(&self) -> &ttf_parser::Face<'_> {
240        // We can't implement Deref because that would leak the
241        // internal 'static lifetime.
242        &self.0.rusty
243    }
244
245    /// A reference to the underlying `rustybuzz` face.
246    pub fn rusty(&self) -> &rustybuzz::Face<'_> {
247        // We can't implement Deref because that would leak the
248        // internal 'static lifetime.
249        &self.0.rusty
250    }
251
252    /// Resolve the top and bottom edges of text.
253    pub fn edges(
254        &self,
255        top_edge: TopEdge,
256        bottom_edge: BottomEdge,
257        font_size: Abs,
258        bounds: TextEdgeBounds,
259    ) -> (Abs, Abs) {
260        let cell = OnceCell::new();
261        let bbox = |gid, f: fn(ttf_parser::Rect) -> i16| {
262            cell.get_or_init(|| self.ttf().glyph_bounding_box(GlyphId(gid)))
263                .map(|bbox| self.to_em(f(bbox)).at(font_size))
264                .unwrap_or_default()
265        };
266
267        let top = match top_edge {
268            TopEdge::Metric(metric) => match metric.try_into() {
269                Ok(metric) => self.metrics().vertical(metric).at(font_size),
270                Err(_) => match bounds {
271                    TextEdgeBounds::Zero => Abs::zero(),
272                    TextEdgeBounds::Frame(frame) => frame.ascent(),
273                    TextEdgeBounds::Glyph(gid) => bbox(gid, |b| b.y_max),
274                },
275            },
276            TopEdge::Length(length) => length.at(font_size),
277        };
278
279        let bottom = match bottom_edge {
280            BottomEdge::Metric(metric) => match metric.try_into() {
281                Ok(metric) => -self.metrics().vertical(metric).at(font_size),
282                Err(_) => match bounds {
283                    TextEdgeBounds::Zero => Abs::zero(),
284                    TextEdgeBounds::Frame(frame) => frame.descent(),
285                    TextEdgeBounds::Glyph(gid) => -bbox(gid, |b| b.y_min),
286                },
287            },
288            BottomEdge::Length(length) => -length.at(font_size),
289        };
290
291        (top, bottom)
292    }
293}
294
295impl Deref for FontInstance {
296    type Target = Font;
297
298    fn deref(&self) -> &Self::Target {
299        self.font()
300    }
301}
302
303impl Debug for FontInstance {
304    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
305        f.debug_struct("FontInstance")
306            .field("font", self.font())
307            .field("variations", self.variations())
308            .finish()
309    }
310}
311
312impl Hash for FontInstance {
313    fn hash<H: Hasher>(&self, state: &mut H) {
314        self.0.font.hash(state);
315        self.0.variations.hash(state);
316    }
317}
318
319impl Eq for FontInstance {}
320
321impl PartialEq for FontInstance {
322    fn eq(&self, other: &Self) -> bool {
323        self.0.font == other.0.font && self.0.variations == other.0.variations
324    }
325}