rusttype/
font.rs

1use crate::{Glyph, GlyphIter, IntoGlyphId, LayoutIter, Point, Scale, VMetrics};
2#[cfg(not(feature = "has-atomics"))]
3use alloc::rc::Rc as Arc;
4#[cfg(feature = "has-atomics")]
5use alloc::sync::Arc;
6#[cfg(not(feature = "std"))]
7use alloc::vec::Vec;
8use core::fmt;
9
10/// A single font. This may or may not own the font data.
11///
12/// # Lifetime
13/// The lifetime reflects the font data lifetime. `Font<'static>` covers most
14/// cases ie both dynamically loaded owned data and for referenced compile time
15/// font data.
16///
17/// # Example
18///
19/// ```
20/// # use rusttype::Font;
21/// # fn example() -> Option<()> {
22/// let font_data: &[u8] = include_bytes!("../dev/fonts/dejavu/DejaVuSansMono.ttf");
23/// let font: Font<'static> = Font::try_from_bytes(font_data)?;
24///
25/// let owned_font_data: Vec<u8> = font_data.to_vec();
26/// let from_owned_font: Font<'static> = Font::try_from_vec(owned_font_data)?;
27/// # Some(())
28/// # }
29/// ```
30#[derive(Clone)]
31pub enum Font<'a> {
32    Ref(Arc<owned_ttf_parser::Face<'a>>),
33    Owned(Arc<owned_ttf_parser::OwnedFace>),
34}
35
36impl fmt::Debug for Font<'_> {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        write!(f, "Font")
39    }
40}
41
42impl Font<'_> {
43    /// Creates a Font from byte-slice data.
44    ///
45    /// Returns `None` for invalid data.
46    pub fn try_from_bytes(bytes: &[u8]) -> Option<Font<'_>> {
47        Self::try_from_bytes_and_index(bytes, 0)
48    }
49
50    /// Creates a Font from byte-slice data & a font collection `index`.
51    ///
52    /// Returns `None` for invalid data.
53    pub fn try_from_bytes_and_index(bytes: &[u8], index: u32) -> Option<Font<'_>> {
54        let inner = Arc::new(owned_ttf_parser::Face::from_slice(bytes, index).ok()?);
55        Some(Font::Ref(inner))
56    }
57
58    /// Creates a Font from owned font data.
59    ///
60    /// Returns `None` for invalid data.
61    pub fn try_from_vec(data: Vec<u8>) -> Option<Font<'static>> {
62        Self::try_from_vec_and_index(data, 0)
63    }
64
65    /// Creates a Font from owned font data & a font collection `index`.
66    ///
67    /// Returns `None` for invalid data.
68    pub fn try_from_vec_and_index(data: Vec<u8>, index: u32) -> Option<Font<'static>> {
69        let inner = Arc::new(owned_ttf_parser::OwnedFace::from_vec(data, index).ok()?);
70        Some(Font::Owned(inner))
71    }
72}
73
74impl<'font> Font<'font> {
75    #[inline]
76    pub(crate) fn inner(&self) -> &owned_ttf_parser::Face<'_> {
77        use owned_ttf_parser::AsFaceRef;
78        match self {
79            Self::Ref(f) => f,
80            Self::Owned(f) => f.as_face_ref(),
81        }
82    }
83
84    /// The "vertical metrics" for this font at a given scale. These metrics are
85    /// shared by all of the glyphs in the font. See `VMetrics` for more detail.
86    pub fn v_metrics(&self, scale: Scale) -> VMetrics {
87        self.v_metrics_unscaled() * self.scale_for_pixel_height(scale.y)
88    }
89
90    /// Get the unscaled VMetrics for this font, shared by all glyphs.
91    /// See `VMetrics` for more detail.
92    pub fn v_metrics_unscaled(&self) -> VMetrics {
93        let font = self.inner();
94        VMetrics {
95            ascent: font.ascender() as f32,
96            descent: font.descender() as f32,
97            line_gap: font.line_gap() as f32,
98        }
99    }
100
101    /// Returns the units per EM square of this font
102    pub fn units_per_em(&self) -> u16 {
103        self.inner().units_per_em()
104    }
105
106    /// The number of glyphs present in this font. Glyph identifiers for this
107    /// font will always be in the range `0..self.glyph_count()`
108    pub fn glyph_count(&self) -> usize {
109        self.inner().number_of_glyphs() as _
110    }
111
112    /// Returns the corresponding glyph for a Unicode code point or a glyph id
113    /// for this font.
114    ///
115    /// If `id` is a `GlyphId`, it must be valid for this font; otherwise, this
116    /// function panics. `GlyphId`s should always be produced by looking up some
117    /// other sort of designator (like a Unicode code point) in a font, and
118    /// should only be used to index the font they were produced for.
119    ///
120    /// Note that code points without corresponding glyphs in this font map to
121    /// the ".notdef" glyph, glyph 0.
122    pub fn glyph<C: IntoGlyphId>(&self, id: C) -> Glyph<'font> {
123        let gid = id.into_glyph_id(self);
124        assert!((gid.0 as usize) < self.glyph_count());
125        // font clone either a reference clone, or arc clone
126        Glyph {
127            font: self.clone(),
128            id: gid,
129        }
130    }
131
132    /// A convenience function.
133    ///
134    /// Returns an iterator that produces the glyphs corresponding to the code
135    /// points or glyph ids produced by the given iterator `itr`.
136    ///
137    /// This is equivalent in behaviour to `itr.map(|c| font.glyph(c))`.
138    pub fn glyphs_for<'a, I: Iterator>(&'a self, itr: I) -> GlyphIter<'a, 'font, I>
139    where
140        I::Item: IntoGlyphId,
141    {
142        GlyphIter { font: self, itr }
143    }
144
145    /// A convenience function for laying out glyphs for a string horizontally.
146    /// It does not take control characters like line breaks into account, as
147    /// treatment of these is likely to depend on the application.
148    ///
149    /// Note that this function does not perform Unicode normalisation.
150    /// Composite characters (such as ö constructed from two code points, ¨ and
151    /// o), will not be normalised to single code points. So if a font does not
152    /// contain a glyph for each separate code point, but does contain one for
153    /// the normalised single code point (which is common), the desired glyph
154    /// will not be produced, despite being present in the font. Deal with this
155    /// by performing Unicode normalisation on the input string before passing
156    /// it to `layout`. The crate
157    /// [unicode-normalization](http://crates.io/crates/unicode-normalization)
158    /// is perfect for this purpose.
159    ///
160    /// Calling this function is equivalent to a longer sequence of operations
161    /// involving `glyphs_for`, e.g.
162    ///
163    /// ```no_run
164    /// # use rusttype::*;
165    /// # let (scale, start) = (Scale::uniform(0.0), point(0.0, 0.0));
166    /// # let font: Font = unimplemented!();
167    /// font.layout("Hello World!", scale, start)
168    /// # ;
169    /// ```
170    ///
171    /// produces an iterator with behaviour equivalent to the following:
172    ///
173    /// ```no_run
174    /// # use rusttype::*;
175    /// # let (scale, start) = (Scale::uniform(0.0), point(0.0, 0.0));
176    /// # let font: Font = unimplemented!();
177    /// font.glyphs_for("Hello World!".chars())
178    ///     .scan((None, 0.0), |(last, x), g| {
179    ///         let g = g.scaled(scale);
180    ///         if let Some(last) = last {
181    ///             *x += font.pair_kerning(scale, *last, g.id());
182    ///         }
183    ///         let w = g.h_metrics().advance_width;
184    ///         let next = g.positioned(start + vector(*x, 0.0));
185    ///         *last = Some(next.id());
186    ///         *x += w;
187    ///         Some(next)
188    ///     })
189    /// # ;
190    /// ```
191    pub fn layout<'a, 's>(
192        &'a self,
193        s: &'s str,
194        scale: Scale,
195        start: Point<f32>,
196    ) -> LayoutIter<'a, 'font, 's> {
197        LayoutIter {
198            font: self,
199            chars: s.chars(),
200            caret: 0.0,
201            scale,
202            start,
203            last_glyph: None,
204        }
205    }
206
207    /// Returns additional kerning to apply as well as that given by HMetrics
208    /// for a particular pair of glyphs.
209    pub fn pair_kerning<A, B>(&self, scale: Scale, first: A, second: B) -> f32
210    where
211        A: IntoGlyphId,
212        B: IntoGlyphId,
213    {
214        let first_id = first.into_glyph_id(self).into();
215        let second_id = second.into_glyph_id(self).into();
216
217        let factor = {
218            let hscale = self.scale_for_pixel_height(scale.y);
219            hscale * (scale.x / scale.y)
220        };
221
222        let kern = if let Some(kern) = self.inner().tables().kern {
223            kern.subtables
224                .into_iter()
225                .filter(|st| st.horizontal && !st.variable)
226                .find_map(|st| st.glyphs_kerning(first_id, second_id))
227                .unwrap_or(0)
228        } else {
229            0
230        };
231
232        factor * f32::from(kern)
233    }
234
235    /// Computes a scale factor to produce a font whose "height" is 'pixels'
236    /// tall. Height is measured as the distance from the highest ascender
237    /// to the lowest descender; in other words, it's equivalent to calling
238    /// GetFontVMetrics and computing:
239    ///       scale = pixels / (ascent - descent)
240    /// so if you prefer to measure height by the ascent only, use a similar
241    /// calculation.
242    pub fn scale_for_pixel_height(&self, height: f32) -> f32 {
243        let inner = self.inner();
244        let fheight = f32::from(inner.ascender()) - f32::from(inner.descender());
245        height / fheight
246    }
247}