blob: a7cff9362205f57262fc176a17be4d519942d946 [file] [log] [blame]
//! RustType is a pure Rust alternative to libraries like FreeType.
//!
//! The current capabilities of RustType:
//!
//! * Reading TrueType formatted fonts and font collections. This includes
//! `*.ttf` as well as a subset of `*.otf` font files.
//! * Retrieving glyph shapes and commonly used properties for a font and its
//! glyphs.
//! * Laying out glyphs horizontally using horizontal and vertical metrics, and
//! glyph-pair-specific kerning.
//! * Rasterising glyphs with sub-pixel positioning using an accurate analytical
//! algorithm (not based on sampling).
//! * Managing a font cache on the GPU with the `gpu_cache` module. This keeps
//! recently used glyph renderings in a dynamic cache in GPU memory to
//! minimise texture uploads per-frame. It also allows you keep the draw call
//! count for text very low, as all glyphs are kept in one GPU texture.
//!
//! Notable things that RustType does not support *yet*:
//!
//! * OpenType formatted fonts that are not just TrueType fonts (OpenType is a
//! superset of TrueType). Notably there is no support yet for cubic Bezier
//! curves used in glyphs.
//! * Font hinting.
//! * Ligatures of any kind.
//! * Some less common TrueType sub-formats.
//! * Right-to-left and vertical text layout.
//!
//! # Getting Started
//!
//! To hit the ground running with RustType, look at the `simple.rs` example
//! supplied with the crate. It demonstrates loading a font file, rasterising an
//! arbitrary string, and displaying the result as ASCII art. If you prefer to
//! just look at the documentation, the entry point for loading fonts is
//! `FontCollection`, from which you can access individual fonts, then their
//! glyphs.
//!
//! # Glyphs
//!
//! The glyph API uses wrapper structs to augment a glyph with information such
//! as scaling and positioning, making relevant methods that make use of this
//! information available as appropriate. For example, given a `Glyph` `glyph`
//! obtained directly from a `Font`:
//!
//! ```no_run
//! # use rusttype::*;
//! # let glyph: Glyph<'static> = unimplemented!();
//! // One of the few things you can do with an unsized, positionless glyph is get its id.
//! let id = glyph.id();
//! let glyph = glyph.scaled(Scale::uniform(10.0));
//! // Now glyph is a ScaledGlyph, you can do more with it, as well as what you can do with Glyph.
//! // For example, you can access the correctly scaled horizontal metrics for the glyph.
//! let h_metrics = glyph.h_metrics();
//! let glyph = glyph.positioned(point(5.0, 3.0));
//! // Now glyph is a PositionedGlyph, and you can do even more with it, e.g. drawing.
//! glyph.draw(|x, y, v| {}); // In this case the pixel values are not used.
//! ```
//!
//! # Unicode terminology
//!
//! This crate uses terminology for computerised typography as specified by the
//! Unicode standard. If you are not sure of the differences between a code
//! point, a character, and a glyph, you may want to check the [official Unicode
//! glossary](http://unicode.org/glossary/), or alternatively, here's my take on
//! it from a practical perspective:
//!
//! * A character is what you would conventionally call a single symbol,
//! independent of its appearance or representation in a particular font.
//! Examples include `a`, `A`, `ä`, `å`, `1`, `*`, `Ω`, etc.
//! * A Unicode code point is the particular number that the Unicode standard
//! associates with a particular character. Note however that code points also
//! exist for things not conventionally thought of as characters by
//! themselves, but can be combined to form characters, such as diacritics
//! like accents. These "characters" are known in Unicode as "combining
//! characters". E.g., a diaeresis (`¨`) has the code point U+0308. If this
//! code point follows the code point U+0055 (the letter `u`), this sequence
//! represents the character `ü`. Note that there is also a single codepoint
//! for `ü`, U+00FC. This means that what visually looks like the same string
//! can have multiple different Unicode representations. Some fonts will have
//! glyphs (see below) for one sequence of codepoints, but not another that
//! has the same meaning. To deal with this problem it is recommended to use
//! Unicode normalisation, as provided by, for example, the
//! [unicode-normalization](http://crates.io/crates/unicode-normalization)
//! crate, to convert to code point sequences that work with the font in
//! question. Typically a font is more likely to support a single code point
//! vs. a sequence with the same meaning, so the best normalisation to use is
//! "canonical recomposition", known as NFC in the normalisation crate.
//! * A glyph is a particular font's shape to draw the character for a
//! particular Unicode code point. This will have its own identifying number
//! unique to the font, its ID.
#![allow(unknown_lints)]
#![warn(clippy::all)]
#![allow(
clippy::cyclomatic_complexity,
clippy::doc_markdown,
clippy::cast_lossless,
clippy::many_single_char_names
)]
#![cfg_attr(feature = "bench", feature(test))]
#[cfg(feature = "bench")]
extern crate test;
mod geometry;
mod rasterizer;
#[cfg(feature = "gpu_cache")]
pub mod gpu_cache;
pub use crate::geometry::{point, vector, Curve, Line, Point, Rect, Vector};
use approx::relative_eq;
use stb_truetype as tt;
use std::fmt;
use std::sync::Arc;
/// A collection of fonts read straight from a font file's data. The data in the
/// collection is not validated. This structure may or may not own the font
/// data.
///
/// # Lifetime
/// The lifetime reflects the font data lifetime. `FontCollection<'static>`
/// covers most cases ie both dynamically loaded owned data and for referenced
/// compile time font data.
#[derive(Clone, Debug)]
pub struct FontCollection<'a>(SharedBytes<'a>);
/// A single font. This may or may not own the font data.
///
/// # Lifetime
/// The lifetime reflects the font data lifetime. `Font<'static>` covers most
/// cases ie both dynamically loaded owned data and for referenced compile time
/// font data.
///
/// # Example
///
/// ```
/// # use rusttype::{Font, Error};
/// # fn example() -> Result<(), Error> {
/// let font_data: &[u8] = include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf");
/// let font: Font<'static> = Font::from_bytes(font_data)?;
///
/// let owned_font_data: Vec<u8> = font_data.to_vec();
/// let from_owned_font: Font<'static> = Font::from_bytes(owned_font_data)?;
/// # Ok(())
/// # }
/// ```
#[derive(Clone)]
pub struct Font<'a> {
info: tt::FontInfo<SharedBytes<'a>>,
}
impl fmt::Debug for Font<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Font")
}
}
/// `SharedBytes` handles the lifetime of font data used in RustType. The data
/// is either a shared reference to externally owned data, or managed by
/// reference counting. `SharedBytes` can be conveniently used with `From` and
/// `Into`, and dereferences to the contained bytes.
///
/// # Lifetime
/// The lifetime reflects the font data lifetime. `SharedBytes<'static>` covers
/// most cases ie both dynamically loaded owned data and for referenced compile
/// time font data.
#[derive(Clone, Debug)]
pub enum SharedBytes<'a> {
ByRef(&'a [u8]),
ByArc(Arc<[u8]>),
}
impl<'a> ::std::ops::Deref for SharedBytes<'a> {
type Target = [u8];
fn deref(&self) -> &[u8] {
match *self {
SharedBytes::ByRef(bytes) => bytes,
SharedBytes::ByArc(ref bytes) => &**bytes,
}
}
}
/// ```
/// # use rusttype::SharedBytes;
/// let bytes: &[u8] = &[0u8, 1, 2, 3];
/// let shared: SharedBytes = bytes.into();
/// assert_eq!(&*shared, bytes);
/// ```
impl<'a> From<&'a [u8]> for SharedBytes<'a> {
fn from(bytes: &'a [u8]) -> SharedBytes<'a> {
SharedBytes::ByRef(bytes)
}
}
/// ```
/// # use rusttype::SharedBytes;
/// # use std::sync::Arc;
/// let bytes: Arc<[u8]> = vec![0u8, 1, 2, 3].into();
/// let shared: SharedBytes = Arc::clone(&bytes).into();
/// assert_eq!(&*shared, &*bytes);
/// ```
impl From<Arc<[u8]>> for SharedBytes<'static> {
fn from(bytes: Arc<[u8]>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes)
}
}
/// ```
/// # use rusttype::SharedBytes;
/// let bytes: Box<[u8]> = vec![0u8, 1, 2, 3].into();
/// let shared: SharedBytes = bytes.into();
/// assert_eq!(&*shared, &[0u8, 1, 2, 3]);
/// ```
impl From<Box<[u8]>> for SharedBytes<'static> {
fn from(bytes: Box<[u8]>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes.into())
}
}
/// ```
/// # use rusttype::SharedBytes;
/// let bytes = vec![0u8, 1, 2, 3];
/// let shared: SharedBytes = bytes.into();
/// assert_eq!(&*shared, &[0u8, 1, 2, 3]);
/// ```
impl From<Vec<u8>> for SharedBytes<'static> {
fn from(bytes: Vec<u8>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes.into())
}
}
/// ```
/// # use rusttype::SharedBytes;
/// let bytes = vec![0u8, 1, 2, 3];
/// let shared: SharedBytes = (&bytes).into();
/// assert_eq!(&*shared, &bytes as &[u8]);
/// ```
impl<'a, T: AsRef<[u8]>> From<&'a T> for SharedBytes<'a> {
fn from(bytes: &'a T) -> SharedBytes<'a> {
SharedBytes::ByRef(bytes.as_ref())
}
}
#[test]
fn lazy_static_shared_bytes() {
lazy_static::lazy_static! {
static ref BYTES: Vec<u8> = vec![0, 1, 2, 3];
}
let shared_bytes: SharedBytes<'static> = (&*BYTES).into();
assert_eq!(&*shared_bytes, &[0, 1, 2, 3]);
}
/// Represents a Unicode code point.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Codepoint(pub u32);
/// Represents a glyph identifier for a particular font. This identifier will
/// not necessarily correspond to the correct glyph in a font other than the
/// one that it was obtained from.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GlyphId(pub u32);
/// A single glyph of a font. this may either be a thin wrapper referring to the
/// font and the glyph id, or it may be a standalone glyph that owns the data
/// needed by it.
///
/// A `Glyph` does not have an inherent scale or position associated with it. To
/// augment a glyph with a size, give it a scale using `scaled`. You can then
/// position it using `positioned`.
#[derive(Clone)]
pub struct Glyph<'a> {
inner: GlyphInner<'a>,
}
impl fmt::Debug for Glyph<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Glyph").field("id", &self.id().0).finish()
}
}
#[derive(Clone)]
enum GlyphInner<'a> {
Proxy(Font<'a>, u32),
Shared(Arc<SharedGlyphData>),
}
#[derive(Debug)]
pub struct SharedGlyphData {
pub id: u32,
pub extents: Option<Rect<i32>>,
pub scale_for_1_pixel: f32,
pub unit_h_metrics: HMetrics,
pub shape: Option<Vec<tt::Vertex>>,
}
/// The "horizontal metrics" of a glyph. This is useful for calculating the
/// horizontal offset of a glyph from the previous one in a string when laying a
/// string out horizontally.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct HMetrics {
/// The horizontal offset that the origin of the next glyph should be from
/// the origin of this glyph.
pub advance_width: f32,
/// The horizontal offset between the origin of this glyph and the leftmost
/// edge/point of the glyph.
pub left_side_bearing: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
/// The "vertical metrics" of a font at a particular scale. This is useful for
/// calculating the amount of vertical space to give a line of text, and for
/// computing the vertical offset between successive lines.
pub struct VMetrics {
/// The highest point that any glyph in the font extends to above the
/// baseline. Typically positive.
pub ascent: f32,
/// The lowest point that any glyph in the font extends to below the
/// baseline. Typically negative.
pub descent: f32,
/// The gap to leave between the descent of one line and the ascent of the
/// next. This is of course only a guideline given by the font's designers.
pub line_gap: f32,
}
impl From<tt::VMetrics> for VMetrics {
fn from(vm: tt::VMetrics) -> Self {
Self {
ascent: vm.ascent as f32,
descent: vm.descent as f32,
line_gap: vm.line_gap as f32,
}
}
}
impl ::std::ops::Mul<f32> for VMetrics {
type Output = VMetrics;
fn mul(self, rhs: f32) -> Self {
Self {
ascent: self.ascent * rhs,
descent: self.descent * rhs,
line_gap: self.line_gap * rhs,
}
}
}
/// A glyph augmented with scaling information. You can query such a glyph for
/// information that depends on the scale of the glyph.
#[derive(Clone)]
pub struct ScaledGlyph<'a> {
g: Glyph<'a>,
api_scale: Scale,
scale: Vector<f32>,
}
impl fmt::Debug for ScaledGlyph<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ScaledGlyph")
.field("id", &self.id().0)
.field("scale", &self.api_scale)
.finish()
}
}
/// A glyph augmented with positioning and scaling information. You can query
/// such a glyph for information that depends on the scale and position of the
/// glyph.
#[derive(Clone)]
pub struct PositionedGlyph<'a> {
sg: ScaledGlyph<'a>,
position: Point<f32>,
bb: Option<Rect<i32>>,
}
impl fmt::Debug for PositionedGlyph<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PositionedGlyph")
.field("id", &self.id().0)
.field("scale", &self.scale())
.field("position", &self.position)
.finish()
}
}
/// Defines the size of a rendered face of a font, in pixels, horizontally and
/// vertically. A vertical scale of `y` pixels means that the distance betwen
/// the ascent and descent lines (see `VMetrics`) of the face will be `y`
/// pixels. If `x` and `y` are equal the scaling is uniform. Non-uniform scaling
/// by a factor *f* in the horizontal direction is achieved by setting `x` equal
/// to *f* times `y`.
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Scale {
/// Horizontal scale, in pixels.
pub x: f32,
/// Vertical scale, in pixels.
pub y: f32,
}
impl Scale {
/// Uniform scaling, equivalent to `Scale { x: s, y: s }`.
#[inline]
pub fn uniform(s: f32) -> Scale {
Scale { x: s, y: s }
}
}
/// A trait for types that can be converted into a `GlyphId`, in the context of
/// a specific font.
///
/// Many `rusttype` functions that operate on characters accept values of any
/// type that implements `IntoGlyphId`. Such types include `char`, `Codepoint`,
/// and obviously `GlyphId` itself.
pub trait IntoGlyphId {
/// Convert `self` into a `GlyphId`, consulting the index map of `font` if
/// necessary.
fn into_glyph_id(self, _: &Font<'_>) -> GlyphId;
}
impl IntoGlyphId for char {
fn into_glyph_id(self, font: &Font<'_>) -> GlyphId {
GlyphId(font.info.find_glyph_index(self as u32))
}
}
impl IntoGlyphId for Codepoint {
fn into_glyph_id(self, font: &Font<'_>) -> GlyphId {
GlyphId(font.info.find_glyph_index(self.0))
}
}
impl IntoGlyphId for GlyphId {
#[inline]
fn into_glyph_id(self, _font: &Font<'_>) -> GlyphId {
self
}
}
impl<'a> FontCollection<'a> {
/// Constructs a font collection from an array of bytes, typically loaded
/// from a font file, which may be a single font or a TrueType Collection
/// holding a number of fonts. This array may be owned (e.g. `Vec<u8>`), or
/// borrowed (`&[u8]`). As long as `From<T>` is implemented for `Bytes` for
/// some type `T`, `T` can be used as input.
///
/// This returns an error if `bytes` does not seem to be font data in a
/// format we recognize.
pub fn from_bytes<B: Into<SharedBytes<'a>>>(bytes: B) -> Result<FontCollection<'a>, Error> {
let bytes = bytes.into();
// We should use tt::is_collection once it lands in stb_truetype-rs:
// https://github.com/redox-os/stb_truetype-rs/pull/15
if !tt::is_font(&bytes) && &bytes[0..4] != b"ttcf" {
return Err(Error::UnrecognizedFormat);
}
Ok(FontCollection(bytes))
}
/// If this `FontCollection` holds a single font, or a TrueType Collection
/// containing only one font, return that as a `Font`. The `FontCollection`
/// is consumed.
///
/// If this `FontCollection` holds multiple fonts, return a
/// `CollectionContainsMultipleFonts` error.
///
/// If an error occurs, the `FontCollection` is lost, since this function
/// takes ownership of it, and the error values don't give it back. If that
/// is a problem, use the `font_at` or `into_fonts` methods instead, which
/// borrow the `FontCollection` rather than taking ownership of it.
pub fn into_font(self) -> Result<Font<'a>, Error> {
let offset = if tt::is_font(&self.0) {
0
} else if tt::get_font_offset_for_index(&self.0, 1).is_some() {
return Err(Error::CollectionContainsMultipleFonts);
} else {
// We now know that either a) `self.0` is a collection with only one
// font, or b) `get_font_offset_for_index` found data it couldn't
// recognize. Request the first font's offset, distinguishing
// those two cases.
match tt::get_font_offset_for_index(&self.0, 0) {
None => return Err(Error::IllFormed),
Some(offset) => offset,
}
};
let info = tt::FontInfo::new(self.0, offset as usize).ok_or(Error::IllFormed)?;
Ok(Font { info })
}
/// Gets the font at index `i` in the font collection, if it exists and is
/// valid. The produced font borrows the font data that is either borrowed
/// or owned by this font collection.
pub fn font_at(&self, i: usize) -> Result<Font<'a>, Error> {
let offset = tt::get_font_offset_for_index(&self.0, i as i32)
.ok_or(Error::CollectionIndexOutOfBounds)?;
let info = tt::FontInfo::new(self.0.clone(), offset as usize).ok_or(Error::IllFormed)?;
Ok(Font { info })
}
/// Converts `self` into an `Iterator` yielding each `Font` that exists
/// within the collection.
pub fn into_fonts(self) -> IntoFontsIter<'a> {
IntoFontsIter {
collection: self,
next_index: 0,
}
}
}
pub struct IntoFontsIter<'a> {
next_index: usize,
collection: FontCollection<'a>,
}
impl<'a> Iterator for IntoFontsIter<'a> {
type Item = Result<Font<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
let result = self.collection.font_at(self.next_index);
if let Err(Error::CollectionIndexOutOfBounds) = result {
return None;
}
self.next_index += 1;
Some(result)
}
}
impl<'a> Font<'a> {
/// Constructs a font from an array of bytes, this is a shortcut for
/// `FontCollection::from_bytes` for collections comprised of a single font.
pub fn from_bytes<B: Into<SharedBytes<'a>>>(bytes: B) -> Result<Font<'a>, Error> {
FontCollection::from_bytes(bytes).and_then(|c| c.into_font())
}
/// The "vertical metrics" for this font at a given scale. These metrics are
/// shared by all of the glyphs in the font. See `VMetrics` for more detail.
pub fn v_metrics(&self, scale: Scale) -> VMetrics {
let vm = self.info.get_v_metrics();
let scale = self.info.scale_for_pixel_height(scale.y);
VMetrics::from(vm) * scale
}
/// Get the unscaled VMetrics for this font, shared by all glyphs.
/// See `VMetrics` for more detail.
pub fn v_metrics_unscaled(&self) -> VMetrics {
VMetrics::from(self.info.get_v_metrics())
}
/// Returns the units per EM square of this font
pub fn units_per_em(&self) -> u16 {
self.info.units_per_em()
}
/// The number of glyphs present in this font. Glyph identifiers for this
/// font will always be in the range `0..self.glyph_count()`
pub fn glyph_count(&self) -> usize {
self.info.get_num_glyphs() as usize
}
/// Returns the corresponding glyph for a Unicode code point or a glyph id
/// for this font.
///
/// If `id` is a `GlyphId`, it must be valid for this font; otherwise, this
/// function panics. `GlyphId`s should always be produced by looking up some
/// other sort of designator (like a Unicode code point) in a font, and
/// should only be used to index the font they were produced for.
///
/// Note that code points without corresponding glyphs in this font map to
/// the ".notdef" glyph, glyph 0.
pub fn glyph<C: IntoGlyphId>(&self, id: C) -> Glyph<'a> {
let gid = id.into_glyph_id(self);
assert!((gid.0 as usize) < self.glyph_count());
// font clone either a reference clone, or arc clone
Glyph::new(GlyphInner::Proxy(self.clone(), gid.0))
}
/// A convenience function.
///
/// Returns an iterator that produces the glyphs corresponding to the code
/// points or glyph ids produced by the given iterator `itr`.
///
/// This is equivalent in behaviour to `itr.map(|c| font.glyph(c))`.
pub fn glyphs_for<I: Iterator>(&self, itr: I) -> GlyphIter<'_, I>
where
I::Item: IntoGlyphId,
{
GlyphIter { font: self, itr }
}
/// Returns an iterator over the names for this font.
pub fn font_name_strings(&self) -> tt::FontNameIter<'_, SharedBytes<'a>> {
self.info.get_font_name_strings()
}
/// A convenience function for laying out glyphs for a string horizontally.
/// It does not take control characters like line breaks into account, as
/// treatment of these is likely to depend on the application.
///
/// Note that this function does not perform Unicode normalisation.
/// Composite characters (such as ö constructed from two code points, ¨ and
/// o), will not be normalised to single code points. So if a font does not
/// contain a glyph for each separate code point, but does contain one for
/// the normalised single code point (which is common), the desired glyph
/// will not be produced, despite being present in the font. Deal with this
/// by performing Unicode normalisation on the input string before passing
/// it to `layout`. The crate
/// [unicode-normalization](http://crates.io/crates/unicode-normalization)
/// is perfect for this purpose.
///
/// Calling this function is equivalent to a longer sequence of operations
/// involving `glyphs_for`, e.g.
///
/// ```no_run
/// # use rusttype::*;
/// # let (scale, start) = (Scale::uniform(0.0), point(0.0, 0.0));
/// # let font: Font = unimplemented!();
/// font.layout("Hello World!", scale, start)
/// # ;
/// ```
///
/// produces an iterator with behaviour equivalent to the following:
///
/// ```no_run
/// # use rusttype::*;
/// # let (scale, start) = (Scale::uniform(0.0), point(0.0, 0.0));
/// # let font: Font = unimplemented!();
/// font.glyphs_for("Hello World!".chars())
/// .scan((None, 0.0), |&mut (mut last, mut x), g| {
/// let g = g.scaled(scale);
/// if let Some(last) = last {
/// x += font.pair_kerning(scale, last, g.id());
/// }
/// let w = g.h_metrics().advance_width;
/// let next = g.positioned(start + vector(x, 0.0));
/// last = Some(next.id());
/// x += w;
/// Some(next)
/// })
/// # ;
/// ```
pub fn layout<'b>(&self, s: &'b str, scale: Scale, start: Point<f32>) -> LayoutIter<'_, 'b> {
LayoutIter {
font: self,
chars: s.chars(),
caret: 0.0,
scale,
start,
last_glyph: None,
}
}
/// Returns additional kerning to apply as well as that given by HMetrics
/// for a particular pair of glyphs.
pub fn pair_kerning<A, B>(&self, scale: Scale, first: A, second: B) -> f32
where
A: IntoGlyphId,
B: IntoGlyphId,
{
let first_id = first.into_glyph_id(self);
let second_id = second.into_glyph_id(self);
let factor = self.info.scale_for_pixel_height(scale.y) * (scale.x / scale.y);
let kern = self.info.get_glyph_kern_advance(first_id.0, second_id.0);
factor * kern as f32
}
}
#[derive(Clone)]
pub struct GlyphIter<'a, I: Iterator>
where
I::Item: IntoGlyphId,
{
font: &'a Font<'a>,
itr: I,
}
impl<'a, I: Iterator> Iterator for GlyphIter<'a, I>
where
I::Item: IntoGlyphId,
{
type Item = Glyph<'a>;
fn next(&mut self) -> Option<Glyph<'a>> {
self.itr.next().map(|c| self.font.glyph(c))
}
}
#[derive(Clone)]
pub struct LayoutIter<'a, 'b> {
font: &'a Font<'a>,
chars: ::std::str::Chars<'b>,
caret: f32,
scale: Scale,
start: Point<f32>,
last_glyph: Option<GlyphId>,
}
impl<'a, 'b> Iterator for LayoutIter<'a, 'b> {
type Item = PositionedGlyph<'a>;
fn next(&mut self) -> Option<PositionedGlyph<'a>> {
self.chars.next().map(|c| {
let g = self.font.glyph(c).scaled(self.scale);
if let Some(last) = self.last_glyph {
self.caret += self.font.pair_kerning(self.scale, last, g.id());
}
let g = g.positioned(point(self.start.x + self.caret, self.start.y));
self.caret += g.sg.h_metrics().advance_width;
self.last_glyph = Some(g.id());
g
})
}
}
impl<'a> Glyph<'a> {
fn new(inner: GlyphInner<'a>) -> Glyph<'a> {
Glyph { inner }
}
/// The font to which this glyph belongs. If the glyph is a standalone glyph
/// that owns its resources, it no longer has a reference to the font which
/// it was created from (using `standalone()`). In which case, `None` is
/// returned.
pub fn font(&self) -> Option<&Font<'a>> {
match self.inner {
GlyphInner::Proxy(ref f, _) => Some(f),
GlyphInner::Shared(_) => None,
}
}
/// The glyph identifier for this glyph.
pub fn id(&self) -> GlyphId {
match self.inner {
GlyphInner::Proxy(_, id) => GlyphId(id),
GlyphInner::Shared(ref data) => GlyphId(data.id),
}
}
/// Augments this glyph with scaling information, making methods that depend
/// on the scale of the glyph available.
pub fn scaled(self, scale: Scale) -> ScaledGlyph<'a> {
let (scale_x, scale_y) = match self.inner {
GlyphInner::Proxy(ref font, _) => {
let scale_y = font.info.scale_for_pixel_height(scale.y);
let scale_x = scale_y * scale.x / scale.y;
(scale_x, scale_y)
}
GlyphInner::Shared(ref data) => {
let scale_y = data.scale_for_1_pixel * scale.y;
let scale_x = scale_y * scale.x / scale.y;
(scale_x, scale_y)
}
};
ScaledGlyph {
g: self,
api_scale: scale,
scale: vector(scale_x, scale_y),
}
}
/// Turns a `Glyph<'a>` into a `Glyph<'static>`. This produces a glyph that
/// owns its resources, extracted from the font. This glyph can outlive the
/// font that it comes from.
///
/// Calling `standalone()` on a standalone glyph shares the resources, and
/// is equivalent to `clone()`.
pub fn standalone(&self) -> Glyph<'static> {
match self.inner {
GlyphInner::Proxy(ref font, id) => {
Glyph::new(GlyphInner::Shared(Arc::new(SharedGlyphData {
id,
scale_for_1_pixel: font.info.scale_for_pixel_height(1.0),
unit_h_metrics: {
let hm = font.info.get_glyph_h_metrics(id);
HMetrics {
advance_width: hm.advance_width as f32,
left_side_bearing: hm.left_side_bearing as f32,
}
},
extents: font.info.get_glyph_box(id).map(|bb| Rect {
min: point(bb.x0 as i32, -(bb.y1 as i32)),
max: point(bb.x1 as i32, -(bb.y0 as i32)),
}),
shape: font.info.get_glyph_shape(id),
})))
}
GlyphInner::Shared(ref data) => Glyph::new(GlyphInner::Shared(data.clone())),
}
}
/// Get the data from this glyph (such as width, extents, vertices, etc.).
/// Only possible if the glyph is a shared glyph.
pub fn get_data(&self) -> Option<Arc<SharedGlyphData>> {
match self.inner {
GlyphInner::Proxy(..) => None,
GlyphInner::Shared(ref s) => Some(s.clone()),
}
}
}
/// Part of a `Contour`, either a `Line` or a `Curve`.
#[derive(Copy, Clone, Debug)]
pub enum Segment {
Line(Line),
Curve(Curve),
}
/// A closed loop consisting of a sequence of `Segment`s.
#[derive(Clone, Debug)]
pub struct Contour {
pub segments: Vec<Segment>,
}
impl<'a> ScaledGlyph<'a> {
/// The glyph identifier for this glyph.
pub fn id(&self) -> GlyphId {
self.g.id()
}
/// The font to which this glyph belongs. If the glyph is a standalone glyph
/// that owns its resources, it no longer has a reference to the font which
/// it was created from (using `standalone()`). In which case, `None` is
/// returned.
pub fn font(&self) -> Option<&Font<'a>> {
self.g.font()
}
/// A reference to this glyph without the scaling
pub fn into_unscaled(self) -> Glyph<'a> {
self.g
}
/// Removes the scaling from this glyph
pub fn unscaled(&self) -> &Glyph<'a> {
&self.g
}
/// Augments this glyph with positioning information, making methods that
/// depend on the position of the glyph available.
pub fn positioned(self, p: Point<f32>) -> PositionedGlyph<'a> {
let bb = self.pixel_bounds_at(p);
PositionedGlyph {
sg: self,
position: p,
bb,
}
}
pub fn scale(&self) -> Scale {
self.api_scale
}
/// Retrieves the "horizontal metrics" of this glyph. See `HMetrics` for
/// more detail.
pub fn h_metrics(&self) -> HMetrics {
match self.g.inner {
GlyphInner::Proxy(ref font, id) => {
let hm = font.info.get_glyph_h_metrics(id);
HMetrics {
advance_width: hm.advance_width as f32 * self.scale.x,
left_side_bearing: hm.left_side_bearing as f32 * self.scale.x,
}
}
GlyphInner::Shared(ref data) => HMetrics {
advance_width: data.unit_h_metrics.advance_width * self.scale.x,
left_side_bearing: data.unit_h_metrics.left_side_bearing * self.scale.y,
},
}
}
fn shape_with_offset(&self, offset: Point<f32>) -> Option<Vec<Contour>> {
use stb_truetype::VertexType;
use std::mem::replace;
match self.g.inner {
GlyphInner::Proxy(ref font, id) => font.info.get_glyph_shape(id),
GlyphInner::Shared(ref data) => data.shape.clone(),
}
.map(|shape| {
let mut result = Vec::new();
let mut current = Vec::new();
let mut last = point(0.0, 0.0);
for v in shape {
let end = point(
v.x as f32 * self.scale.x + offset.x,
v.y as f32 * self.scale.y + offset.y,
);
match v.vertex_type() {
VertexType::MoveTo if !current.is_empty() => result.push(Contour {
segments: replace(&mut current, Vec::new()),
}),
VertexType::LineTo => current.push(Segment::Line(Line { p: [last, end] })),
VertexType::CurveTo => {
let control = point(
v.cx as f32 * self.scale.x + offset.x,
v.cy as f32 * self.scale.y + offset.y,
);
current.push(Segment::Curve(Curve {
p: [last, control, end],
}))
}
_ => (),
}
last = end;
}
if !current.is_empty() {
result.push(Contour {
segments: replace(&mut current, Vec::new()),
});
}
result
})
}
/// Produces a list of the contours that make up the shape of this glyph.
/// Each contour consists of a sequence of segments. Each segment is either
/// a straight `Line` or a `Curve`.
///
/// The winding of the produced contours is clockwise for closed shapes,
/// anticlockwise for holes.
pub fn shape(&self) -> Option<Vec<Contour>> {
self.shape_with_offset(point(0.0, 0.0))
}
/// The bounding box of the shape of this glyph, not to be confused with
/// `pixel_bounding_box`, the conservative pixel-boundary bounding box. The
/// coordinates are relative to the glyph's origin.
pub fn exact_bounding_box(&self) -> Option<Rect<f32>> {
match self.g.inner {
GlyphInner::Proxy(ref font, id) => font.info.get_glyph_box(id).map(|bb| Rect {
min: point(bb.x0 as f32 * self.scale.x, -bb.y1 as f32 * self.scale.y),
max: point(bb.x1 as f32 * self.scale.x, -bb.y0 as f32 * self.scale.y),
}),
GlyphInner::Shared(ref data) => data.extents.map(|bb| Rect {
min: point(
bb.min.x as f32 * self.scale.x,
bb.min.y as f32 * self.scale.y,
),
max: point(
bb.max.x as f32 * self.scale.x,
bb.max.y as f32 * self.scale.y,
),
}),
}
}
/// Constructs a glyph that owns its data from this glyph. This is similar
/// to `Glyph::standalone`. See that function for more details.
pub fn standalone(&self) -> ScaledGlyph<'static> {
ScaledGlyph {
g: self.g.standalone(),
api_scale: self.api_scale,
scale: self.scale,
}
}
#[inline]
fn pixel_bounds_at(&self, p: Point<f32>) -> Option<Rect<i32>> {
// Use subpixel fraction in floor/ceil rounding to elimate rounding error
// from identical subpixel positions
let (x_trunc, x_fract) = (p.x.trunc() as i32, p.x.fract());
let (y_trunc, y_fract) = (p.y.trunc() as i32, p.y.fract());
match self.g.inner {
GlyphInner::Proxy(ref font, id) => font
.info
.get_glyph_bitmap_box_subpixel(id, self.scale.x, self.scale.y, x_fract, y_fract)
.map(|bb| Rect {
min: point(x_trunc + bb.x0, y_trunc + bb.y0),
max: point(x_trunc + bb.x1, y_trunc + bb.y1),
}),
GlyphInner::Shared(ref data) => data.extents.map(|bb| Rect {
min: point(
(bb.min.x as f32 * self.scale.x + x_fract).floor() as i32 + x_trunc,
(bb.min.y as f32 * self.scale.y + y_fract).floor() as i32 + y_trunc,
),
max: point(
(bb.max.x as f32 * self.scale.x + x_fract).ceil() as i32 + x_trunc,
(bb.max.y as f32 * self.scale.y + y_fract).ceil() as i32 + y_trunc,
),
}),
}
}
}
impl<'a> PositionedGlyph<'a> {
/// The glyph identifier for this glyph.
pub fn id(&self) -> GlyphId {
self.sg.id()
}
/// The font to which this glyph belongs. If the glyph is a standalone glyph
/// that owns its resources, it no longer has a reference to the font which
/// it was created from (using `standalone()`). In which case, `None` is
/// returned.
pub fn font(&self) -> Option<&Font<'a>> {
self.sg.font()
}
/// A reference to this glyph without positioning
pub fn unpositioned(&self) -> &ScaledGlyph<'a> {
&self.sg
}
/// Removes the positioning from this glyph
pub fn into_unpositioned(self) -> ScaledGlyph<'a> {
self.sg
}
/// The conservative pixel-boundary bounding box for this glyph. This is the
/// smallest rectangle aligned to pixel boundaries that encloses the shape
/// of this glyph at this position. Note that the origin of the glyph, at
/// pixel-space coordinates (0, 0), is at the top left of the bounding box.
pub fn pixel_bounding_box(&self) -> Option<Rect<i32>> {
self.bb
}
/// Similar to `ScaledGlyph::shape()`, but with the position of the glyph
/// taken into account.
pub fn shape(&self) -> Option<Vec<Contour>> {
self.sg.shape_with_offset(self.position)
}
pub fn scale(&self) -> Scale {
self.sg.api_scale
}
pub fn position(&self) -> Point<f32> {
self.position
}
/// Rasterises this glyph. For each pixel in the rect given by
/// `pixel_bounding_box()`, `o` is called:
///
/// ```ignore
/// o(x, y, v)
/// ```
///
/// where `x` and `y` are the coordinates of the pixel relative to the `min`
/// coordinates of the bounding box, and `v` is the analytically calculated
/// coverage of the pixel by the shape of the glyph. Calls to `o` proceed in
/// horizontal scanline order, similar to this pseudo-code:
///
/// ```ignore
/// let bb = glyph.pixel_bounding_box();
/// for y in 0..bb.height() {
/// for x in 0..bb.width() {
/// o(x, y, calc_coverage(&glyph, x, y));
/// }
/// }
/// ```
pub fn draw<O: FnMut(u32, u32, f32)>(&self, o: O) {
use crate::geometry::{Curve, Line};
use stb_truetype::VertexType;
let shape = match self.sg.g.inner {
GlyphInner::Proxy(ref font, id) => {
font.info.get_glyph_shape(id).unwrap_or_else(Vec::new)
}
GlyphInner::Shared(ref data) => data.shape.clone().unwrap_or_else(Vec::new),
};
let bb = if let Some(bb) = self.bb.as_ref() {
bb
} else {
return;
};
let offset = vector(bb.min.x as f32, bb.min.y as f32);
let mut lines = Vec::new();
let mut curves = Vec::new();
let mut last = point(0.0, 0.0);
for v in shape {
let end = point(
v.x as f32 * self.sg.scale.x + self.position.x,
-v.y as f32 * self.sg.scale.y + self.position.y,
) - offset;
match v.vertex_type() {
VertexType::LineTo => lines.push(Line { p: [last, end] }),
VertexType::CurveTo => {
let control = point(
v.cx as f32 * self.sg.scale.x + self.position.x,
-v.cy as f32 * self.sg.scale.y + self.position.y,
) - offset;
curves.push(Curve {
p: [last, control, end],
})
}
VertexType::MoveTo => {}
}
last = end;
}
rasterizer::rasterize(
&lines,
&curves,
(bb.max.x - bb.min.x) as u32,
(bb.max.y - bb.min.y) as u32,
o,
);
}
/// Constructs a glyph that owns its data from this glyph. This is similar
/// to `Glyph::standalone`. See that function for more details.
pub fn standalone(&self) -> PositionedGlyph<'static> {
PositionedGlyph {
sg: self.sg.standalone(),
bb: self.bb,
position: self.position,
}
}
/// Resets positioning information and recalculates the pixel bounding box
pub fn set_position(&mut self, p: Point<f32>) {
let p_diff = p - self.position;
if relative_eq!(p_diff.x.fract(), 0.0) && relative_eq!(p_diff.y.fract(), 0.0) {
if let Some(bb) = self.bb.as_mut() {
let rounded_diff = vector(p_diff.x.round() as i32, p_diff.y.round() as i32);
bb.min = bb.min + rounded_diff;
bb.max = bb.max + rounded_diff;
}
} else {
self.bb = self.sg.pixel_bounds_at(p);
}
self.position = p;
}
}
/// The type for errors returned by rusttype.
#[derive(Debug)]
pub enum Error {
/// Font data presented to rusttype is not in a format that the library
/// recognizes.
UnrecognizedFormat,
/// Font data presented to rusttype was ill-formed (lacking necessary
/// tables, for example).
IllFormed,
/// The caller tried to access the `i`'th font from a `FontCollection`, but
/// the collection doesn't contain that many fonts.
CollectionIndexOutOfBounds,
/// The caller tried to convert a `FontCollection` into a font via
/// `into_font`, but the `FontCollection` contains more than one font.
CollectionContainsMultipleFonts,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
f.write_str(std::error::Error::description(self))
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
use self::Error::*;
match *self {
UnrecognizedFormat => "Font data in unrecognized format",
IllFormed => "Font data is ill-formed",
CollectionIndexOutOfBounds => "Font collection has no font at the given index",
CollectionContainsMultipleFonts => {
"Attempted to convert collection into a font, \
but collection contais more than one font"
}
}
}
}
impl std::convert::From<Error> for std::io::Error {
fn from(error: Error) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, error)
}
}