font_map_core/font.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
//! This module contains the font enumeration and glyph data structures
//!
//! The `Font` struct contains all the glyphs in a font, along with any stored strings
//!
//! The `Glyph` struct contains information about a single glyph in a font:
//! - Unicode codepoint
//! - Postscript name
//! - Outline data
//!
#![allow(clippy::match_on_vec_items)]
#![allow(clippy::cast_possible_truncation)]
pub use crate::raw::ttf::NameKind as StringKind;
use crate::{
error::ParseResult,
raw::ttf::{GlyfOutline, SimpleGlyf, TrueTypeFont},
svg::SvgExt,
};
use std::collections::HashMap;
/// A parsed font, with access to its glyphs and stored strings
#[derive(Debug, Clone)]
pub struct Font {
glyphs: Vec<Glyph>,
strings: HashMap<StringKind, String>,
}
impl Font {
/// Creates a new font from the given font data
///
/// # Errors
/// Returns an error if the font data is invalid or cannot be parsed
pub fn new(font_data: &[u8]) -> ParseResult<Self> {
let font = TrueTypeFont::new(font_data)?;
Ok(font.into())
}
/// Creates a new font from the font file at the specified path
///
/// # Errors
/// Returns an error if the font data is invalid or cannot be parsed
pub fn from_file(path: impl AsRef<std::path::Path>) -> ParseResult<Self> {
let font_data = std::fs::read(path)?;
Self::new(&font_data)
}
/// Returns the string with the specified kind, if it exists
#[must_use]
pub fn string(&self, kind: StringKind) -> Option<&str> {
self.strings.get(&kind).map(String::as_str)
}
/// Returns all the strings in the font
#[must_use]
pub fn strings(&self) -> &HashMap<StringKind, String> {
&self.strings
}
/// Returns the glyph with the specified unicode codepoint, if it exists
#[must_use]
pub fn glyph(&self, codepoint: u32) -> Option<&Glyph> {
self.glyphs.iter().find(|g| g.codepoint == codepoint)
}
/// Returns the glyph with the specified postscript name, if it exists
#[must_use]
pub fn glyph_named(&self, name: &str) -> Option<&Glyph> {
self.glyphs.iter().find(|g| g.name == name)
}
/// Returns the glyphs in the font
#[must_use]
pub fn glyphs(&self) -> &[Glyph] {
&self.glyphs
}
}
impl From<TrueTypeFont> for Font {
fn from(value: TrueTypeFont) -> Self {
let cmap = value.cmap_table;
let post = value.post_table;
let name = value.name_table;
let glyf = value.glyf_table;
let mut strings = HashMap::new();
for record in name.records {
strings.insert(record.name_id, record.name);
}
let mut glyphs = Vec::new();
for (glyph_index, name) in post.glyph_names.into_iter().enumerate() {
let glyph_index = glyph_index as u16;
// Find unicode codepoint, skipping unmapped glyphs
let codepoint = cmap.unicode_subtable.get_codepoint(glyph_index);
let codepoint = match codepoint {
Some(c) if glyph_index == 0 => c,
Some(c) if c != 0xFFFF => c,
_ => continue,
};
// Get the glyph outline data
let outline = match glyf[glyph_index as usize] {
GlyfOutline::Simple(ref outline) => outline.clone(),
GlyfOutline::Compound(ref outline) => outline.as_simple(&glyf),
};
glyphs.push(Glyph {
codepoint,
name,
outline,
});
}
Self { glyphs, strings }
}
}
/// A single glyph in a font
#[derive(Debug, Clone)]
pub struct Glyph {
codepoint: u32,
name: String,
outline: SimpleGlyf,
}
impl Glyph {
/// Returns the unicode range for the glyph
#[must_use]
pub fn unicode_range(&self) -> &str {
crate::unicode_range::unicode_range(self.codepoint)
}
/// Returns the unicode codepoint for the glyph
#[must_use]
pub fn codepoint(&self) -> u32 {
self.codepoint
}
/// Returns the postscript name of the glyph
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
/// Returns the raw outline data of this glyph
/// Compound glyphs will be simplified to a single outline
#[must_use]
pub fn outline(&self) -> &SimpleGlyf {
&self.outline
}
/// Returns the SVG data of this glyph's outline
#[must_use]
pub fn svg_preview(&self) -> String {
self.outline.to_svg()
}
/// Returns the gzip compressed SVGZ data of this glyph
///
/// # Errors
/// Returns an error if the data cannot be compressed
#[cfg(feature = "extended-svg")]
#[cfg_attr(docsrs, doc(cfg(feature = "extended-svg")))]
pub fn svgz_preview(&self) -> std::io::Result<Vec<u8>> {
self.outline.to_svgz()
}
/// Generates a `data:` link containing the outline svg data for this glyph
///
/// # Errors
/// Returns an error if the data cannot be encoded properly
#[cfg(feature = "extended-svg")]
#[cfg_attr(docsrs, doc(cfg(feature = "extended-svg")))]
pub fn svg_dataimage_url(&self) -> std::io::Result<String> {
self.outline.to_svg_dataimage_url()
}
}