1use crate::context::Context;
4use crate::context::InterpreterCache;
5use crate::device::Device;
6use crate::font::cid::Type0Font;
7use crate::font::generated::{
8 glyph_names, mac_expert, mac_os_roman, mac_roman, standard, win_ansi,
9};
10use crate::font::true_type::TrueTypeFont;
11use crate::font::type1::Type1Font;
12use crate::font::type3::Type3;
13use crate::interpret::state::State;
14use crate::{CMapResolverFn, CacheKey, FontResolverFn, InterpreterSettings, Paint};
15use bitflags::bitflags;
16use hayro_syntax::object::Name;
17use hayro_syntax::object::dict::keys::SUBTYPE;
18use hayro_syntax::object::dict::keys::*;
19use hayro_syntax::object::{Dict, Stream};
20use hayro_syntax::page::Resources;
21use hayro_syntax::xref::XRef;
22use kurbo::{Affine, BezPath, Vec2};
23use outline::OutlineFont;
24use skrifa::GlyphId;
25use std::borrow::Cow;
26use std::fmt::Debug;
27use std::ops::Deref;
28use std::rc::Rc;
29use std::sync::Arc;
30
31mod blob;
32mod cid;
33mod generated;
34mod glyph_simulator;
35pub(crate) mod outline;
36mod standard_font;
37mod true_type;
38mod type1;
39pub(crate) mod type3;
40
41pub(crate) const UNITS_PER_EM: f32 = 1000.0;
42
43pub(crate) fn stretch_glyph(path: BezPath, expected_width: f32, actual_width: f32) -> BezPath {
44 if actual_width != 0.0 && actual_width != expected_width {
45 let stretch_factor = expected_width / actual_width;
46 Affine::scale_non_uniform(stretch_factor as f64, 1.0) * path
47 } else {
48 path
49 }
50}
51
52pub type FontData = Arc<dyn AsRef<[u8]> + Send + Sync>;
54
55pub(crate) fn strip_subset_prefix(name: &str) -> &str {
61 match name.split_once('+') {
62 Some((prefix, rest)) if prefix.len() == 6 => rest,
63 _ => name,
64 }
65}
66
67use crate::util::hash128;
68use hayro_cmap::{BfString, CMap, CMapName, CharacterCollection};
69pub use outline::OutlineFontData;
70pub use standard_font::StandardFont;
71
72pub enum Glyph<'a> {
74 Outline(OutlineGlyph),
76 Type3(Box<Type3Glyph<'a>>),
78}
79
80impl Glyph<'_> {
81 pub fn as_unicode(&self) -> Option<BfString> {
103 match self {
104 Glyph::Outline(g) => g.as_unicode(),
105 Glyph::Type3(g) => g.as_unicode(),
106 }
107 }
108}
109
110#[derive(Clone, Debug)]
112pub struct GlyphIdentifier {
113 id: GlyphId,
114 font: OutlineFont,
115}
116
117impl CacheKey for GlyphIdentifier {
118 fn cache_key(&self) -> u128 {
119 hash128(&(self.id, self.font.cache_key()))
120 }
121}
122
123#[derive(Clone, Debug)]
125pub struct OutlineGlyph {
126 pub(crate) id: GlyphId,
127 pub(crate) font: OutlineFont,
128 pub(crate) char_code: u32,
129}
130
131impl OutlineGlyph {
132 pub fn outline(&self) -> BezPath {
134 self.font.outline_glyph(self.id, self.char_code)
135 }
136
137 pub fn identifier(&self) -> GlyphIdentifier {
143 GlyphIdentifier {
144 id: self.id,
145 font: self.font.clone(),
146 }
147 }
148
149 pub fn as_unicode(&self) -> Option<BfString> {
153 self.font.char_code_to_unicode(self.char_code)
154 }
155
156 pub fn font_data(&self) -> Option<OutlineFontData> {
160 self.font.font_data()
161 }
162
163 pub fn glyph_id(&self) -> GlyphId {
165 self.id
166 }
167
168 pub fn advance_width(&self) -> Option<f32> {
173 self.font.glyph_advance_width(self.char_code)
174 }
175
176 pub fn font_cache_key(&self) -> u128 {
181 self.font.cache_key()
182 }
183}
184
185#[derive(Clone)]
187pub struct Type3Glyph<'a> {
188 pub(crate) font: Rc<Type3<'a>>,
189 pub(crate) glyph_id: GlyphId,
190 pub(crate) state: State<'a>,
191 pub(crate) parent_resources: Resources<'a>,
192 pub(crate) cache: InterpreterCache<'a>,
193 pub(crate) xref: &'a XRef,
194 pub(crate) settings: InterpreterSettings,
195 pub(crate) nesting_depth: u32,
196 pub(crate) char_code: u32,
197}
198
199impl<'a> Type3Glyph<'a> {
201 pub fn interpret(
203 &self,
204 device: &mut impl Device<'a>,
205 transform: Affine,
206 glyph_transform: Affine,
207 paint: &Paint<'a>,
208 ) {
209 self.font
210 .render_glyph(self, transform, glyph_transform, paint, device);
211 }
212
213 pub fn as_unicode(&self) -> Option<BfString> {
217 self.font.char_code_to_unicode(self.char_code)
218 }
219}
220
221impl CacheKey for Type3Glyph<'_> {
222 fn cache_key(&self) -> u128 {
223 hash128(&(self.font.cache_key(), self.glyph_id))
224 }
225}
226
227#[derive(Clone, Debug)]
228pub(crate) struct Font<'a>(u128, FontType<'a>);
229
230impl<'a> Font<'a> {
231 pub(crate) fn new(
232 dict: &Dict<'a>,
233 font_resolver: &FontResolverFn,
234 cmap_resolver: &CMapResolverFn,
235 ) -> Option<Self> {
236 let f_type = match dict.get::<Name<'_>>(SUBTYPE)?.deref() {
237 TYPE1 | MM_TYPE1 => {
238 FontType::Type1(Rc::new(Type1Font::new(dict, font_resolver, cmap_resolver)?))
239 }
240 TRUE_TYPE | OPEN_TYPE => FontType::TrueType(Rc::new(TrueTypeFont::new(
242 dict,
243 font_resolver,
244 cmap_resolver,
245 )?)),
246 TYPE0 => FontType::Type0(Rc::new(Type0Font::new(dict, font_resolver, cmap_resolver)?)),
247 TYPE3 => FontType::Type3(Rc::new(Type3::new(dict, cmap_resolver)?)),
248 f => {
249 warn!(
250 "unimplemented font type {:?}",
251 std::str::from_utf8(f).unwrap_or("unknown type")
252 );
253
254 return None;
255 }
256 };
257
258 let cache_key = dict.cache_key();
259
260 Some(Self(cache_key, f_type))
261 }
262
263 pub(crate) fn new_standard(
264 standard_font: StandardFont,
265 font_resolver: &FontResolverFn,
266 ) -> Option<Self> {
267 let font = Type1Font::new_standard(standard_font, font_resolver)?;
268
269 Some(Self(0, FontType::Type1(Rc::new(font))))
270 }
271
272 pub(crate) fn map_code(&self, code: u32) -> GlyphId {
273 match &self.1 {
274 FontType::Type1(f) => {
275 debug_assert!(code <= u8::MAX as u32);
276
277 f.map_code(code as u8)
278 }
279 FontType::TrueType(t) => {
280 debug_assert!(code <= u8::MAX as u32);
281
282 t.map_code(code as u8)
283 }
284 FontType::Type0(t) => t.map_code(code),
285 FontType::Type3(t) => {
286 debug_assert!(code <= u8::MAX as u32);
287
288 t.map_code(code as u8)
289 }
290 }
291 }
292
293 pub(crate) fn get_glyph(
294 &self,
295 glyph: GlyphId,
296 char_code: u32,
297 ctx: &mut Context<'a>,
298 resources: &Resources<'a>,
299 origin_displacement: Vec2,
300 ) -> (Glyph<'a>, Affine) {
301 let glyph_transform = ctx.get().text_state.full_transform()
302 * Affine::scale(1.0 / UNITS_PER_EM as f64)
303 * Affine::translate(origin_displacement);
304
305 let glyph = match &self.1 {
306 FontType::Type1(t) => {
307 let font = OutlineFont::Type1(t.clone());
308 Glyph::Outline(OutlineGlyph {
309 id: glyph,
310 font,
311 char_code,
312 })
313 }
314 FontType::TrueType(t) => {
315 let font = OutlineFont::TrueType(t.clone());
316 Glyph::Outline(OutlineGlyph {
317 id: glyph,
318 font,
319 char_code,
320 })
321 }
322 FontType::Type0(t) => {
323 let font = OutlineFont::Type0(t.clone());
324 Glyph::Outline(OutlineGlyph {
325 id: glyph,
326 font,
327 char_code,
328 })
329 }
330 FontType::Type3(t) => {
331 let nesting_depth = ctx.nesting_depth() + 1;
332 let shape_glyph = Type3Glyph {
333 font: t.clone(),
334 glyph_id: glyph,
335 state: ctx.get().clone(),
336 parent_resources: resources.clone(),
337 cache: ctx.interpreter_cache.clone(),
338 xref: ctx.xref,
339 settings: ctx.settings.clone(),
340 nesting_depth,
341 char_code,
342 };
343
344 Glyph::Type3(Box::new(shape_glyph))
345 }
346 };
347
348 (glyph, glyph_transform)
349 }
350
351 pub(crate) fn code_advance(&self, code: u32) -> Vec2 {
352 match &self.1 {
353 FontType::Type1(t) => {
354 debug_assert!(code <= u8::MAX as u32);
355
356 Vec2::new(t.glyph_width(code as u8).unwrap_or(0.0) as f64, 0.0)
357 }
358 FontType::TrueType(t) => {
359 debug_assert!(code <= u8::MAX as u32);
360
361 Vec2::new(t.glyph_width(code as u8) as f64, 0.0)
362 }
363 FontType::Type0(t) => t.code_advance(code),
364 FontType::Type3(t) => {
365 debug_assert!(code <= u8::MAX as u32);
366
367 Vec2::new(t.glyph_width(code as u8) as f64, 0.0)
368 }
369 }
370 }
371
372 pub(crate) fn origin_displacement(&self, code: u32) -> Vec2 {
373 match &self.1 {
374 FontType::Type1(_) => Vec2::default(),
375 FontType::TrueType(_) => Vec2::default(),
376 FontType::Type0(t) => t.origin_displacement(code),
377 FontType::Type3(_) => Vec2::default(),
378 }
379 }
380
381 pub(crate) fn read_code(&self, bytes: &[u8], offset: usize) -> (u32, usize) {
382 match &self.1 {
383 FontType::Type1(_) => (bytes[offset] as u32, 1),
384 FontType::TrueType(_) => (bytes[offset] as u32, 1),
385 FontType::Type0(t) => t.read_code(bytes, offset),
386 FontType::Type3(_) => (bytes[offset] as u32, 1),
387 }
388 }
389
390 pub(crate) fn is_horizontal(&self) -> bool {
391 match &self.1 {
392 FontType::Type1(_) => true,
393 FontType::TrueType(_) => true,
394 FontType::Type0(t) => t.is_horizontal(),
395 FontType::Type3(_) => true,
396 }
397 }
398}
399
400impl CacheKey for Font<'_> {
401 fn cache_key(&self) -> u128 {
402 self.0
403 }
404}
405
406#[derive(Clone, Debug)]
407enum FontType<'a> {
408 Type1(Rc<Type1Font>),
409 TrueType(Rc<TrueTypeFont>),
410 Type0(Rc<Type0Font>),
411 Type3(Rc<Type3<'a>>),
412}
413
414#[derive(Debug)]
415enum Encoding {
416 Standard,
417 MacRoman,
418 WinAnsi,
419 MacExpert,
420 BuiltIn,
421}
422
423impl Encoding {
424 fn map_code(&self, code: u8) -> Option<&'static str> {
425 if code == 0 {
426 return Some(".notdef");
427 }
428 match self {
429 Self::Standard => standard::get(code),
430 Self::MacRoman => mac_roman::get(code).or_else(|| mac_os_roman::get(code)),
431 Self::WinAnsi => win_ansi::get(code),
432 Self::MacExpert => mac_expert::get(code),
433 Self::BuiltIn => None,
434 }
435 }
436}
437
438#[derive(Debug, Copy, Clone)]
440pub enum FontStretch {
441 Normal,
443 UltraCondensed,
445 ExtraCondensed,
447 Condensed,
449 SemiCondensed,
451 SemiExpanded,
453 Expanded,
455 ExtraExpanded,
457 UltraExpanded,
459}
460
461impl FontStretch {
462 fn from_string(s: &str) -> Self {
463 match s {
464 "UltraCondensed" => Self::UltraCondensed,
465 "ExtraCondensed" => Self::ExtraCondensed,
466 "Condensed" => Self::Condensed,
467 "SemiCondensed" => Self::SemiCondensed,
468 "SemiExpanded" => Self::SemiExpanded,
469 "Expanded" => Self::Expanded,
470 "ExtraExpanded" => Self::ExtraExpanded,
471 "UltraExpanded" => Self::UltraExpanded,
472 _ => Self::Normal,
473 }
474 }
475}
476
477bitflags! {
478 #[derive(Debug)]
480 pub(crate) struct FontFlags: u32 {
481 const FIXED_PITCH = 1 << 0;
482 const SERIF = 1 << 1;
483 const SYMBOLIC = 1 << 2;
484 const SCRIPT = 1 << 3;
485 const NON_SYMBOLIC = 1 << 5;
486 const ITALIC = 1 << 6;
487 const ALL_CAP = 1 << 16;
488 const SMALL_CAP = 1 << 17;
489 const FORCE_BOLD = 1 << 18;
490 }
491}
492
493pub enum FontQuery {
495 Standard(StandardFont),
497 Fallback(FallbackFontQuery),
502}
503
504#[derive(Debug, Clone)]
506pub struct FallbackFontQuery {
507 pub post_script_name: Option<String>,
509 pub font_name: Option<String>,
511 pub font_family: Option<String>,
513 pub font_stretch: FontStretch,
515 pub font_weight: u32,
517 pub is_fixed_pitch: bool,
519 pub is_serif: bool,
521 pub is_italic: bool,
523 pub is_bold: bool,
525 pub is_small_cap: bool,
527 pub character_collection: Option<CharacterCollection>,
529}
530
531impl FallbackFontQuery {
532 pub(crate) fn new(dict: &Dict<'_>) -> Self {
533 let post_script_name = dict
534 .get::<Name<'_>>(BASE_FONT)
535 .map(|n| strip_subset_prefix(n.as_str()).to_string());
536
537 let mut data = Self {
538 post_script_name,
539 ..Default::default()
540 };
541
542 if let Some(descriptor) = dict.get::<Dict<'_>>(FONT_DESC) {
543 data.font_name = dict
544 .get::<Name<'_>>(FONT_NAME)
545 .map(|n| strip_subset_prefix(n.as_str()).to_string());
546 data.font_family = descriptor
547 .get::<Name<'_>>(FONT_FAMILY)
548 .map(|n| n.as_str().to_string());
549 data.font_stretch = descriptor
550 .get::<Name<'_>>(FONT_STRETCH)
551 .map(|n| FontStretch::from_string(n.as_str()))
552 .unwrap_or(FontStretch::Normal);
553 data.font_weight = descriptor.get::<u32>(FONT_WEIGHT).unwrap_or(400);
554
555 if let Some(flags) = descriptor
556 .get::<u32>(FLAGS)
557 .map(FontFlags::from_bits_truncate)
558 {
559 data.is_serif = flags.contains(FontFlags::SERIF);
560 data.is_italic = flags.contains(FontFlags::ITALIC);
561 data.is_small_cap = flags.contains(FontFlags::SMALL_CAP);
562 }
563 }
564
565 data.is_italic |= data
566 .post_script_name
567 .as_ref()
568 .is_some_and(|s| s.contains("Italic"));
569
570 data.is_bold |= data
571 .post_script_name
572 .as_ref()
573 .is_some_and(|s| s.contains("Bold"));
574
575 data
576 }
577
578 pub fn pick_standard_font(&self) -> StandardFont {
580 if self.is_fixed_pitch {
581 match (self.is_bold, self.is_italic) {
582 (true, true) => StandardFont::CourierBoldOblique,
583 (true, false) => StandardFont::CourierBold,
584 (false, true) => StandardFont::CourierOblique,
585 (false, false) => StandardFont::Courier,
586 }
587 } else if !self.is_serif {
588 match (self.is_bold, self.is_italic) {
589 (true, true) => StandardFont::HelveticaBoldOblique,
590 (true, false) => StandardFont::HelveticaBold,
591 (false, true) => StandardFont::HelveticaOblique,
592 (false, false) => StandardFont::Helvetica,
593 }
594 } else {
595 match (self.is_bold, self.is_italic) {
596 (true, true) => StandardFont::TimesBoldItalic,
597 (true, false) => StandardFont::TimesBold,
598 (false, true) => StandardFont::TimesItalic,
599 (false, false) => StandardFont::TimesRoman,
600 }
601 }
602 }
603}
604
605impl Default for FallbackFontQuery {
606 fn default() -> Self {
607 Self {
608 post_script_name: None,
609 font_name: None,
610 font_family: None,
611 font_stretch: FontStretch::Normal,
612 font_weight: 400,
613 is_fixed_pitch: false,
614 is_serif: false,
615 is_italic: false,
616 is_bold: false,
617 is_small_cap: false,
618 character_collection: None,
619 }
620 }
621}
622
623pub(crate) fn glyph_name_to_unicode(name: &str) -> Option<char> {
627 if let Some(unicode_str) = glyph_names::get(name) {
628 return unicode_str.chars().next();
629 }
630
631 unicode_from_name(name).or_else(|| {
632 warn!("failed to map glyph name {} to unicode", name);
633
634 None
635 })
636}
637
638pub(crate) fn unicode_from_name(name: &str) -> Option<char> {
639 let convert = |input: &str| u32::from_str_radix(input, 16).ok().and_then(char::from_u32);
640
641 name.starts_with("uni")
642 .then(|| name.get(3..).and_then(convert))
643 .or_else(|| {
644 name.starts_with("u")
645 .then(|| name.get(1..).and_then(convert))
646 })
647 .flatten()
648}
649
650pub(crate) fn read_to_unicode(dict: &Dict<'_>, cmap_resolver: &CMapResolverFn) -> Option<CMap> {
651 dict.get::<Stream<'_>>(TO_UNICODE)
652 .and_then(|s| s.decoded().ok())
653 .or_else(|| {
656 dict.get::<Name<'_>>(TO_UNICODE)
657 .and_then(|name| (cmap_resolver)(CMapName::from_bytes(name.as_ref())))
658 .map(|d| Cow::Owned(d.to_vec()))
659 })
660 .and_then(|data| {
661 let cmap_resolver = cmap_resolver.clone();
662 CMap::parse(&data, move |name| (cmap_resolver)(name))
663 })
664}
665
666pub(crate) fn normalized_glyph_name(mut name: &str) -> &str {
669 if name == "nbspace" {
670 name = "space";
671 }
672
673 if name == "sfthyphen" {
674 name = "hyphen";
675 }
676
677 name
678}