mod outliner;
#[cfg(feature = "variable-fonts")]
mod variable;
use crate::{point, v2, Font, GlyphId, GlyphImageFormat, GlyphSvg, InvalidFont, Outline, Rect};
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt;
use owned_ttf_parser::{self as ttfp, AsFaceRef};
impl From<GlyphId> for ttfp::GlyphId {
#[inline]
fn from(id: GlyphId) -> Self {
Self(id.0)
}
}
#[derive(Clone)]
pub struct FontRef<'font>(ttfp::PreParsedSubtables<'font, ttfp::Face<'font>>);
impl fmt::Debug for FontRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FontRef")
}
}
impl<'font> FontRef<'font> {
#[inline]
pub fn try_from_slice(data: &'font [u8]) -> Result<Self, InvalidFont> {
Self::try_from_slice_and_index(data, 0)
}
#[inline]
pub fn try_from_slice_and_index(data: &'font [u8], index: u32) -> Result<Self, InvalidFont> {
Ok(Self(ttfp::PreParsedSubtables::from(
ttfp::Face::parse(data, index).map_err(|_| InvalidFont)?,
)))
}
}
pub struct FontVec(ttfp::PreParsedSubtables<'static, ttfp::OwnedFace>);
impl fmt::Debug for FontVec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FontVec")
}
}
impl FontVec {
#[inline]
pub fn try_from_vec(data: Vec<u8>) -> Result<Self, InvalidFont> {
Self::try_from_vec_and_index(data, 0)
}
#[inline]
pub fn try_from_vec_and_index(data: Vec<u8>, index: u32) -> Result<Self, InvalidFont> {
Ok(Self(ttfp::PreParsedSubtables::from(
ttfp::OwnedFace::from_vec(data, index).map_err(|_| InvalidFont)?,
)))
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.0.face.as_slice()
}
#[inline]
pub fn into_vec(self) -> Vec<u8> {
self.0.face.into_vec()
}
}
macro_rules! impl_font {
($font:ty) => {
impl Font for $font {
#[inline]
fn units_per_em(&self) -> Option<f32> {
Some(self.0.as_face_ref().units_per_em().into())
}
#[inline]
fn ascent_unscaled(&self) -> f32 {
self.0.as_face_ref().ascender().into()
}
#[inline]
fn descent_unscaled(&self) -> f32 {
self.0.as_face_ref().descender().into()
}
#[inline]
fn line_gap_unscaled(&self) -> f32 {
self.0.as_face_ref().line_gap().into()
}
#[inline]
fn italic_angle(&self) -> f32 {
self.0.as_face_ref().italic_angle()
}
#[inline]
fn glyph_id(&self, c: char) -> GlyphId {
let index = self.0.glyph_index(c).map(|id| id.0).unwrap_or(0);
GlyphId(index)
}
#[inline]
fn h_advance_unscaled(&self, id: GlyphId) -> f32 {
self.0
.as_face_ref()
.glyph_hor_advance(id.into())
.unwrap_or_default()
.into()
}
#[inline]
fn h_side_bearing_unscaled(&self, id: GlyphId) -> f32 {
self.0
.as_face_ref()
.glyph_hor_side_bearing(id.into())
.unwrap_or_default()
.into()
}
#[inline]
fn v_advance_unscaled(&self, id: GlyphId) -> f32 {
self.0
.as_face_ref()
.glyph_ver_advance(id.into())
.unwrap_or_default()
.into()
}
#[inline]
fn v_side_bearing_unscaled(&self, id: GlyphId) -> f32 {
self.0
.as_face_ref()
.glyph_ver_side_bearing(id.into())
.unwrap_or_default()
.into()
}
#[inline]
fn kern_unscaled(&self, first: GlyphId, second: GlyphId) -> f32 {
self.0
.glyphs_hor_kerning(first.into(), second.into())
.map(f32::from)
.unwrap_or_default()
}
fn outline(&self, id: GlyphId) -> Option<Outline> {
let mut outliner = outliner::OutlineCurveBuilder::default();
let ttfp::Rect {
x_min,
x_max,
y_min,
y_max,
} = self
.0
.as_face_ref()
.outline_glyph(id.into(), &mut outliner)
.filter(|b| b.x_min < b.x_max && b.y_min < b.y_max)?;
let curves = outliner.take_outline();
let bounds = Rect {
min: point(x_min.into(), y_max.into()),
max: point(x_max.into(), y_min.into()),
};
Some(Outline { bounds, curves })
}
#[inline]
fn glyph_count(&self) -> usize {
self.0.as_face_ref().number_of_glyphs() as _
}
fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
let face_ref = self.0.as_face_ref();
#[cfg(feature = "std")]
let mut used_indices =
std::collections::HashSet::with_capacity(face_ref.number_of_glyphs() as _);
#[cfg(not(feature = "std"))]
let mut used_indices = alloc::collections::BTreeSet::new();
let inner = Box::new(
face_ref
.tables()
.cmap
.iter()
.flat_map(|c| c.subtables)
.filter(|s| s.is_unicode())
.flat_map(move |subtable| {
let mut pairs = Vec::new();
subtable.codepoints(|c| {
if let Ok(ch) = char::try_from(c) {
if let Some(idx) = subtable.glyph_index(c).filter(|i| i.0 > 0) {
if used_indices.insert(idx.0) {
pairs.push((GlyphId(idx.0), ch));
}
}
}
});
pairs
}),
);
crate::CodepointIdIter { inner }
}
fn glyph_raster_image2(&self, id: GlyphId, size: u16) -> Option<v2::GlyphImage<'_>> {
use GlyphImageFormat::*;
let img = self.0.as_face_ref().glyph_raster_image(id.into(), size)?;
Some(v2::GlyphImage {
origin: point(img.x.into(), img.y.into()),
width: img.width,
height: img.height,
pixels_per_em: img.pixels_per_em,
data: img.data,
format: match img.format {
ttfp::RasterImageFormat::PNG => Png,
ttfp::RasterImageFormat::BitmapMono => BitmapMono,
ttfp::RasterImageFormat::BitmapMonoPacked => BitmapMonoPacked,
ttfp::RasterImageFormat::BitmapGray2 => BitmapGray2,
ttfp::RasterImageFormat::BitmapGray2Packed => BitmapGray2Packed,
ttfp::RasterImageFormat::BitmapGray4 => BitmapGray4,
ttfp::RasterImageFormat::BitmapGray4Packed => BitmapGray4Packed,
ttfp::RasterImageFormat::BitmapGray8 => BitmapGray8,
ttfp::RasterImageFormat::BitmapPremulBgra32 => BitmapPremulBgra32,
},
})
}
fn glyph_svg_image(&self, id: GlyphId) -> Option<GlyphSvg<'_>> {
let img = self.0.as_face_ref().glyph_svg_image(id.into())?;
Some(GlyphSvg {
data: img.data,
start_glyph_id: GlyphId(img.start_glyph_id.0),
end_glyph_id: GlyphId(img.end_glyph_id.0),
})
}
#[inline]
fn font_data(&self) -> &[u8] {
self.0.as_face_ref().raw_face().data
}
}
};
}
impl_font!(FontRef<'_>);
impl_font!(FontVec);