use std::fmt::Write;
use std::hash::{Hash, Hasher};
use std::{ops::Deref, sync::Arc};
pub use ttf_parser::GlyphId;
use typst::text::Font;
use typst::visualize::{Image as TypstImage, RasterFormat};
use reflexo::hash::item_hash128;
use reflexo::{HashedTrait, ImmutStr, StaticHash128};
use super::ligature::resolve_ligature;
pub trait IGlyphProvider {
fn ligature_glyph(&self, font: &Font, id: GlyphId) -> Option<ImmutStr>;
fn svg_glyph(&self, font: &Font, id: GlyphId) -> Option<Arc<[u8]>>;
fn bitmap_glyph(&self, font: &Font, id: GlyphId, ppem: u16) -> Option<(TypstImage, i16, i16)>;
fn outline_glyph(&self, font: &Font, id: GlyphId) -> Option<String>;
}
#[derive(Clone)]
pub struct GlyphProvider(Arc<HashedTrait<dyn IGlyphProvider + Send + Sync>>);
impl GlyphProvider {
#[allow(clippy::arc_with_non_send_sync)]
pub fn new<T>(provider: T) -> Self
where
T: IGlyphProvider + Send + Sync + Hash + 'static,
{
let hash = item_hash128(&provider);
let provider = Box::new(provider);
Self(Arc::new(
HashedTrait::<dyn IGlyphProvider + Send + Sync>::new(hash, provider),
))
}
}
impl Deref for GlyphProvider {
type Target = dyn IGlyphProvider;
fn deref(&self) -> &Self::Target {
(*self.0.as_ref()).deref()
}
}
impl Hash for GlyphProvider {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u128(self.0.get_hash());
}
}
impl Default for GlyphProvider {
fn default() -> Self {
Self::new(FontGlyphProvider::default())
}
}
#[derive(Default, Hash)]
pub struct FontGlyphProvider {}
impl IGlyphProvider for FontGlyphProvider {
fn ligature_glyph(&self, font: &Font, id: GlyphId) -> Option<ImmutStr> {
resolve_ligature(font, id)
}
fn svg_glyph(&self, font: &Font, id: GlyphId) -> Option<Arc<[u8]>> {
let font_face = font.ttf();
Some(font_face.glyph_svg_image(id)?.data.into())
}
fn bitmap_glyph(&self, font: &Font, id: GlyphId, ppem: u16) -> Option<(TypstImage, i16, i16)> {
let font_face = font.ttf();
let raster = font_face.glyph_raster_image(id, ppem)?;
if raster.format != ttf_parser::RasterImageFormat::PNG {
return None;
}
let glyph_image = TypstImage::new(
raster.data.into(),
RasterFormat::Png.into(),
None,
)
.ok()?;
Some((glyph_image, raster.x, raster.y))
}
fn outline_glyph(&self, font: &Font, id: GlyphId) -> Option<String> {
let font_face = font.ttf();
let mut builder = SvgOutlineBuilder(String::new());
font_face.outline_glyph(id, &mut builder)?;
Some(builder.0)
}
}
#[derive(Default, Hash)]
pub struct DummyFontGlyphProvider {}
impl IGlyphProvider for DummyFontGlyphProvider {
fn ligature_glyph(&self, _font: &Font, _id: GlyphId) -> Option<ImmutStr> {
None
}
fn svg_glyph(&self, _font: &Font, _id: GlyphId) -> Option<Arc<[u8]>> {
None
}
fn bitmap_glyph(
&self,
_font: &Font,
_id: GlyphId,
_ppem: u16,
) -> Option<(TypstImage, i16, i16)> {
None
}
fn outline_glyph(&self, _font: &Font, _id: GlyphId) -> Option<String> {
None
}
}
#[derive(Default)]
struct SvgOutlineBuilder(pub String);
impl ttf_parser::OutlineBuilder for SvgOutlineBuilder {
fn move_to(&mut self, x: f32, y: f32) {
write!(&mut self.0, "M {} {} ", x, y).unwrap();
}
fn line_to(&mut self, x: f32, y: f32) {
write!(&mut self.0, "L {} {} ", x, y).unwrap();
}
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
write!(&mut self.0, "Q {} {} {} {} ", x1, y1, x, y).unwrap();
}
fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
write!(&mut self.0, "C {} {} {} {} {} {} ", x1, y1, x2, y2, x, y).unwrap();
}
fn close(&mut self) {
write!(&mut self.0, "Z ").unwrap();
}
}