typst_pack/
font_catalog.rs1#[cfg(feature = "embedded-fonts")]
4use typst::foundations::Bytes;
5use typst::text::{Font, FontBook, FontInfo};
6use typst::utils::LazyHash;
7use typst_kit::fonts::FontStore;
8
9use crate::CanonicalIdentity;
10use crate::pack::{FontFaceIdentity, font_container_identity};
11use crate::payload::SharedBytes;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub enum FontDisposition {
21 Embedded,
23 External,
25}
26
27impl FontDisposition {
28 pub fn embedded_if(embed: bool) -> Self {
31 if embed {
32 Self::Embedded
33 } else {
34 Self::External
35 }
36 }
37
38 pub fn is_embedded(self) -> bool {
40 matches!(self, Self::Embedded)
41 }
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
46#[non_exhaustive]
47pub enum FontContainerError {
48 #[error("font container has no readable face")]
50 NoReadableFace,
51}
52
53#[derive(Clone, Debug)]
56pub struct FontContainer {
57 data: SharedBytes,
58 identity: CanonicalIdentity,
59 faces: Vec<FontContainerFace>,
60}
61
62impl FontContainer {
63 pub fn new(data: impl Into<Vec<u8>>) -> Result<Self, FontContainerError> {
65 Self::from_shared(SharedBytes::new(data.into()))
66 }
67
68 #[cfg(feature = "embedded-fonts")]
69 fn from_bytes(data: Bytes) -> Result<Self, FontContainerError> {
70 Self::from_shared(SharedBytes::from_typst(data))
71 }
72
73 pub(crate) fn from_shared(data: SharedBytes) -> Result<Self, FontContainerError> {
74 let identity = font_container_identity(data.as_slice());
75 let faces = Font::iter(data.to_typst())
76 .map(|font| FontContainerFace {
77 identity: FontFaceIdentity::new(identity, font.index()),
78 font,
79 })
80 .collect::<Vec<_>>();
81 if faces.is_empty() {
82 return Err(FontContainerError::NoReadableFace);
83 }
84 Ok(Self {
85 data,
86 identity,
87 faces,
88 })
89 }
90
91 pub fn data(&self) -> &[u8] {
93 self.data.as_slice()
94 }
95
96 pub fn identity(&self) -> CanonicalIdentity {
98 self.identity
99 }
100
101 pub fn faces(&self) -> &[FontContainerFace] {
103 &self.faces
104 }
105
106 pub(crate) fn font(&self, index: u32) -> Option<Font> {
107 self.faces
108 .iter()
109 .find(|face| face.identity.index() == index)
110 .map(|face| face.font.clone())
111 }
112}
113
114impl PartialEq for FontContainer {
115 fn eq(&self, other: &Self) -> bool {
116 self.data == other.data
117 }
118}
119
120impl Eq for FontContainer {}
121
122#[derive(Clone, Debug)]
124pub struct FontContainerFace {
125 identity: FontFaceIdentity,
126 font: Font,
127}
128
129impl FontContainerFace {
130 pub fn identity(&self) -> FontFaceIdentity {
132 self.identity
133 }
134
135 pub fn data(&self) -> &[u8] {
137 self.font.data().as_slice()
138 }
139
140 pub fn info(&self) -> &FontInfo {
142 self.font.info()
143 }
144}
145
146impl PartialEq for FontContainerFace {
147 fn eq(&self, other: &Self) -> bool {
148 self.identity == other.identity && self.data() == other.data()
149 }
150}
151
152impl Eq for FontContainerFace {}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct FontCatalogEntry {
157 container: FontContainer,
158 disposition: FontDisposition,
159}
160
161impl FontCatalogEntry {
162 pub fn new(container: FontContainer, disposition: FontDisposition) -> Self {
164 Self {
165 container,
166 disposition,
167 }
168 }
169
170 pub fn container(&self) -> &FontContainer {
172 &self.container
173 }
174
175 pub fn disposition(&self) -> FontDisposition {
177 self.disposition
178 }
179}
180
181#[derive(Clone, Debug, Default, PartialEq, Eq)]
189pub struct FontCatalog {
190 entries: Vec<FontCatalogEntry>,
191}
192
193impl FontCatalog {
194 pub fn new() -> Self {
196 Self::default()
197 }
198
199 pub fn push(&mut self, entry: FontCatalogEntry) {
201 self.entries.push(entry);
202 }
203
204 pub fn entries(&self) -> &[FontCatalogEntry] {
206 &self.entries
207 }
208
209 pub fn faces(&self) -> Vec<FontCatalogFace> {
213 self.expand().faces
214 }
215
216 pub(crate) fn expand(&self) -> CatalogFonts {
218 let mut store = FontStore::new();
219 let mut faces = Vec::new();
220 for entry in &self.entries {
221 for face in entry.container.faces() {
222 let font = face.font.clone();
223 let info = font.info().clone();
224 faces.push(FontCatalogFace {
225 identity: face.identity(),
226 disposition: entry.disposition,
227 });
228 store.push((font, info));
229 }
230 }
231 CatalogFonts { store, faces }
232 }
233}
234
235impl Extend<FontCatalogEntry> for FontCatalog {
236 fn extend<T: IntoIterator<Item = FontCatalogEntry>>(&mut self, entries: T) {
237 self.entries.extend(entries);
238 }
239}
240
241impl FromIterator<FontCatalogEntry> for FontCatalog {
242 fn from_iter<T: IntoIterator<Item = FontCatalogEntry>>(entries: T) -> Self {
243 let mut catalog = Self::new();
244 catalog.extend(entries);
245 catalog
246 }
247}
248
249#[derive(Clone, Copy, Debug, PartialEq, Eq)]
251pub struct FontCatalogFace {
252 identity: FontFaceIdentity,
253 disposition: FontDisposition,
254}
255
256impl FontCatalogFace {
257 pub fn identity(&self) -> FontFaceIdentity {
259 self.identity
260 }
261
262 pub fn disposition(&self) -> FontDisposition {
264 self.disposition
265 }
266}
267
268pub(crate) struct CatalogFonts {
271 store: FontStore,
272 faces: Vec<FontCatalogFace>,
273}
274
275impl CatalogFonts {
277 pub(crate) fn book(&self) -> &LazyHash<FontBook> {
279 self.store.book()
280 }
281
282 pub(crate) fn font(&self, index: usize) -> Option<Font> {
284 self.store.font(index)
285 }
286
287 pub(crate) fn disposition(&self, index: usize) -> Option<FontDisposition> {
290 self.faces.get(index).map(FontCatalogFace::disposition)
291 }
292}
293
294#[cfg(feature = "embedded-fonts")]
300pub fn typst_embedded_font_containers() -> impl Iterator<Item = FontContainer> {
301 let mut containers: Vec<Bytes> = Vec::new();
305 for (font, _) in typst_kit::fonts::embedded() {
306 if !containers.iter().any(|data| data == font.data()) {
307 containers.push(font.data().clone());
308 }
309 }
310 containers
311 .into_iter()
312 .map(|data| FontContainer::from_bytes(data).expect("embedded Font Container is readable"))
313}