pub(crate) mod glyph;
pub(crate) mod tables;
use std::sync::Arc;
use crate::api::path::Path;
use crate::font::tables::ParsedTables;
#[derive(Debug)]
pub enum FontError {
Io(std::io::Error),
InvalidData(&'static str),
}
impl std::fmt::Display for FontError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FontError::Io(e) => write!(f, "I/O error: {e}"),
FontError::InvalidData(msg) => write!(f, "invalid font data: {msg}"),
}
}
}
impl std::error::Error for FontError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FontError::Io(e) => Some(e),
FontError::InvalidData(_) => None,
}
}
}
impl From<std::io::Error> for FontError {
fn from(e: std::io::Error) -> Self {
FontError::Io(e)
}
}
pub struct FontData {
data: Vec<u8>,
}
impl FontData {
pub fn from_file(path: &str) -> Result<Self, FontError> {
let data = std::fs::read(path)?;
Ok(Self { data })
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self { data: bytes }
}
pub fn data(&self) -> &[u8] {
&self.data
}
}
pub struct FontFace {
data: Arc<Vec<u8>>,
tables: ParsedTables,
}
impl FontFace {
pub fn from_data(font_data: &FontData, index: u32) -> Result<Self, FontError> {
let data = Arc::new(font_data.data.clone());
let tables = tables::parse_all(&data, index)?;
Ok(Self { data, tables })
}
pub fn units_per_em(&self) -> u16 {
self.tables.units_per_em
}
pub fn ascent(&self) -> i16 {
self.tables.ascent
}
pub fn descent(&self) -> i16 {
self.tables.descent
}
pub fn line_gap(&self) -> i16 {
self.tables.line_gap
}
}
pub struct Font {
face: Arc<FontFaceInner>,
size: f64,
scale: f64,
}
struct FontFaceInner {
data: Arc<Vec<u8>>,
tables: ParsedTables,
}
impl Font {
pub fn from_face(face: &FontFace, size: f64) -> Self {
let scale = size / face.tables.units_per_em as f64;
let inner = Arc::new(FontFaceInner {
data: Arc::clone(&face.data),
tables: face.tables.clone(),
});
Self {
face: inner,
size,
scale,
}
}
pub fn size(&self) -> f64 {
self.size
}
pub fn scale(&self) -> f64 {
self.scale
}
pub fn map_char_to_glyph(&self, ch: char) -> u16 {
self.face.tables.cmap.map(ch as u32)
}
pub fn glyph_advance(&self, glyph_id: u16) -> f64 {
let gid = glyph_id as usize;
let aw = if gid < self.face.tables.hmtx.advance_widths.len() {
self.face.tables.hmtx.advance_widths[gid]
} else {
0
};
aw as f64 * self.scale
}
pub fn append_glyph_outline(
&self,
glyph_id: u16,
offset_x: f64,
offset_y: f64,
path: &mut Path,
) -> Result<(), FontError> {
glyph::append_glyph_outline(
glyph_id,
offset_x,
offset_y,
self.scale,
&self.face.tables,
&self.face.data,
path,
)
}
pub fn ascent(&self) -> f64 {
self.face.tables.ascent as f64 * self.scale
}
pub fn descent(&self) -> f64 {
self.face.tables.descent as f64 * self.scale
}
}