typst_library/text/font/
mod.rs

1//! Font handling.
2
3pub mod color;
4
5mod book;
6mod exceptions;
7mod variant;
8
9pub use self::book::{Coverage, FontBook, FontFlags, FontInfo};
10pub use self::variant::{FontStretch, FontStyle, FontVariant, FontWeight};
11
12use std::cell::OnceCell;
13use std::fmt::{self, Debug, Formatter};
14use std::hash::{Hash, Hasher};
15use std::sync::{Arc, OnceLock};
16
17use ttf_parser::{GlyphId, name_id};
18
19use self::book::find_name;
20use crate::foundations::{Bytes, Cast};
21use crate::layout::{Abs, Em, Frame};
22use crate::text::{
23    BottomEdge, DEFAULT_SUBSCRIPT_METRICS, DEFAULT_SUPERSCRIPT_METRICS, TopEdge,
24};
25
26/// An OpenType font.
27///
28/// Values of this type are cheap to clone and hash.
29#[derive(Clone)]
30pub struct Font(Arc<Repr>);
31
32/// The internal representation of a font.
33struct Repr {
34    /// The font's index in the buffer.
35    index: u32,
36    /// Metadata about the font.
37    info: FontInfo,
38    /// The font's metrics.
39    metrics: FontMetrics,
40    /// The underlying ttf-parser face.
41    ttf: ttf_parser::Face<'static>,
42    /// The underlying rustybuzz face.
43    rusty: rustybuzz::Face<'static>,
44    // NOTE: `ttf` and `rusty` reference `data`, so it's important for `data`
45    // to be dropped after them or they will be left dangling while they're
46    // dropped. Fields are dropped in declaration order, so `data` needs to be
47    // declared after `ttf` and `rusty`.
48    /// The raw font data, possibly shared with other fonts from the same
49    /// collection. The vector's allocation must not move, because `ttf` points
50    /// into it using unsafe code.
51    data: Bytes,
52}
53
54impl Font {
55    /// Parse a font from data and collection index.
56    pub fn new(data: Bytes, index: u32) -> Option<Self> {
57        // Safety:
58        // - The slices's location is stable in memory:
59        //   - We don't move the underlying vector
60        //   - Nobody else can move it since we have a strong ref to the `Arc`.
61        // - The internal 'static lifetime is not leaked because its rewritten
62        //   to the self-lifetime in `ttf()`.
63        let slice: &'static [u8] =
64            unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
65
66        let ttf = ttf_parser::Face::parse(slice, index).ok()?;
67        let rusty = rustybuzz::Face::from_slice(slice, index)?;
68        let metrics = FontMetrics::from_ttf(&ttf);
69        let info = FontInfo::from_ttf(&ttf)?;
70
71        Some(Self(Arc::new(Repr { data, index, info, metrics, ttf, rusty })))
72    }
73
74    /// Parse all fonts in the given data.
75    pub fn iter(data: Bytes) -> impl Iterator<Item = Self> {
76        let count = ttf_parser::fonts_in_collection(&data).unwrap_or(1);
77        (0..count).filter_map(move |index| Self::new(data.clone(), index))
78    }
79
80    /// The underlying buffer.
81    pub fn data(&self) -> &Bytes {
82        &self.0.data
83    }
84
85    /// The font's index in the buffer.
86    pub fn index(&self) -> u32 {
87        self.0.index
88    }
89
90    /// The font's metadata.
91    pub fn info(&self) -> &FontInfo {
92        &self.0.info
93    }
94
95    /// The font's metrics.
96    pub fn metrics(&self) -> &FontMetrics {
97        &self.0.metrics
98    }
99
100    /// The font's math constants.
101    #[inline]
102    pub fn math(&self) -> &MathConstants {
103        self.0.metrics.math.get_or_init(|| FontMetrics::init_math(self))
104    }
105
106    /// The number of font units per one em.
107    pub fn units_per_em(&self) -> f64 {
108        self.0.metrics.units_per_em
109    }
110
111    /// Convert from font units to an em length.
112    pub fn to_em(&self, units: impl Into<f64>) -> Em {
113        Em::from_units(units, self.units_per_em())
114    }
115
116    /// Look up the horizontal advance width of a glyph.
117    pub fn x_advance(&self, glyph: u16) -> Option<Em> {
118        self.0
119            .ttf
120            .glyph_hor_advance(GlyphId(glyph))
121            .map(|units| self.to_em(units))
122    }
123
124    /// Look up the vertical advance width of a glyph.
125    pub fn y_advance(&self, glyph: u16) -> Option<Em> {
126        self.0
127            .ttf
128            .glyph_ver_advance(GlyphId(glyph))
129            .map(|units| self.to_em(units))
130    }
131
132    /// Lookup a name by id.
133    pub fn find_name(&self, id: u16) -> Option<String> {
134        find_name(&self.0.ttf, id)
135    }
136
137    /// A reference to the underlying `ttf-parser` face.
138    pub fn ttf(&self) -> &ttf_parser::Face<'_> {
139        // We can't implement Deref because that would leak the
140        // internal 'static lifetime.
141        &self.0.ttf
142    }
143
144    /// A reference to the underlying `rustybuzz` face.
145    pub fn rusty(&self) -> &rustybuzz::Face<'_> {
146        // We can't implement Deref because that would leak the
147        // internal 'static lifetime.
148        &self.0.rusty
149    }
150
151    /// Resolve the top and bottom edges of text.
152    pub fn edges(
153        &self,
154        top_edge: TopEdge,
155        bottom_edge: BottomEdge,
156        font_size: Abs,
157        bounds: TextEdgeBounds,
158    ) -> (Abs, Abs) {
159        let cell = OnceCell::new();
160        let bbox = |gid, f: fn(ttf_parser::Rect) -> i16| {
161            cell.get_or_init(|| self.ttf().glyph_bounding_box(GlyphId(gid)))
162                .map(|bbox| self.to_em(f(bbox)).at(font_size))
163                .unwrap_or_default()
164        };
165
166        let top = match top_edge {
167            TopEdge::Metric(metric) => match metric.try_into() {
168                Ok(metric) => self.metrics().vertical(metric).at(font_size),
169                Err(_) => match bounds {
170                    TextEdgeBounds::Zero => Abs::zero(),
171                    TextEdgeBounds::Frame(frame) => frame.ascent(),
172                    TextEdgeBounds::Glyph(gid) => bbox(gid, |b| b.y_max),
173                },
174            },
175            TopEdge::Length(length) => length.at(font_size),
176        };
177
178        let bottom = match bottom_edge {
179            BottomEdge::Metric(metric) => match metric.try_into() {
180                Ok(metric) => -self.metrics().vertical(metric).at(font_size),
181                Err(_) => match bounds {
182                    TextEdgeBounds::Zero => Abs::zero(),
183                    TextEdgeBounds::Frame(frame) => frame.descent(),
184                    TextEdgeBounds::Glyph(gid) => -bbox(gid, |b| b.y_min),
185                },
186            },
187            BottomEdge::Length(length) => -length.at(font_size),
188        };
189
190        (top, bottom)
191    }
192}
193
194impl Hash for Font {
195    fn hash<H: Hasher>(&self, state: &mut H) {
196        self.0.data.hash(state);
197        self.0.index.hash(state);
198    }
199}
200
201impl Debug for Font {
202    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
203        write!(f, "Font({}, {:?})", self.info().family, self.info().variant)
204    }
205}
206
207impl Eq for Font {}
208
209impl PartialEq for Font {
210    fn eq(&self, other: &Self) -> bool {
211        self.0.data == other.0.data && self.0.index == other.0.index
212    }
213}
214
215/// Metrics of a font.
216#[derive(Debug, Clone)]
217pub struct FontMetrics {
218    /// How many font units represent one em unit.
219    pub units_per_em: f64,
220    /// The distance from the baseline to the typographic ascender.
221    pub ascender: Em,
222    /// The approximate height of uppercase letters.
223    pub cap_height: Em,
224    /// The approximate height of non-ascending lowercase letters.
225    pub x_height: Em,
226    /// The distance from the baseline to the typographic descender.
227    pub descender: Em,
228    /// Recommended metrics for a strikethrough line.
229    pub strikethrough: LineMetrics,
230    /// Recommended metrics for an underline.
231    pub underline: LineMetrics,
232    /// Recommended metrics for an overline.
233    pub overline: LineMetrics,
234    /// Metrics for subscripts, if provided by the font.
235    pub subscript: Option<ScriptMetrics>,
236    /// Metrics for superscripts, if provided by the font.
237    pub superscript: Option<ScriptMetrics>,
238    /// Metrics for math layout.
239    pub math: OnceLock<Box<MathConstants>>,
240}
241
242impl FontMetrics {
243    /// Extract the font's metrics.
244    pub fn from_ttf(ttf: &ttf_parser::Face) -> Self {
245        let units_per_em = f64::from(ttf.units_per_em());
246        let to_em = |units| Em::from_units(units, units_per_em);
247
248        let ascender = to_em(ttf.typographic_ascender().unwrap_or(ttf.ascender()));
249        let cap_height = ttf.capital_height().filter(|&h| h > 0).map_or(ascender, to_em);
250        let x_height = ttf.x_height().filter(|&h| h > 0).map_or(ascender, to_em);
251        let descender = to_em(ttf.typographic_descender().unwrap_or(ttf.descender()));
252
253        let strikeout = ttf.strikeout_metrics();
254        let underline = ttf.underline_metrics();
255
256        let strikethrough = LineMetrics {
257            position: strikeout.map_or(Em::new(0.25), |s| to_em(s.position)),
258            thickness: strikeout
259                .or(underline)
260                .map_or(Em::new(0.06), |s| to_em(s.thickness)),
261        };
262
263        let underline = LineMetrics {
264            position: underline.map_or(Em::new(-0.2), |s| to_em(s.position)),
265            thickness: underline
266                .or(strikeout)
267                .map_or(Em::new(0.06), |s| to_em(s.thickness)),
268        };
269
270        let overline = LineMetrics {
271            position: cap_height + Em::new(0.1),
272            thickness: underline.thickness,
273        };
274
275        let subscript = ttf.subscript_metrics().map(|metrics| ScriptMetrics {
276            width: to_em(metrics.x_size),
277            height: to_em(metrics.y_size),
278            horizontal_offset: to_em(metrics.x_offset),
279            vertical_offset: -to_em(metrics.y_offset),
280        });
281
282        let superscript = ttf.superscript_metrics().map(|metrics| ScriptMetrics {
283            width: to_em(metrics.x_size),
284            height: to_em(metrics.y_size),
285            horizontal_offset: to_em(metrics.x_offset),
286            vertical_offset: to_em(metrics.y_offset),
287        });
288
289        Self {
290            units_per_em,
291            ascender,
292            cap_height,
293            x_height,
294            descender,
295            strikethrough,
296            underline,
297            overline,
298            superscript,
299            subscript,
300            math: OnceLock::new(),
301        }
302    }
303
304    fn init_math(font: &Font) -> Box<MathConstants> {
305        let ttf = font.ttf();
306        let metrics = font.metrics();
307
308        let space_width = ttf
309            .glyph_index(' ')
310            .and_then(|id| ttf.glyph_hor_advance(id).map(|units| font.to_em(units)))
311            .unwrap_or(typst_library::math::THICK);
312
313        let is_cambria = || {
314            font.find_name(name_id::POST_SCRIPT_NAME)
315                .is_some_and(|name| name == "CambriaMath")
316        };
317
318        Box::new(
319            ttf.tables()
320                .math
321                .and_then(|math| math.constants)
322                .map(|constants| MathConstants {
323                    space_width,
324                    script_percent_scale_down: constants.script_percent_scale_down(),
325                    script_script_percent_scale_down: constants
326                        .script_script_percent_scale_down(),
327                    display_operator_min_height: font.to_em(if is_cambria() {
328                        constants.delimited_sub_formula_min_height()
329                    } else {
330                        constants.display_operator_min_height()
331                    }),
332                    axis_height: font.to_em(constants.axis_height().value),
333                    accent_base_height: font.to_em(constants.accent_base_height().value),
334                    flattened_accent_base_height: font
335                        .to_em(constants.flattened_accent_base_height().value),
336                    subscript_shift_down: font
337                        .to_em(constants.subscript_shift_down().value),
338                    subscript_top_max: font.to_em(constants.subscript_top_max().value),
339                    subscript_baseline_drop_min: font
340                        .to_em(constants.subscript_baseline_drop_min().value),
341                    superscript_shift_up: font
342                        .to_em(constants.superscript_shift_up().value),
343                    superscript_shift_up_cramped: font
344                        .to_em(constants.superscript_shift_up_cramped().value),
345                    superscript_bottom_min: font
346                        .to_em(constants.superscript_bottom_min().value),
347                    superscript_baseline_drop_max: font
348                        .to_em(constants.superscript_baseline_drop_max().value),
349                    sub_superscript_gap_min: font
350                        .to_em(constants.sub_superscript_gap_min().value),
351                    superscript_bottom_max_with_subscript: font
352                        .to_em(constants.superscript_bottom_max_with_subscript().value),
353                    space_after_script: font.to_em(constants.space_after_script().value),
354                    upper_limit_gap_min: font
355                        .to_em(constants.upper_limit_gap_min().value),
356                    upper_limit_baseline_rise_min: font
357                        .to_em(constants.upper_limit_baseline_rise_min().value),
358                    lower_limit_gap_min: font
359                        .to_em(constants.lower_limit_gap_min().value),
360                    lower_limit_baseline_drop_min: font
361                        .to_em(constants.lower_limit_baseline_drop_min().value),
362                    fraction_numerator_shift_up: font
363                        .to_em(constants.fraction_numerator_shift_up().value),
364                    fraction_numerator_display_style_shift_up: font.to_em(
365                        constants.fraction_numerator_display_style_shift_up().value,
366                    ),
367                    fraction_denominator_shift_down: font
368                        .to_em(constants.fraction_denominator_shift_down().value),
369                    fraction_denominator_display_style_shift_down: font.to_em(
370                        constants.fraction_denominator_display_style_shift_down().value,
371                    ),
372                    fraction_numerator_gap_min: font
373                        .to_em(constants.fraction_numerator_gap_min().value),
374                    fraction_num_display_style_gap_min: font
375                        .to_em(constants.fraction_num_display_style_gap_min().value),
376                    fraction_rule_thickness: font
377                        .to_em(constants.fraction_rule_thickness().value),
378                    fraction_denominator_gap_min: font
379                        .to_em(constants.fraction_denominator_gap_min().value),
380                    fraction_denom_display_style_gap_min: font
381                        .to_em(constants.fraction_denom_display_style_gap_min().value),
382                    skewed_fraction_vertical_gap: font
383                        .to_em(constants.skewed_fraction_vertical_gap().value),
384                    skewed_fraction_horizontal_gap: font
385                        .to_em(constants.skewed_fraction_horizontal_gap().value),
386                    overbar_vertical_gap: font
387                        .to_em(constants.overbar_vertical_gap().value),
388                    overbar_rule_thickness: font
389                        .to_em(constants.overbar_rule_thickness().value),
390                    overbar_extra_ascender: font
391                        .to_em(constants.overbar_extra_ascender().value),
392                    underbar_vertical_gap: font
393                        .to_em(constants.underbar_vertical_gap().value),
394                    underbar_rule_thickness: font
395                        .to_em(constants.underbar_rule_thickness().value),
396                    underbar_extra_descender: font
397                        .to_em(constants.underbar_extra_descender().value),
398                    radical_vertical_gap: font
399                        .to_em(constants.radical_vertical_gap().value),
400                    radical_display_style_vertical_gap: font
401                        .to_em(constants.radical_display_style_vertical_gap().value),
402                    radical_rule_thickness: font
403                        .to_em(constants.radical_rule_thickness().value),
404                    radical_extra_ascender: font
405                        .to_em(constants.radical_extra_ascender().value),
406                    radical_kern_before_degree: font
407                        .to_em(constants.radical_kern_before_degree().value),
408                    radical_kern_after_degree: font
409                        .to_em(constants.radical_kern_after_degree().value),
410                    radical_degree_bottom_raise_percent: constants
411                        .radical_degree_bottom_raise_percent()
412                        as f64
413                        / 100.0,
414                })
415                // Most of these fallback constants are from the MathML Core
416                // spec, with the exceptions of
417                // - `flattened_accent_base_height` from Building Math Fonts
418                // - `overbar_rule_thickness` and `underbar_rule_thickness`
419                //   from our best guess
420                // - `skewed_fraction_vertical_gap` and `skewed_fraction_horizontal_gap`
421                //   from our best guess
422                // - `script_percent_scale_down` and
423                //   `script_script_percent_scale_down` from Building Math
424                //   Fonts as the defaults given in MathML Core have more
425                //   precision than i16.
426                //
427                // https://www.w3.org/TR/mathml-core/#layout-constants-mathconstants
428                // https://github.com/notofonts/math/blob/main/documentation/building-math-fonts/index.md
429                .unwrap_or(MathConstants {
430                    space_width,
431                    script_percent_scale_down: 70,
432                    script_script_percent_scale_down: 50,
433                    display_operator_min_height: Em::zero(),
434                    axis_height: metrics.x_height / 2.0,
435                    accent_base_height: metrics.x_height,
436                    flattened_accent_base_height: metrics.cap_height,
437                    subscript_shift_down: metrics
438                        .subscript
439                        .map(|metrics| metrics.vertical_offset)
440                        .unwrap_or(DEFAULT_SUBSCRIPT_METRICS.vertical_offset),
441                    subscript_top_max: 0.8 * metrics.x_height,
442                    subscript_baseline_drop_min: Em::zero(),
443                    superscript_shift_up: metrics
444                        .superscript
445                        .map(|metrics| metrics.vertical_offset)
446                        .unwrap_or(DEFAULT_SUPERSCRIPT_METRICS.vertical_offset),
447                    superscript_shift_up_cramped: Em::zero(),
448                    superscript_bottom_min: 0.25 * metrics.x_height,
449                    superscript_baseline_drop_max: Em::zero(),
450                    sub_superscript_gap_min: 4.0 * metrics.underline.thickness,
451                    superscript_bottom_max_with_subscript: 0.8 * metrics.x_height,
452                    space_after_script: Em::new(1.0 / 24.0),
453                    upper_limit_gap_min: Em::zero(),
454                    upper_limit_baseline_rise_min: Em::zero(),
455                    lower_limit_gap_min: Em::zero(),
456                    lower_limit_baseline_drop_min: Em::zero(),
457                    fraction_numerator_shift_up: Em::zero(),
458                    fraction_numerator_display_style_shift_up: Em::zero(),
459                    fraction_denominator_shift_down: Em::zero(),
460                    fraction_denominator_display_style_shift_down: Em::zero(),
461                    fraction_numerator_gap_min: metrics.underline.thickness,
462                    fraction_num_display_style_gap_min: 3.0 * metrics.underline.thickness,
463                    fraction_rule_thickness: metrics.underline.thickness,
464                    fraction_denominator_gap_min: metrics.underline.thickness,
465                    fraction_denom_display_style_gap_min: 3.0
466                        * metrics.underline.thickness,
467                    skewed_fraction_vertical_gap: Em::zero(),
468                    skewed_fraction_horizontal_gap: Em::new(0.5),
469                    overbar_vertical_gap: 3.0 * metrics.underline.thickness,
470                    overbar_rule_thickness: metrics.underline.thickness,
471                    overbar_extra_ascender: metrics.underline.thickness,
472                    underbar_vertical_gap: 3.0 * metrics.underline.thickness,
473                    underbar_rule_thickness: metrics.underline.thickness,
474                    underbar_extra_descender: metrics.underline.thickness,
475                    radical_vertical_gap: 1.25 * metrics.underline.thickness,
476                    radical_display_style_vertical_gap: metrics.underline.thickness
477                        + 0.25 * metrics.x_height,
478                    radical_rule_thickness: metrics.underline.thickness,
479                    radical_extra_ascender: metrics.underline.thickness,
480                    radical_kern_before_degree: Em::new(5.0 / 18.0),
481                    radical_kern_after_degree: Em::new(-10.0 / 18.0),
482                    radical_degree_bottom_raise_percent: 0.6,
483                }),
484        )
485    }
486
487    /// Look up a vertical metric.
488    pub fn vertical(&self, metric: VerticalFontMetric) -> Em {
489        match metric {
490            VerticalFontMetric::Ascender => self.ascender,
491            VerticalFontMetric::CapHeight => self.cap_height,
492            VerticalFontMetric::XHeight => self.x_height,
493            VerticalFontMetric::Baseline => Em::zero(),
494            VerticalFontMetric::Descender => self.descender,
495        }
496    }
497}
498
499/// Metrics for a decorative line.
500#[derive(Debug, Copy, Clone)]
501pub struct LineMetrics {
502    /// The vertical offset of the line from the baseline. Positive goes
503    /// upwards, negative downwards.
504    pub position: Em,
505    /// The thickness of the line.
506    pub thickness: Em,
507}
508
509/// Metrics for subscripts or superscripts.
510#[derive(Debug, Copy, Clone)]
511pub struct ScriptMetrics {
512    /// The width of those scripts, relative to the outer font size.
513    pub width: Em,
514    /// The height of those scripts, relative to the outer font size.
515    pub height: Em,
516    /// The horizontal (to the right) offset of those scripts, relative to the
517    /// outer font size.
518    ///
519    /// This is used for italic correction.
520    pub horizontal_offset: Em,
521    /// The vertical (to the top) offset of those scripts, relative to the outer font size.
522    ///
523    /// For superscripts, this is positive. For subscripts, this is negative.
524    pub vertical_offset: Em,
525}
526
527/// Constants from the OpenType MATH constants table used in Typst.
528///
529/// Ones not currently used are omitted.
530#[derive(Debug, Copy, Clone)]
531pub struct MathConstants {
532    // This is not from the OpenType MATH spec.
533    pub space_width: Em,
534    // These are both i16 instead of f64 as they need to go on the StyleChain.
535    pub script_percent_scale_down: i16,
536    pub script_script_percent_scale_down: i16,
537    pub display_operator_min_height: Em,
538    pub axis_height: Em,
539    pub accent_base_height: Em,
540    pub flattened_accent_base_height: Em,
541    pub subscript_shift_down: Em,
542    pub subscript_top_max: Em,
543    pub subscript_baseline_drop_min: Em,
544    pub superscript_shift_up: Em,
545    pub superscript_shift_up_cramped: Em,
546    pub superscript_bottom_min: Em,
547    pub superscript_baseline_drop_max: Em,
548    pub sub_superscript_gap_min: Em,
549    pub superscript_bottom_max_with_subscript: Em,
550    pub space_after_script: Em,
551    pub upper_limit_gap_min: Em,
552    pub upper_limit_baseline_rise_min: Em,
553    pub lower_limit_gap_min: Em,
554    pub lower_limit_baseline_drop_min: Em,
555    pub fraction_numerator_shift_up: Em,
556    pub fraction_numerator_display_style_shift_up: Em,
557    pub fraction_denominator_shift_down: Em,
558    pub fraction_denominator_display_style_shift_down: Em,
559    pub fraction_numerator_gap_min: Em,
560    pub fraction_num_display_style_gap_min: Em,
561    pub fraction_rule_thickness: Em,
562    pub fraction_denominator_gap_min: Em,
563    pub fraction_denom_display_style_gap_min: Em,
564    pub skewed_fraction_vertical_gap: Em,
565    pub skewed_fraction_horizontal_gap: Em,
566    pub overbar_vertical_gap: Em,
567    pub overbar_rule_thickness: Em,
568    pub overbar_extra_ascender: Em,
569    pub underbar_vertical_gap: Em,
570    pub underbar_rule_thickness: Em,
571    pub underbar_extra_descender: Em,
572    pub radical_vertical_gap: Em,
573    pub radical_display_style_vertical_gap: Em,
574    pub radical_rule_thickness: Em,
575    pub radical_extra_ascender: Em,
576    pub radical_kern_before_degree: Em,
577    pub radical_kern_after_degree: Em,
578    pub radical_degree_bottom_raise_percent: f64,
579}
580
581/// Identifies a vertical metric of a font.
582#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
583pub enum VerticalFontMetric {
584    /// The font's ascender, which typically exceeds the height of all glyphs.
585    Ascender,
586    /// The approximate height of uppercase letters.
587    CapHeight,
588    /// The approximate height of non-ascending lowercase letters.
589    XHeight,
590    /// The baseline on which the letters rest.
591    Baseline,
592    /// The font's ascender, which typically exceeds the depth of all glyphs.
593    Descender,
594}
595
596/// Defines how to resolve a `Bounds` text edge.
597#[derive(Debug, Copy, Clone)]
598pub enum TextEdgeBounds<'a> {
599    /// Set the bounds to zero.
600    Zero,
601    /// Use the bounding box of the given glyph for the bounds.
602    Glyph(u16),
603    /// Use the dimension of the given frame for the bounds.
604    Frame(&'a Frame),
605}