#[cfg(feature = "embedded-fonts")]
use typst::foundations::Bytes;
use typst::text::{Font, FontBook, FontInfo};
use typst::utils::LazyHash;
use typst_kit::fonts::FontStore;
use crate::CanonicalIdentity;
use crate::pack::{FontFaceIdentity, font_container_identity};
use crate::payload::SharedBytes;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FontDisposition {
Embedded,
External,
}
impl FontDisposition {
pub fn embedded_if(embed: bool) -> Self {
if embed {
Self::Embedded
} else {
Self::External
}
}
pub fn is_embedded(self) -> bool {
matches!(self, Self::Embedded)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum FontContainerError {
#[error("font container has no readable face")]
NoReadableFace,
}
#[derive(Clone, Debug)]
pub struct FontContainer {
data: SharedBytes,
identity: CanonicalIdentity,
faces: Vec<FontContainerFace>,
}
impl FontContainer {
pub fn new(data: impl Into<Vec<u8>>) -> Result<Self, FontContainerError> {
Self::from_shared(SharedBytes::new(data.into()))
}
#[cfg(feature = "embedded-fonts")]
fn from_bytes(data: Bytes) -> Result<Self, FontContainerError> {
Self::from_shared(SharedBytes::from_typst(data))
}
pub(crate) fn from_shared(data: SharedBytes) -> Result<Self, FontContainerError> {
let identity = font_container_identity(data.as_slice());
let faces = Font::iter(data.to_typst())
.map(|font| FontContainerFace {
identity: FontFaceIdentity::new(identity, font.index()),
font,
})
.collect::<Vec<_>>();
if faces.is_empty() {
return Err(FontContainerError::NoReadableFace);
}
Ok(Self {
data,
identity,
faces,
})
}
pub fn data(&self) -> &[u8] {
self.data.as_slice()
}
pub fn identity(&self) -> CanonicalIdentity {
self.identity
}
pub fn faces(&self) -> &[FontContainerFace] {
&self.faces
}
pub(crate) fn font(&self, index: u32) -> Option<Font> {
self.faces
.iter()
.find(|face| face.identity.index() == index)
.map(|face| face.font.clone())
}
}
impl PartialEq for FontContainer {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl Eq for FontContainer {}
#[derive(Clone, Debug)]
pub struct FontContainerFace {
identity: FontFaceIdentity,
font: Font,
}
impl FontContainerFace {
pub fn identity(&self) -> FontFaceIdentity {
self.identity
}
pub fn data(&self) -> &[u8] {
self.font.data().as_slice()
}
pub fn info(&self) -> &FontInfo {
self.font.info()
}
}
impl PartialEq for FontContainerFace {
fn eq(&self, other: &Self) -> bool {
self.identity == other.identity && self.data() == other.data()
}
}
impl Eq for FontContainerFace {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FontCatalogEntry {
container: FontContainer,
disposition: FontDisposition,
}
impl FontCatalogEntry {
pub fn new(container: FontContainer, disposition: FontDisposition) -> Self {
Self {
container,
disposition,
}
}
pub fn container(&self) -> &FontContainer {
&self.container
}
pub fn disposition(&self) -> FontDisposition {
self.disposition
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FontCatalog {
entries: Vec<FontCatalogEntry>,
}
impl FontCatalog {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, entry: FontCatalogEntry) {
self.entries.push(entry);
}
pub fn entries(&self) -> &[FontCatalogEntry] {
&self.entries
}
pub fn faces(&self) -> Vec<FontCatalogFace> {
self.expand().faces
}
pub(crate) fn expand(&self) -> CatalogFonts {
let mut store = FontStore::new();
let mut faces = Vec::new();
for entry in &self.entries {
for face in entry.container.faces() {
let font = face.font.clone();
let info = font.info().clone();
faces.push(FontCatalogFace {
identity: face.identity(),
disposition: entry.disposition,
});
store.push((font, info));
}
}
CatalogFonts { store, faces }
}
}
impl Extend<FontCatalogEntry> for FontCatalog {
fn extend<T: IntoIterator<Item = FontCatalogEntry>>(&mut self, entries: T) {
self.entries.extend(entries);
}
}
impl FromIterator<FontCatalogEntry> for FontCatalog {
fn from_iter<T: IntoIterator<Item = FontCatalogEntry>>(entries: T) -> Self {
let mut catalog = Self::new();
catalog.extend(entries);
catalog
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FontCatalogFace {
identity: FontFaceIdentity,
disposition: FontDisposition,
}
impl FontCatalogFace {
pub fn identity(&self) -> FontFaceIdentity {
self.identity
}
pub fn disposition(&self) -> FontDisposition {
self.disposition
}
}
pub(crate) struct CatalogFonts {
store: FontStore,
faces: Vec<FontCatalogFace>,
}
impl CatalogFonts {
pub(crate) fn book(&self) -> &LazyHash<FontBook> {
self.store.book()
}
pub(crate) fn font(&self, index: usize) -> Option<Font> {
self.store.font(index)
}
pub(crate) fn disposition(&self, index: usize) -> Option<FontDisposition> {
self.faces.get(index).map(FontCatalogFace::disposition)
}
}
#[cfg(feature = "embedded-fonts")]
pub fn typst_embedded_font_containers() -> impl Iterator<Item = FontContainer> {
let mut containers: Vec<Bytes> = Vec::new();
for (font, _) in typst_kit::fonts::embedded() {
if !containers.iter().any(|data| data == font.data()) {
containers.push(font.data().clone());
}
}
containers
.into_iter()
.map(|data| FontContainer::from_bytes(data).expect("embedded Font Container is readable"))
}