use std::path::Path;
use parking_lot::Mutex;
use skia_safe::{FontMgr, textlayout::TypefaceFontProvider};
use crate::native::error::NativeError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FontAxisTag([u8; 4]);
impl FontAxisTag {
pub const fn new(bytes: &[u8; 4]) -> Self {
Self(*bytes)
}
pub fn as_bytes(&self) -> &[u8; 4] {
&self.0
}
pub const WGHT: Self = Self(*b"wght");
pub const WDTH: Self = Self(*b"wdth");
pub const OPSZ: Self = Self(*b"opsz");
pub const SLNT: Self = Self(*b"slnt");
pub const ITAL: Self = Self(*b"ital");
}
impl std::str::FromStr for FontAxisTag {
type Err = InvalidFontAxisTag;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let b = s.as_bytes();
if b.len() == 4 && b.iter().all(u8::is_ascii) {
Ok(Self([b[0], b[1], b[2], b[3]]))
} else {
Err(InvalidFontAxisTag)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidFontAxisTag;
impl std::fmt::Display for InvalidFontAxisTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("FontAxisTag requires exactly 4 ASCII bytes")
}
}
impl std::error::Error for InvalidFontAxisTag {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FontVariation {
pub axis: FontAxisTag,
pub value: f32,
}
impl FontVariation {
pub const fn new(axis: FontAxisTag, value: f32) -> Self {
Self { axis, value }
}
}
pub struct NativeFontManager {
inner: Mutex<NativeFontManagerInner>,
}
struct NativeFontManagerInner {
provider: TypefaceFontProvider,
font_mgr: FontMgr,
families: Vec<String>,
}
impl Default for NativeFontManager {
fn default() -> Self {
Self::new()
}
}
impl NativeFontManager {
pub fn new() -> Self {
Self {
inner: Mutex::new(NativeFontManagerInner {
provider: TypefaceFontProvider::new(),
font_mgr: FontMgr::new(),
families: Vec::new(),
}),
}
}
pub fn register_font_from_data(&self, family: &str, bytes: &[u8]) -> Result<(), NativeError> {
let mut inner = self.inner.lock();
let typeface =
inner
.font_mgr
.new_from_data(bytes, None)
.ok_or_else(|| NativeError::FontRegister {
reason: format!("could not parse typeface for family {family:?}"),
})?;
inner.provider.register_typeface(typeface, Some(family));
if !inner.families.iter().any(|f| f == family) {
inner.families.push(family.to_string());
}
Ok(())
}
pub fn register_font_from_path(
&self,
family: &str,
path: impl AsRef<Path>,
) -> Result<(), NativeError> {
let path = path.as_ref();
let bytes = std::fs::read(path).map_err(|e| NativeError::FontRegister {
reason: format!("could not read font file {}: {e}", path.display()),
})?;
self.register_font_from_data(family, &bytes)
}
pub fn has_font(&self, family: &str) -> bool {
let inner = self.inner.lock();
inner.families.iter().any(|f| f == family)
}
pub fn families(&self) -> Vec<String> {
let inner = self.inner.lock();
inner.families.clone()
}
pub(crate) fn snapshot_provider(&self) -> TypefaceFontProvider {
let inner = self.inner.lock();
inner.provider.clone()
}
pub(crate) fn registered_family_names(&self) -> Vec<String> {
self.inner.lock().families.clone()
}
}