use lightweight_pdf_core::FontKey;
use lightweight_pdf_fonts::{EmbeddedFontMetrics, FontData, FontError};
use lightweight_pdf_layout::{FontMetrics, FontResolver};
#[cfg(feature = "default-fonts")]
const SANS_REGULAR_BYTES: &[u8] = include_bytes!("../assets/fonts/SourceSans3-Regular.ttf");
#[cfg(feature = "default-fonts")]
const SANS_BOLD_BYTES: &[u8] = include_bytes!("../assets/fonts/SourceSans3-Bold.ttf");
struct MetricsAdapter(pub(crate) EmbeddedFontMetrics);
impl FontMetrics for MetricsAdapter {
fn advance(&self, ch: char) -> f32 {
self.0.advance_1000(ch).unwrap_or(500.0)
}
fn ascent(&self) -> f32 {
self.0.ascent
}
fn descent(&self) -> f32 {
self.0.descent
}
fn has_glyph(&self, ch: char) -> bool {
self.0.advance_1000(ch).is_some()
}
}
pub struct RegisteredFont {
pub font_data: FontData,
pub base_font_name: String,
adapter: MetricsAdapter,
}
impl RegisteredFont {
fn new(bytes: &[u8], base_font_name: impl Into<String>) -> Result<Self, FontError> {
let font_data = FontData::load(bytes.to_vec())?;
let metrics = EmbeddedFontMetrics::from_font_data(&font_data)?;
Ok(RegisteredFont {
font_data,
base_font_name: base_font_name.into(),
adapter: MetricsAdapter(metrics),
})
}
pub fn metrics(&self) -> &EmbeddedFontMetrics {
&self.adapter.0
}
}
pub struct FontRegistry {
fonts: std::collections::HashMap<FontKey, RegisteredFont>,
default_key: FontKey,
}
impl FontRegistry {
pub fn empty() -> Self {
FontRegistry {
fonts: std::collections::HashMap::new(),
default_key: FontKey::SANS_REGULAR,
}
}
#[cfg(feature = "default-fonts")]
pub fn with_defaults() -> Result<Self, FontError> {
let mut reg = Self::empty();
reg.register_named(FontKey::SANS_REGULAR, "SourceSans3-Subset", SANS_REGULAR_BYTES)?;
reg.register_named(FontKey::SANS_BOLD, "SourceSans3-Bold-Subset", SANS_BOLD_BYTES)?;
Ok(reg)
}
pub fn with_fonts(regular_bytes: &[u8], bold_bytes: &[u8]) -> Result<Self, FontError> {
let mut reg = Self::empty();
reg.register_named(FontKey::SANS_REGULAR, "CustomFont-Regular-Subset", regular_bytes)?;
reg.register_named(FontKey::SANS_BOLD, "CustomFont-Bold-Subset", bold_bytes)?;
Ok(reg)
}
pub fn register(&mut self, key: FontKey, bytes: &[u8]) -> Result<(), FontError> {
let name = format!("CustomFont-{}-Subset", key.0);
self.register_named(key, name, bytes)
}
pub fn register_named(&mut self, key: FontKey, name: impl Into<String>, bytes: &[u8]) -> Result<(), FontError> {
let font = RegisteredFont::new(bytes, name)?;
self.fonts.insert(key, font);
Ok(())
}
pub fn font_entries(&self) -> Vec<(FontKey, &RegisteredFont)> {
self.fonts.iter().map(|(&k, v)| (k, v)).collect()
}
pub fn is_empty(&self) -> bool {
self.fonts.is_empty()
}
pub fn entry(&self, key: FontKey) -> &RegisteredFont {
self.fonts
.get(&key)
.or_else(|| self.fonts.get(&self.default_key))
.expect("FontRegistry must contain at least one registered font")
}
pub fn get(&self, key: FontKey) -> Option<&RegisteredFont> {
self.fonts.get(&key)
}
}
impl FontResolver for FontRegistry {
fn metrics(&self, key: FontKey) -> &dyn FontMetrics {
&self.entry(key).adapter
}
}