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}