1use crate::cache::Cache;
4use crate::context::Context;
5use crate::device::Device;
6use crate::font::cid::Type0Font;
7use crate::font::generated::{mac_expert, mac_os_roman, mac_roman, standard, win_ansi};
8use crate::font::true_type::TrueTypeFont;
9use crate::font::type1::Type1Font;
10use crate::font::type3::Type3;
11use crate::interpret::state::State;
12use crate::{CacheKey, FontResolverFn, InterpreterSettings, Paint};
13use bitflags::bitflags;
14use hayro_syntax::object::Dict;
15use hayro_syntax::object::Name;
16use hayro_syntax::object::dict::keys::SUBTYPE;
17use hayro_syntax::object::dict::keys::*;
18use hayro_syntax::page::Resources;
19use hayro_syntax::xref::XRef;
20use kurbo::{Affine, BezPath, Vec2};
21use log::warn;
22use outline::OutlineFont;
23use skrifa::GlyphId;
24use std::fmt::Debug;
25use std::ops::Deref;
26use std::rc::Rc;
27use std::sync::Arc;
28
29mod blob;
30mod cid;
31mod cmap;
32mod generated;
33mod glyph_simulator;
34pub(crate) mod outline;
35mod standard_font;
36mod true_type;
37mod type1;
38pub(crate) mod type3;
39
40pub(crate) const UNITS_PER_EM: f32 = 1000.0;
41
42pub type FontData = Arc<dyn AsRef<[u8]> + Send + Sync>;
44
45use crate::util::hash128;
46pub use standard_font::StandardFont;
47
48pub enum Glyph<'a> {
50 Outline(OutlineGlyph),
52 Type3(Box<Type3Glyph<'a>>),
54}
55
56#[derive(Clone, Debug)]
58pub struct GlyphIdentifier {
59 id: GlyphId,
60 font: OutlineFont,
61}
62
63impl CacheKey for GlyphIdentifier {
64 fn cache_key(&self) -> u128 {
65 hash128(&(self.id, self.font.cache_key()))
66 }
67}
68
69#[derive(Clone, Debug)]
71pub struct OutlineGlyph {
72 pub(crate) id: GlyphId,
73 pub(crate) font: OutlineFont,
74}
75
76impl OutlineGlyph {
77 pub fn outline(&self) -> BezPath {
79 self.font.outline_glyph(self.id)
80 }
81
82 pub fn identifier(&self) -> GlyphIdentifier {
88 GlyphIdentifier {
89 id: self.id,
90 font: self.font.clone(),
91 }
92 }
93}
94
95pub struct Type3Glyph<'a> {
97 pub(crate) font: Rc<Type3<'a>>,
98 pub(crate) glyph_id: GlyphId,
99 pub(crate) state: State<'a>,
100 pub(crate) parent_resources: Resources<'a>,
101 pub(crate) cache: Cache,
102 pub(crate) xref: &'a XRef,
103 pub(crate) settings: InterpreterSettings,
104}
105
106impl<'a> Type3Glyph<'a> {
108 pub fn interpret(
110 &self,
111 device: &mut impl Device<'a>,
112 transform: Affine,
113 glyph_transform: Affine,
114 paint: &Paint<'a>,
115 ) {
116 self.font
117 .render_glyph(self, transform, glyph_transform, paint, device);
118 }
119}
120
121#[derive(Clone, Debug)]
122pub(crate) struct Font<'a>(u128, FontType<'a>);
123
124impl<'a> Font<'a> {
125 pub(crate) fn new(dict: &Dict<'a>, resolver: &FontResolverFn) -> Option<Self> {
126 let f_type = match dict.get::<Name>(SUBTYPE)?.deref() {
127 TYPE1 | MM_TYPE1 => FontType::Type1(Rc::new(Type1Font::new(dict, resolver)?)),
128 TRUE_TYPE => TrueTypeFont::new(dict)
129 .map(Rc::new)
130 .map(FontType::TrueType)
131 .or_else(|| {
132 Type1Font::new(dict, resolver)
133 .map(Rc::new)
134 .map(FontType::Type1)
135 })?,
136 TYPE0 => FontType::Type0(Rc::new(Type0Font::new(dict)?)),
137 TYPE3 => FontType::Type3(Rc::new(Type3::new(dict))),
138 f => {
139 warn!(
140 "unimplemented font type {:?}",
141 std::str::from_utf8(f).unwrap_or("unknown type")
142 );
143
144 return None;
145 }
146 };
147
148 let cache_key = dict.cache_key();
149
150 Some(Self(cache_key, f_type))
151 }
152
153 pub(crate) fn map_code(&self, code: u32) -> GlyphId {
154 match &self.1 {
155 FontType::Type1(f) => {
156 debug_assert!(code <= u8::MAX as u32);
157
158 f.map_code(code as u8)
159 }
160 FontType::TrueType(t) => {
161 debug_assert!(code <= u8::MAX as u32);
162
163 t.map_code(code as u8)
164 }
165 FontType::Type0(t) => t.map_code(code),
166 FontType::Type3(t) => {
167 debug_assert!(code <= u8::MAX as u32);
168
169 t.map_code(code as u8)
170 }
171 }
172 }
173
174 pub(crate) fn get_glyph(
175 &self,
176 glyph: GlyphId,
177 ctx: &mut Context<'a>,
178 resources: &Resources<'a>,
179 origin_displacement: Vec2,
180 ) -> (Glyph<'a>, Affine) {
181 let glyph_transform = ctx.get().text_state.full_transform()
182 * Affine::scale(1.0 / UNITS_PER_EM as f64)
183 * Affine::translate(origin_displacement);
184
185 let glyph = match &self.1 {
186 FontType::Type1(t) => {
187 let font = OutlineFont::Type1(t.clone());
188 Glyph::Outline(OutlineGlyph { id: glyph, font })
189 }
190 FontType::TrueType(t) => {
191 let font = OutlineFont::TrueType(t.clone());
192 Glyph::Outline(OutlineGlyph { id: glyph, font })
193 }
194 FontType::Type0(t) => {
195 let font = OutlineFont::Type0(t.clone());
196 Glyph::Outline(OutlineGlyph { id: glyph, font })
197 }
198 FontType::Type3(t) => {
199 let shape_glyph = Type3Glyph {
200 font: t.clone(),
201 glyph_id: glyph,
202 state: ctx.get().clone(),
203 parent_resources: resources.clone(),
204 cache: ctx.object_cache.clone(),
205 xref: ctx.xref,
206 settings: ctx.settings.clone(),
207 };
208
209 Glyph::Type3(Box::new(shape_glyph))
210 }
211 };
212
213 (glyph, glyph_transform)
214 }
215
216 pub(crate) fn code_advance(&self, code: u32) -> Vec2 {
217 match &self.1 {
218 FontType::Type1(t) => {
219 debug_assert!(code <= u8::MAX as u32);
220
221 Vec2::new(t.glyph_width(code as u8).unwrap_or(0.0) as f64, 0.0)
222 }
223 FontType::TrueType(t) => {
224 debug_assert!(code <= u8::MAX as u32);
225
226 Vec2::new(t.glyph_width(code as u8) as f64, 0.0)
227 }
228 FontType::Type0(t) => t.code_advance(code),
229 FontType::Type3(t) => {
230 debug_assert!(code <= u8::MAX as u32);
231
232 Vec2::new(t.glyph_width(code as u8) as f64, 0.0)
233 }
234 }
235 }
236
237 pub(crate) fn origin_displacement(&self, code: u32) -> Vec2 {
238 match &self.1 {
239 FontType::Type1(_) => Vec2::default(),
240 FontType::TrueType(_) => Vec2::default(),
241 FontType::Type0(t) => t.origin_displacement(code),
242 FontType::Type3(_) => Vec2::default(),
243 }
244 }
245
246 pub(crate) fn read_code(&self, bytes: &[u8], offset: usize) -> (u32, usize) {
247 match &self.1 {
248 FontType::Type1(_) => (bytes[offset] as u32, 1),
249 FontType::TrueType(_) => (bytes[offset] as u32, 1),
250 FontType::Type0(t) => t.read_code(bytes, offset),
251 FontType::Type3(_) => (bytes[offset] as u32, 1),
252 }
253 }
254
255 pub(crate) fn is_horizontal(&self) -> bool {
256 match &self.1 {
257 FontType::Type1(_) => true,
258 FontType::TrueType(_) => true,
259 FontType::Type0(t) => t.is_horizontal(),
260 FontType::Type3(_) => true,
261 }
262 }
263}
264
265impl CacheKey for Font<'_> {
266 fn cache_key(&self) -> u128 {
267 self.0
268 }
269}
270
271#[derive(Clone, Debug)]
272enum FontType<'a> {
273 Type1(Rc<Type1Font>),
274 TrueType(Rc<TrueTypeFont>),
275 Type0(Rc<Type0Font>),
276 Type3(Rc<Type3<'a>>),
277}
278
279#[derive(Debug)]
280enum Encoding {
281 Standard,
282 MacRoman,
283 WinAnsi,
284 MacExpert,
285 BuiltIn,
286}
287
288impl Encoding {
289 fn map_code(&self, code: u8) -> Option<&'static str> {
290 if code == 0 {
291 return Some(".notdef");
292 }
293 match self {
294 Encoding::Standard => standard::get(code),
295 Encoding::MacRoman => mac_roman::get(code).or_else(|| mac_os_roman::get(code)),
296 Encoding::WinAnsi => win_ansi::get(code),
297 Encoding::MacExpert => mac_expert::get(code),
298 Encoding::BuiltIn => None,
299 }
300 }
301}
302
303#[derive(Debug, Copy, Clone)]
305pub enum FontStretch {
306 Normal,
308 UltraCondensed,
310 ExtraCondensed,
312 Condensed,
314 SemiCondensed,
316 SemiExpanded,
318 Expanded,
320 ExtraExpanded,
322 UltraExpanded,
324}
325
326impl FontStretch {
327 fn from_string(s: &str) -> Self {
328 match s {
329 "UltraCondensed" => FontStretch::UltraCondensed,
330 "ExtraCondensed" => FontStretch::ExtraCondensed,
331 "Condensed" => FontStretch::Condensed,
332 "SemiCondensed" => FontStretch::SemiCondensed,
333 "SemiExpanded" => FontStretch::SemiExpanded,
334 "Expanded" => FontStretch::Expanded,
335 "ExtraExpanded" => FontStretch::ExtraExpanded,
336 "UltraExpanded" => FontStretch::UltraExpanded,
337 _ => FontStretch::Normal,
338 }
339 }
340}
341
342bitflags! {
343 #[derive(Debug)]
345 pub(crate) struct FontFlags: u32 {
346 const FIXED_PITCH = 1 << 0;
347 const SERIF = 1 << 1;
348 const SYMBOLIC = 1 << 2;
349 const SCRIPT = 1 << 3;
350 const NON_SYMBOLIC = 1 << 5;
351 const ITALIC = 1 << 6;
352 const ALL_CAP = 1 << 16;
353 const SMALL_CAP = 1 << 17;
354 const FORCE_BOLD = 1 << 18;
355 }
356}
357
358pub enum FontQuery {
360 Standard(StandardFont),
362 Fallback(FallbackFontQuery),
367}
368
369#[derive(Debug, Clone)]
371pub struct FallbackFontQuery {
372 pub post_script_name: Option<String>,
374 pub font_name: Option<String>,
376 pub font_family: Option<String>,
378 pub font_stretch: FontStretch,
380 pub font_weight: u32,
382 pub is_fixed_pitch: bool,
384 pub is_serif: bool,
386 pub is_italic: bool,
388 pub is_bold: bool,
390 pub is_small_cap: bool,
392}
393
394impl FallbackFontQuery {
395 pub(crate) fn new(dict: &Dict) -> Self {
396 let mut data = Self::default();
397
398 let remove_subset_prefix = |s: String| {
399 if s.contains("+") {
400 s.chars().skip(7).collect()
401 } else {
402 s
403 }
404 };
405
406 data.post_script_name = dict
407 .get::<Name>(BASE_FONT)
408 .map(|n| remove_subset_prefix(n.as_str().to_string()));
409
410 if let Some(descriptor) = dict.get::<Dict>(FONT_DESC) {
411 data.font_name = dict
412 .get::<Name>(FONT_NAME)
413 .map(|n| remove_subset_prefix(n.as_str().to_string()));
414 data.font_family = descriptor
415 .get::<Name>(FONT_FAMILY)
416 .map(|n| n.as_str().to_string());
417 data.font_stretch = descriptor
418 .get::<Name>(FONT_STRETCH)
419 .map(|n| FontStretch::from_string(n.as_str()))
420 .unwrap_or(FontStretch::Normal);
421 data.font_weight = descriptor.get::<u32>(FONT_WEIGHT).unwrap_or(400);
422
423 if let Some(flags) = descriptor
424 .get::<u32>(FLAGS)
425 .map(FontFlags::from_bits_truncate)
426 {
427 data.is_serif = flags.contains(FontFlags::SERIF);
428 data.is_italic = flags.contains(FontFlags::ITALIC)
429 || data
430 .post_script_name
431 .as_ref()
432 .is_some_and(|s| s.contains("Italic"));
433 data.is_small_cap = flags.contains(FontFlags::SMALL_CAP);
434 data.is_bold = data
435 .post_script_name
436 .as_ref()
437 .is_some_and(|s| s.contains("Bold"));
438 }
439 }
440
441 data
442 }
443
444 pub fn pick_standard_font(&self) -> StandardFont {
446 if self.is_fixed_pitch {
447 match (self.is_bold, self.is_italic) {
448 (true, true) => StandardFont::CourierBoldOblique,
449 (true, false) => StandardFont::CourierBold,
450 (false, true) => StandardFont::CourierOblique,
451 (false, false) => StandardFont::Courier,
452 }
453 } else if !self.is_serif {
454 match (self.is_bold, self.is_italic) {
455 (true, true) => StandardFont::HelveticaBoldOblique,
456 (true, false) => StandardFont::HelveticaBold,
457 (false, true) => StandardFont::HelveticaOblique,
458 (false, false) => StandardFont::Helvetica,
459 }
460 } else {
461 match (self.is_bold, self.is_italic) {
462 (true, true) => StandardFont::TimesBoldItalic,
463 (true, false) => StandardFont::TimesBold,
464 (false, true) => StandardFont::TimesItalic,
465 (false, false) => StandardFont::TimesRoman,
466 }
467 }
468 }
469}
470
471impl Default for FallbackFontQuery {
472 fn default() -> Self {
473 Self {
474 post_script_name: None,
475 font_name: None,
476 font_family: None,
477 font_stretch: FontStretch::Normal,
478 font_weight: 400,
479 is_fixed_pitch: false,
480 is_serif: false,
481 is_italic: false,
482 is_bold: false,
483 is_small_cap: false,
484 }
485 }
486}