rusttype/
lib.rs

1//! RustType is a pure Rust alternative to libraries like FreeType.
2//!
3//! The current capabilities of RustType:
4//!
5//! * Reading TrueType formatted fonts and font collections. This includes
6//!   `*.ttf` as well as a subset of `*.otf` font files.
7//! * Retrieving glyph shapes and commonly used properties for a font and its
8//!   glyphs.
9//! * Laying out glyphs horizontally using horizontal and vertical metrics, and
10//!   glyph-pair-specific kerning.
11//! * Rasterising glyphs with sub-pixel positioning using an accurate analytical
12//!   algorithm (not based on sampling).
13//! * Managing a font cache on the GPU with the `gpu_cache` module. This keeps
14//!   recently used glyph renderings in a dynamic cache in GPU memory to
15//!   minimise texture uploads per-frame. It also allows you keep the draw call
16//!   count for text very low, as all glyphs are kept in one GPU texture.
17//!
18//! Notable things that RustType does not support *yet*:
19//!
20//! * OpenType formatted fonts that are not just TrueType fonts (OpenType is a
21//!   superset of TrueType). Notably there is no support yet for cubic Bezier
22//!   curves used in glyphs.
23//! * Font hinting.
24//! * Ligatures of any kind.
25//! * Some less common TrueType sub-formats.
26//! * Right-to-left and vertical text layout.
27//!
28//! # Getting Started
29//!
30//! To hit the ground running with RustType, look at the `ascii.rs` example
31//! supplied with the crate. It demonstrates loading a font file, rasterising an
32//! arbitrary string, and displaying the result as ASCII art. If you prefer to
33//! just look at the documentation, the entry point for loading fonts is
34//! `Font`, from which you can access individual fonts, then their
35//! glyphs.
36//!
37//! # Glyphs
38//!
39//! The glyph API uses wrapper structs to augment a glyph with information such
40//! as scaling and positioning, making relevant methods that make use of this
41//! information available as appropriate. For example, given a `Glyph` `glyph`
42//! obtained directly from a `Font`:
43//!
44//! ```no_run
45//! # use rusttype::*;
46//! # let glyph: Glyph<'static> = unimplemented!();
47//! // One of the few things you can do with an unsized, positionless glyph is get its id.
48//! let id = glyph.id();
49//! let glyph = glyph.scaled(Scale::uniform(10.0));
50//! // Now glyph is a ScaledGlyph, you can do more with it, as well as what you can do with Glyph.
51//! // For example, you can access the correctly scaled horizontal metrics for the glyph.
52//! let h_metrics = glyph.h_metrics();
53//! let glyph = glyph.positioned(point(5.0, 3.0));
54//! // Now glyph is a PositionedGlyph, and you can do even more with it, e.g. drawing.
55//! glyph.draw(|x, y, v| {}); // In this case the pixel values are not used.
56//! ```
57//!
58//! # Unicode terminology
59//!
60//! This crate uses terminology for computerised typography as specified by the
61//! Unicode standard. If you are not sure of the differences between a code
62//! point, a character, and a glyph, you may want to check the [official Unicode
63//! glossary](http://unicode.org/glossary/), or alternatively, here's my take on
64//! it from a practical perspective:
65//!
66//! * A character is what you would conventionally call a single symbol,
67//!   independent of its appearance or representation in a particular font.
68//!   Examples include `a`, `A`, `ä`, `å`, `1`, `*`, `Ω`, etc.
69//! * A Unicode code point is the particular number that the Unicode standard
70//!   associates with a particular character. Note however that code points also
71//!   exist for things not conventionally thought of as characters by
72//!   themselves, but can be combined to form characters, such as diacritics
73//!   like accents. These "characters" are known in Unicode as "combining
74//!   characters". E.g., a diaeresis (`¨`) has the code point U+0308. If this
75//!   code point follows the code point U+0055 (the letter `u`), this sequence
76//!   represents the character `ü`. Note that there is also a single codepoint
77//!   for `ü`, U+00FC. This means that what visually looks like the same string
78//!   can have multiple different Unicode representations. Some fonts will have
79//!   glyphs (see below) for one sequence of codepoints, but not another that
80//!   has the same meaning. To deal with this problem it is recommended to use
81//!   Unicode normalisation, as provided by, for example, the
82//!   [unicode-normalization](http://crates.io/crates/unicode-normalization)
83//!   crate, to convert to code point sequences that work with the font in
84//!   question. Typically a font is more likely to support a single code point
85//!   vs. a sequence with the same meaning, so the best normalisation to use is
86//!   "canonical recomposition", known as NFC in the normalisation crate.
87//! * A glyph is a particular font's shape to draw the character for a
88//!   particular Unicode code point. This will have its own identifying number
89//!   unique to the font, its ID.
90#![allow(
91    clippy::cognitive_complexity,
92    clippy::doc_markdown,
93    clippy::cast_lossless,
94    clippy::many_single_char_names
95)]
96#![cfg_attr(not(feature = "std"), no_std)]
97
98extern crate alloc;
99
100mod font;
101mod geometry;
102mod outliner;
103
104#[cfg(all(feature = "libm-math", not(feature = "std")))]
105mod nostd_float;
106
107#[cfg(feature = "gpu_cache")]
108pub mod gpu_cache;
109
110pub use crate::geometry::{point, vector, Point, Rect, Vector};
111pub use font::*;
112
113use core::fmt;
114
115#[cfg(all(feature = "libm-math", not(feature = "std")))]
116use crate::nostd_float::FloatExt;
117
118pub use owned_ttf_parser::OutlineBuilder;
119
120#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
121pub struct GlyphId(pub u16);
122
123impl From<owned_ttf_parser::GlyphId> for GlyphId {
124    fn from(id: owned_ttf_parser::GlyphId) -> Self {
125        Self(id.0)
126    }
127}
128impl From<GlyphId> for owned_ttf_parser::GlyphId {
129    fn from(id: GlyphId) -> Self {
130        Self(id.0)
131    }
132}
133
134/// A single glyph of a font.
135///
136/// A `Glyph` does not have an inherent scale or position associated with it. To
137/// augment a glyph with a size, give it a scale using `scaled`. You can then
138/// position it using `positioned`.
139#[derive(Clone)]
140pub struct Glyph<'font> {
141    font: Font<'font>,
142    id: GlyphId,
143}
144
145impl<'font> Glyph<'font> {
146    /// The font to which this glyph belongs.
147    pub fn font(&self) -> &Font<'font> {
148        &self.font
149    }
150
151    /// The glyph identifier for this glyph.
152    pub fn id(&self) -> GlyphId {
153        self.id
154    }
155
156    /// Augments this glyph with scaling information, making methods that depend
157    /// on the scale of the glyph available.
158    pub fn scaled(self, scale: Scale) -> ScaledGlyph<'font> {
159        let scale_y = self.font.scale_for_pixel_height(scale.y);
160        let scale_x = scale_y * scale.x / scale.y;
161        ScaledGlyph {
162            g: self,
163            api_scale: scale,
164            scale: vector(scale_x, scale_y),
165        }
166    }
167}
168
169impl fmt::Debug for Glyph<'_> {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        f.debug_struct("Glyph").field("id", &self.id().0).finish()
172    }
173}
174
175/// The "horizontal metrics" of a glyph. This is useful for calculating the
176/// horizontal offset of a glyph from the previous one in a string when laying a
177/// string out horizontally.
178#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
179pub struct HMetrics {
180    /// The horizontal offset that the origin of the next glyph should be from
181    /// the origin of this glyph.
182    pub advance_width: f32,
183    /// The horizontal offset between the origin of this glyph and the leftmost
184    /// edge/point of the glyph.
185    pub left_side_bearing: f32,
186}
187
188/// The "vertical metrics" of a font at a particular scale. This is useful for
189/// calculating the amount of vertical space to give a line of text, and for
190/// computing the vertical offset between successive lines.
191#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
192pub struct VMetrics {
193    /// The highest point that any glyph in the font extends to above the
194    /// baseline. Typically positive.
195    pub ascent: f32,
196    /// The lowest point that any glyph in the font extends to below the
197    /// baseline. Typically negative.
198    pub descent: f32,
199    /// The gap to leave between the descent of one line and the ascent of the
200    /// next. This is of course only a guideline given by the font's designers.
201    pub line_gap: f32,
202}
203
204impl core::ops::Mul<f32> for VMetrics {
205    type Output = VMetrics;
206
207    fn mul(self, rhs: f32) -> Self {
208        Self {
209            ascent: self.ascent * rhs,
210            descent: self.descent * rhs,
211            line_gap: self.line_gap * rhs,
212        }
213    }
214}
215
216/// A glyph augmented with scaling information. You can query such a glyph for
217/// information that depends on the scale of the glyph.
218#[derive(Clone)]
219pub struct ScaledGlyph<'font> {
220    g: Glyph<'font>,
221    api_scale: Scale,
222    scale: Vector<f32>,
223}
224
225impl<'font> ScaledGlyph<'font> {
226    /// The glyph identifier for this glyph.
227    pub fn id(&self) -> GlyphId {
228        self.g.id()
229    }
230
231    /// The font to which this glyph belongs.
232    #[inline]
233    pub fn font(&self) -> &Font<'font> {
234        self.g.font()
235    }
236
237    /// A reference to this glyph without the scaling
238    pub fn into_unscaled(self) -> Glyph<'font> {
239        self.g
240    }
241
242    /// Removes the scaling from this glyph
243    pub fn unscaled(&self) -> &Glyph<'font> {
244        &self.g
245    }
246
247    /// Builds the outline of the glyph with the builder specified. Returns
248    /// `false` when the outline is either malformed or empty.
249    pub fn build_outline(&self, builder: &mut impl OutlineBuilder) -> bool {
250        let mut outliner =
251            crate::outliner::OutlineScaler::new(builder, vector(self.scale.x, -self.scale.y));
252
253        self.font()
254            .inner()
255            .outline_glyph(self.id().into(), &mut outliner)
256            .is_some()
257    }
258
259    /// Augments this glyph with positioning information, making methods that
260    /// depend on the position of the glyph available.
261    pub fn positioned(self, p: Point<f32>) -> PositionedGlyph<'font> {
262        let bb = self.pixel_bounds_at(p);
263        PositionedGlyph {
264            sg: self,
265            position: p,
266            bb,
267        }
268    }
269
270    pub fn scale(&self) -> Scale {
271        self.api_scale
272    }
273
274    /// Retrieves the "horizontal metrics" of this glyph. See `HMetrics` for
275    /// more detail.
276    pub fn h_metrics(&self) -> HMetrics {
277        let inner = self.font().inner();
278        let id = self.id().into();
279
280        let advance = inner.glyph_hor_advance(id).unwrap();
281        let left_side_bearing = inner.glyph_hor_side_bearing(id).unwrap();
282
283        HMetrics {
284            advance_width: advance as f32 * self.scale.x,
285            left_side_bearing: left_side_bearing as f32 * self.scale.x,
286        }
287    }
288
289    /// The bounding box of the shape of this glyph, not to be confused with
290    /// `pixel_bounding_box`, the conservative pixel-boundary bounding box. The
291    /// coordinates are relative to the glyph's origin.
292    pub fn exact_bounding_box(&self) -> Option<Rect<f32>> {
293        let owned_ttf_parser::Rect {
294            x_min,
295            y_min,
296            x_max,
297            y_max,
298        } = self.font().inner().glyph_bounding_box(self.id().into())?;
299
300        Some(Rect {
301            min: point(x_min as f32 * self.scale.x, -y_max as f32 * self.scale.y),
302            max: point(x_max as f32 * self.scale.x, -y_min as f32 * self.scale.y),
303        })
304    }
305
306    fn glyph_bitmap_box_subpixel(
307        &self,
308        font: &Font<'font>,
309        shift_x: f32,
310        shift_y: f32,
311    ) -> Option<Rect<i32>> {
312        let owned_ttf_parser::Rect {
313            x_min,
314            y_min,
315            x_max,
316            y_max,
317        } = font.inner().glyph_bounding_box(self.id().into())?;
318
319        Some(Rect {
320            min: point(
321                (x_min as f32 * self.scale.x + shift_x).floor() as i32,
322                (-y_max as f32 * self.scale.y + shift_y).floor() as i32,
323            ),
324            max: point(
325                (x_max as f32 * self.scale.x + shift_x).ceil() as i32,
326                (-y_min as f32 * self.scale.y + shift_y).ceil() as i32,
327            ),
328        })
329    }
330
331    #[inline]
332    fn pixel_bounds_at(&self, p: Point<f32>) -> Option<Rect<i32>> {
333        // Use subpixel fraction in floor/ceil rounding to eliminate rounding error
334        // from identical subpixel positions
335        let (x_trunc, x_fract) = (p.x.trunc() as i32, p.x.fract());
336        let (y_trunc, y_fract) = (p.y.trunc() as i32, p.y.fract());
337
338        let Rect { min, max } = self.glyph_bitmap_box_subpixel(self.font(), x_fract, y_fract)?;
339        Some(Rect {
340            min: point(x_trunc + min.x, y_trunc + min.y),
341            max: point(x_trunc + max.x, y_trunc + max.y),
342        })
343    }
344}
345
346impl fmt::Debug for ScaledGlyph<'_> {
347    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348        f.debug_struct("ScaledGlyph")
349            .field("id", &self.id().0)
350            .field("scale", &self.api_scale)
351            .finish()
352    }
353}
354
355/// A glyph augmented with positioning and scaling information. You can query
356/// such a glyph for information that depends on the scale and position of the
357/// glyph.
358#[derive(Clone)]
359pub struct PositionedGlyph<'font> {
360    sg: ScaledGlyph<'font>,
361    position: Point<f32>,
362    bb: Option<Rect<i32>>,
363}
364
365impl<'font> PositionedGlyph<'font> {
366    /// The glyph identifier for this glyph.
367    pub fn id(&self) -> GlyphId {
368        self.sg.id()
369    }
370
371    /// The font to which this glyph belongs.
372    #[inline]
373    pub fn font(&self) -> &Font<'font> {
374        self.sg.font()
375    }
376
377    /// A reference to this glyph without positioning
378    pub fn unpositioned(&self) -> &ScaledGlyph<'font> {
379        &self.sg
380    }
381
382    /// Removes the positioning from this glyph
383    pub fn into_unpositioned(self) -> ScaledGlyph<'font> {
384        self.sg
385    }
386
387    /// The conservative pixel-boundary bounding box for this glyph. This is the
388    /// smallest rectangle aligned to pixel boundaries that encloses the shape
389    /// of this glyph at this position. Note that the origin of the glyph, at
390    /// pixel-space coordinates (0, 0), is at the top left of the bounding box.
391    pub fn pixel_bounding_box(&self) -> Option<Rect<i32>> {
392        self.bb
393    }
394
395    pub fn scale(&self) -> Scale {
396        self.sg.api_scale
397    }
398
399    pub fn position(&self) -> Point<f32> {
400        self.position
401    }
402
403    /// Builds the outline of the glyph with the builder specified. Returns
404    /// `false` when the outline is either malformed or empty.
405    pub fn build_outline(&self, builder: &mut impl OutlineBuilder) -> bool {
406        let bb = if let Some(bb) = self.bb.as_ref() {
407            bb
408        } else {
409            return false;
410        };
411
412        let offset = vector(bb.min.x as f32, bb.min.y as f32);
413
414        let mut outliner = crate::outliner::OutlineTranslator::new(builder, self.position - offset);
415
416        self.sg.build_outline(&mut outliner)
417    }
418
419    /// Rasterises this glyph. For each pixel in the rect given by
420    /// `pixel_bounding_box()`, `o` is called:
421    ///
422    /// ```ignore
423    /// o(x, y, v)
424    /// ```
425    ///
426    /// where `x` and `y` are the coordinates of the pixel relative to the `min`
427    /// coordinates of the bounding box, and `v` is the analytically calculated
428    /// coverage of the pixel by the shape of the glyph. Calls to `o` proceed in
429    /// horizontal scanline order, similar to this pseudo-code:
430    ///
431    /// ```ignore
432    /// let bb = glyph.pixel_bounding_box();
433    /// for y in 0..bb.height() {
434    ///     for x in 0..bb.width() {
435    ///         o(x, y, calc_coverage(&glyph, x, y));
436    ///     }
437    /// }
438    /// ```
439    pub fn draw<O: FnMut(u32, u32, f32)>(&self, o: O) {
440        let bb = if let Some(bb) = self.bb.as_ref() {
441            bb
442        } else {
443            return;
444        };
445
446        let width = (bb.max.x - bb.min.x) as u32;
447        let height = (bb.max.y - bb.min.y) as u32;
448
449        let mut outliner = crate::outliner::OutlineRasterizer::new(width as _, height as _);
450
451        self.build_outline(&mut outliner);
452
453        outliner.rasterizer.for_each_pixel_2d(o);
454    }
455
456    /// Resets positioning information and recalculates the pixel bounding box
457    pub fn set_position(&mut self, p: Point<f32>) {
458        let p_diff = p - self.position;
459        if p_diff.x.fract().is_near_zero() && p_diff.y.fract().is_near_zero() {
460            if let Some(bb) = self.bb.as_mut() {
461                let rounded_diff = vector(p_diff.x.round() as i32, p_diff.y.round() as i32);
462                bb.min = bb.min + rounded_diff;
463                bb.max = bb.max + rounded_diff;
464            }
465        } else {
466            self.bb = self.sg.pixel_bounds_at(p);
467        }
468        self.position = p;
469    }
470}
471
472impl fmt::Debug for PositionedGlyph<'_> {
473    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474        f.debug_struct("PositionedGlyph")
475            .field("id", &self.id().0)
476            .field("scale", &self.scale())
477            .field("position", &self.position)
478            .finish()
479    }
480}
481
482/// Defines the size of a rendered face of a font, in pixels, horizontally and
483/// vertically. A vertical scale of `y` pixels means that the distance between
484/// the ascent and descent lines (see `VMetrics`) of the face will be `y`
485/// pixels. If `x` and `y` are equal the scaling is uniform. Non-uniform scaling
486/// by a factor *f* in the horizontal direction is achieved by setting `x` equal
487/// to *f* times `y`.
488#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
489pub struct Scale {
490    /// Horizontal scale, in pixels.
491    pub x: f32,
492    /// Vertical scale, in pixels.
493    pub y: f32,
494}
495
496impl Scale {
497    /// Uniform scaling, equivalent to `Scale { x: s, y: s }`.
498    #[inline]
499    pub fn uniform(s: f32) -> Scale {
500        Scale { x: s, y: s }
501    }
502}
503/// A trait for types that can be converted into a `GlyphId`, in the context of
504/// a specific font.
505///
506/// Many `rusttype` functions that operate on characters accept values of any
507/// type that implements `IntoGlyphId`. Such types include `char`, `Codepoint`,
508/// and obviously `GlyphId` itself.
509pub trait IntoGlyphId {
510    /// Convert `self` into a `GlyphId`, consulting the index map of `font` if
511    /// necessary.
512    fn into_glyph_id(self, font: &Font<'_>) -> GlyphId;
513}
514impl IntoGlyphId for char {
515    #[inline]
516    fn into_glyph_id(self, font: &Font<'_>) -> GlyphId {
517        font.inner()
518            .glyph_index(self)
519            .unwrap_or(owned_ttf_parser::GlyphId(0))
520            .into()
521    }
522}
523impl<G: Into<GlyphId>> IntoGlyphId for G {
524    #[inline]
525    fn into_glyph_id(self, _font: &Font<'_>) -> GlyphId {
526        self.into()
527    }
528}
529
530#[derive(Clone)]
531pub struct GlyphIter<'a, 'font, I: Iterator>
532where
533    I::Item: IntoGlyphId,
534{
535    font: &'a Font<'font>,
536    itr: I,
537}
538
539impl<'a, 'font, I> Iterator for GlyphIter<'a, 'font, I>
540where
541    I: Iterator,
542    I::Item: IntoGlyphId,
543{
544    type Item = Glyph<'font>;
545
546    fn next(&mut self) -> Option<Glyph<'font>> {
547        self.itr.next().map(|c| self.font.glyph(c))
548    }
549}
550
551#[derive(Clone)]
552pub struct LayoutIter<'a, 'font, 's> {
553    font: &'a Font<'font>,
554    chars: core::str::Chars<'s>,
555    caret: f32,
556    scale: Scale,
557    start: Point<f32>,
558    last_glyph: Option<GlyphId>,
559}
560
561impl<'a, 'font, 's> Iterator for LayoutIter<'a, 'font, 's> {
562    type Item = PositionedGlyph<'font>;
563
564    fn next(&mut self) -> Option<PositionedGlyph<'font>> {
565        self.chars.next().map(|c| {
566            let g = self.font.glyph(c).scaled(self.scale);
567            if let Some(last) = self.last_glyph {
568                self.caret += self.font.pair_kerning(self.scale, last, g.id());
569            }
570            let g = g.positioned(point(self.start.x + self.caret, self.start.y));
571            self.caret += g.sg.h_metrics().advance_width;
572            self.last_glyph = Some(g.id());
573            g
574        })
575    }
576}
577
578pub(crate) trait NearZero {
579    /// Returns if this number is kinda pretty much zero.
580    fn is_near_zero(&self) -> bool;
581}
582impl NearZero for f32 {
583    #[inline]
584    fn is_near_zero(&self) -> bool {
585        self.abs() <= core::f32::EPSILON
586    }
587}