use crate::{Aabb, SegmentCollector};
#[derive(Debug, Clone)]
pub enum ReadGlyphOutlineError<Inner: std::error::Error> {
NotFound,
ShouldRasterize,
EmptyGlyph,
Other(Inner),
}
impl<Inner: std::error::Error> std::fmt::Display for ReadGlyphOutlineError<Inner> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ReadGlyphOutlineError::NotFound => write!(f, "Requested glyph was not found in font."),
ReadGlyphOutlineError::ShouldRasterize => {
write!(
f,
"Requested glyph can not be rendered with signed distance field."
)
}
ReadGlyphOutlineError::EmptyGlyph => write!(f, "Requested glyph is empty."),
ReadGlyphOutlineError::Other(e) => std::fmt::Display::fmt(e, f),
}
}
}
impl<Inner: std::error::Error> std::error::Error for ReadGlyphOutlineError<Inner> {}
pub trait OutlineProvider {
type ReadFontError: std::error::Error;
fn length_per_em(&self) -> f32;
fn collect_outline<'collect>(
&self,
collector: &mut SegmentCollector<'collect>,
) -> Result<Aabb, ReadGlyphOutlineError<Self::ReadFontError>>;
}
#[cfg(feature = "skrifa")]
pub struct SkrifaOutlineProvider<'a> {
font: skrifa::FontRef<'a>,
glyph_id: skrifa::GlyphId,
}
#[cfg(feature = "skrifa")]
impl<'a> SkrifaOutlineProvider<'a> {
pub fn new(font: skrifa::FontRef<'a>, glyph_id: skrifa::GlyphId) -> Self {
Self { font, glyph_id }
}
}
#[cfg(feature = "skrifa")]
impl<'a> OutlineProvider for SkrifaOutlineProvider<'a> {
type ReadFontError = skrifa::outline::DrawError;
fn collect_outline<'collect>(
&self,
collector: &mut SegmentCollector<'collect>,
) -> Result<Aabb, ReadGlyphOutlineError<Self::ReadFontError>> {
use skrifa::{MetadataProvider, raw::TableProvider};
let outlines = self.font.outline_glyphs();
let font = &self.font;
if let Ok(colr) = font.colr()
&& (colr.v0_base_glyph(self.glyph_id).is_ok_and(|x| x.is_some())
|| colr.v1_base_glyph(self.glyph_id).is_ok_and(|x| x.is_some()))
{
return Err(ReadGlyphOutlineError::ShouldRasterize);
}
if let Ok(svg) = font.svg()
&& svg.glyph_data(self.glyph_id).is_ok_and(|x| x.is_some())
{
return Err(ReadGlyphOutlineError::ShouldRasterize);
}
if font
.bitmap_strikes()
.glyph_for_size(skrifa::prelude::Size::unscaled(), self.glyph_id)
.is_some()
{
return Err(ReadGlyphOutlineError::ShouldRasterize);
}
let (Some(glyph), Some(bound)) = (
outlines.get(self.glyph_id),
font.glyph_metrics(
skrifa::prelude::Size::unscaled(),
skrifa::prelude::LocationRef::default(),
)
.bounds(self.glyph_id),
) else {
return Err(ReadGlyphOutlineError::NotFound);
};
let format = outlines.format();
let reversed = matches!(
format,
Some(skrifa::outline::OutlineGlyphFormat::Cff)
| Some(skrifa::outline::OutlineGlyphFormat::Cff2)
);
collector.set_reversed(reversed);
glyph
.draw(
skrifa::outline::DrawSettings::unhinted(
skrifa::prelude::Size::unscaled(),
skrifa::prelude::LocationRef::default(),
),
collector,
)
.map_err(ReadGlyphOutlineError::Other)?;
Ok(Aabb::new(
bound.x_min,
bound.y_min,
bound.x_max,
bound.y_max,
))
}
fn length_per_em(&self) -> f32 {
use skrifa::MetadataProvider;
self.font
.metrics(
skrifa::prelude::Size::unscaled(),
skrifa::prelude::LocationRef::default(),
)
.units_per_em as f32
}
}
#[cfg(feature = "skrifa")]
impl skrifa::outline::OutlinePen for SegmentCollector<'_> {
fn move_to(&mut self, x: f32, y: f32) {
self.move_to(x, y);
}
fn line_to(&mut self, x: f32, y: f32) {
self.line_to(x, y);
}
fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
self.quad_to(cx0, cy0, x, y);
}
fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
self.curve_to(cx0, cy0, cx1, cy1, x, y);
}
fn close(&mut self) {
self.close()
}
}