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
110pub struct PositionedGlyph<'a> {
112 pub(crate) glyph: Glyph<'a>,
113 pub(crate) transform: Affine,
114}
115
116impl PositionedGlyph<'_> {
117 pub fn transform(&self) -> Affine {
119 self.transform
120 }
121}
122
123impl<'a> Deref for PositionedGlyph<'a> {
124 type Target = Glyph<'a>;
125
126 fn deref(&self) -> &Self::Target {
127 &self.glyph
128 }
129}
130
131pub struct GlyphRun<'run, 'a> {
133 pub(crate) glyphs: &'run [PositionedGlyph<'a>],
134}
135
136impl<'run, 'a> GlyphRun<'run, 'a> {
137 pub fn glyphs(&self) -> &'run [PositionedGlyph<'a>] {
139 self.glyphs
140 }
141}
142
143#[derive(Clone, Debug)]
145pub struct GlyphIdentifier {
146 id: GlyphId,
147 font: OutlineFont,
148}
149
150impl CacheKey for GlyphIdentifier {
151 fn cache_key(&self) -> u128 {
152 hash128(&(self.id, self.font.cache_key()))
153 }
154}
155
156#[derive(Clone, Debug)]
158pub struct OutlineGlyph {
159 pub(crate) id: GlyphId,
160 pub(crate) font: OutlineFont,
161 pub(crate) char_code: u32,
162}
163
164impl OutlineGlyph {
165 pub fn outline(&self) -> BezPath {
167 self.font.outline_glyph(self.id, self.char_code)
168 }
169
170 pub fn identifier(&self) -> GlyphIdentifier {
176 GlyphIdentifier {
177 id: self.id,
178 font: self.font.clone(),
179 }
180 }
181
182 pub fn as_unicode(&self) -> Option<BfString> {
186 self.font.char_code_to_unicode(self.char_code)
187 }
188
189 pub fn font_data(&self) -> Option<OutlineFontData> {
193 self.font.font_data()
194 }
195
196 pub fn glyph_id(&self) -> GlyphId {
198 self.id
199 }
200
201 pub fn advance_width(&self) -> Option<f32> {
206 self.font.glyph_advance_width(self.char_code)
207 }
208
209 pub fn font_cache_key(&self) -> u128 {
214 self.font.cache_key()
215 }
216}
217
218#[derive(Clone)]
220pub struct Type3Glyph<'a> {
221 pub(crate) font: Rc<Type3<'a>>,
222 pub(crate) glyph_id: GlyphId,
223 pub(crate) state: State<'a>,
224 pub(crate) parent_resources: Resources<'a>,
225 pub(crate) cache: InterpreterCache<'a>,
226 pub(crate) xref: &'a XRef,
227 pub(crate) settings: InterpreterSettings,
228 pub(crate) nesting_depth: u32,
229 pub(crate) char_code: u32,
230}
231
232impl<'a> Type3Glyph<'a> {
234 pub fn interpret(
236 &self,
237 device: &mut impl Device<'a>,
238 transform: Affine,
239 glyph_transform: Affine,
240 paint: &Paint<'a>,
241 ) {
242 self.font
243 .render_glyph(self, transform, glyph_transform, paint, device);
244 }
245
246 pub fn as_unicode(&self) -> Option<BfString> {
250 self.font.char_code_to_unicode(self.char_code)
251 }
252}
253
254impl CacheKey for Type3Glyph<'_> {
255 fn cache_key(&self) -> u128 {
256 hash128(&(self.font.cache_key(), self.glyph_id))
257 }
258}
259
260#[derive(Clone, Debug)]
261pub(crate) struct Font<'a>(u128, FontType<'a>);
262
263impl<'a> Font<'a> {
264 pub(crate) fn new(
265 dict: &Dict<'a>,
266 font_resolver: &FontResolverFn,
267 cmap_resolver: &CMapResolverFn,
268 ) -> Option<Self> {
269 let f_type = match dict.get::<Name<'_>>(SUBTYPE)?.deref() {
270 TYPE1 | MM_TYPE1 => {
271 FontType::Type1(Rc::new(Type1Font::new(dict, font_resolver, cmap_resolver)?))
272 }
273 TRUE_TYPE | OPEN_TYPE => FontType::TrueType(Rc::new(TrueTypeFont::new(
275 dict,
276 font_resolver,
277 cmap_resolver,
278 )?)),
279 TYPE0 => FontType::Type0(Rc::new(Type0Font::new(dict, font_resolver, cmap_resolver)?)),
280 TYPE3 => FontType::Type3(Rc::new(Type3::new(dict, cmap_resolver)?)),
281 f => {
282 warn!(
283 "unimplemented font type {:?}",
284 std::str::from_utf8(f).unwrap_or("unknown type")
285 );
286
287 return None;
288 }
289 };
290
291 let cache_key = dict.cache_key();
292
293 Some(Self(cache_key, f_type))
294 }
295
296 pub(crate) fn new_standard(
297 standard_font: StandardFont,
298 font_resolver: &FontResolverFn,
299 ) -> Option<Self> {
300 let font = Type1Font::new_standard(standard_font, font_resolver)?;
301
302 Some(Self(0, FontType::Type1(Rc::new(font))))
303 }
304
305 pub(crate) fn map_code(&self, code: u32) -> GlyphId {
306 match &self.1 {
307 FontType::Type1(f) => {
308 debug_assert!(code <= u8::MAX as u32);
309
310 f.map_code(code as u8)
311 }
312 FontType::TrueType(t) => {
313 debug_assert!(code <= u8::MAX as u32);
314
315 t.map_code(code as u8)
316 }
317 FontType::Type0(t) => t.map_code(code),
318 FontType::Type3(t) => {
319 debug_assert!(code <= u8::MAX as u32);
320
321 t.map_code(code as u8)
322 }
323 }
324 }
325
326 pub(crate) fn get_glyph(
327 &self,
328 glyph: GlyphId,
329 char_code: u32,
330 ctx: &mut Context<'a>,
331 resources: &Resources<'a>,
332 ) -> Glyph<'a> {
333 match &self.1 {
334 FontType::Type1(t) => {
335 let font = OutlineFont::Type1(t.clone());
336 Glyph::Outline(OutlineGlyph {
337 id: glyph,
338 font,
339 char_code,
340 })
341 }
342 FontType::TrueType(t) => {
343 let font = OutlineFont::TrueType(t.clone());
344 Glyph::Outline(OutlineGlyph {
345 id: glyph,
346 font,
347 char_code,
348 })
349 }
350 FontType::Type0(t) => {
351 let font = OutlineFont::Type0(t.clone());
352 Glyph::Outline(OutlineGlyph {
353 id: glyph,
354 font,
355 char_code,
356 })
357 }
358 FontType::Type3(t) => {
359 let nesting_depth = ctx.nesting_depth() + 1;
360 let shape_glyph = Type3Glyph {
361 font: t.clone(),
362 glyph_id: glyph,
363 state: ctx.get().clone(),
364 parent_resources: resources.clone(),
365 cache: ctx.interpreter_cache.clone(),
366 xref: ctx.xref,
367 settings: ctx.settings.clone(),
368 nesting_depth,
369 char_code,
370 };
371
372 Glyph::Type3(Box::new(shape_glyph))
373 }
374 }
375 }
376
377 pub(crate) fn code_advance(&self, code: u32) -> Vec2 {
378 match &self.1 {
379 FontType::Type1(t) => {
380 debug_assert!(code <= u8::MAX as u32);
381
382 Vec2::new(t.glyph_width(code as u8).unwrap_or(0.0) as f64, 0.0)
383 }
384 FontType::TrueType(t) => {
385 debug_assert!(code <= u8::MAX as u32);
386
387 Vec2::new(t.glyph_width(code as u8) as f64, 0.0)
388 }
389 FontType::Type0(t) => t.code_advance(code),
390 FontType::Type3(t) => {
391 debug_assert!(code <= u8::MAX as u32);
392
393 Vec2::new(t.glyph_width(code as u8) as f64, 0.0)
394 }
395 }
396 }
397
398 pub(crate) fn origin_displacement(&self, code: u32) -> Vec2 {
399 match &self.1 {
400 FontType::Type1(_) => Vec2::default(),
401 FontType::TrueType(_) => Vec2::default(),
402 FontType::Type0(t) => t.origin_displacement(code),
403 FontType::Type3(_) => Vec2::default(),
404 }
405 }
406
407 pub(crate) fn read_code(&self, bytes: &[u8], offset: usize) -> (u32, usize) {
408 match &self.1 {
409 FontType::Type1(_) => (bytes[offset] as u32, 1),
410 FontType::TrueType(_) => (bytes[offset] as u32, 1),
411 FontType::Type0(t) => t.read_code(bytes, offset),
412 FontType::Type3(_) => (bytes[offset] as u32, 1),
413 }
414 }
415
416 pub(crate) fn is_horizontal(&self) -> bool {
417 match &self.1 {
418 FontType::Type1(_) => true,
419 FontType::TrueType(_) => true,
420 FontType::Type0(t) => t.is_horizontal(),
421 FontType::Type3(_) => true,
422 }
423 }
424}
425
426impl CacheKey for Font<'_> {
427 fn cache_key(&self) -> u128 {
428 self.0
429 }
430}
431
432#[derive(Clone, Debug)]
433enum FontType<'a> {
434 Type1(Rc<Type1Font>),
435 TrueType(Rc<TrueTypeFont>),
436 Type0(Rc<Type0Font>),
437 Type3(Rc<Type3<'a>>),
438}
439
440#[derive(Debug)]
441enum Encoding {
442 Standard,
443 MacRoman,
444 WinAnsi,
445 MacExpert,
446 BuiltIn,
447}
448
449impl Encoding {
450 fn map_code(&self, code: u8) -> Option<&'static str> {
451 if code == 0 {
452 return Some(".notdef");
453 }
454 match self {
455 Self::Standard => standard::get(code),
456 Self::MacRoman => mac_roman::get(code).or_else(|| mac_os_roman::get(code)),
457 Self::WinAnsi => win_ansi::get(code),
458 Self::MacExpert => mac_expert::get(code),
459 Self::BuiltIn => None,
460 }
461 }
462}
463
464#[derive(Debug, Copy, Clone)]
466pub enum FontStretch {
467 Normal,
469 UltraCondensed,
471 ExtraCondensed,
473 Condensed,
475 SemiCondensed,
477 SemiExpanded,
479 Expanded,
481 ExtraExpanded,
483 UltraExpanded,
485}
486
487impl FontStretch {
488 fn from_string(s: &str) -> Self {
489 match s {
490 "UltraCondensed" => Self::UltraCondensed,
491 "ExtraCondensed" => Self::ExtraCondensed,
492 "Condensed" => Self::Condensed,
493 "SemiCondensed" => Self::SemiCondensed,
494 "SemiExpanded" => Self::SemiExpanded,
495 "Expanded" => Self::Expanded,
496 "ExtraExpanded" => Self::ExtraExpanded,
497 "UltraExpanded" => Self::UltraExpanded,
498 _ => Self::Normal,
499 }
500 }
501}
502
503bitflags! {
504 #[derive(Debug)]
506 pub(crate) struct FontFlags: u32 {
507 const FIXED_PITCH = 1 << 0;
508 const SERIF = 1 << 1;
509 const SYMBOLIC = 1 << 2;
510 const SCRIPT = 1 << 3;
511 const NON_SYMBOLIC = 1 << 5;
512 const ITALIC = 1 << 6;
513 const ALL_CAP = 1 << 16;
514 const SMALL_CAP = 1 << 17;
515 const FORCE_BOLD = 1 << 18;
516 }
517}
518
519pub enum FontQuery {
521 Standard(StandardFont),
523 Fallback(FallbackFontQuery),
528}
529
530#[derive(Debug, Clone)]
532pub struct FallbackFontQuery {
533 pub post_script_name: Option<String>,
535 pub font_name: Option<String>,
537 pub font_family: Option<String>,
539 pub font_stretch: FontStretch,
541 pub font_weight: u32,
543 pub is_fixed_pitch: bool,
545 pub is_serif: bool,
547 pub is_italic: bool,
549 pub is_bold: bool,
551 pub is_small_cap: bool,
553 pub character_collection: Option<CharacterCollection>,
555}
556
557impl FallbackFontQuery {
558 pub(crate) fn new(dict: &Dict<'_>) -> Self {
559 let post_script_name = dict
560 .get::<Name<'_>>(BASE_FONT)
561 .map(|n| strip_subset_prefix(n.as_str()).to_string());
562
563 let mut data = Self {
564 post_script_name,
565 ..Default::default()
566 };
567
568 if let Some(descriptor) = dict.get::<Dict<'_>>(FONT_DESC) {
569 data.font_name = dict
570 .get::<Name<'_>>(FONT_NAME)
571 .map(|n| strip_subset_prefix(n.as_str()).to_string());
572 data.font_family = descriptor
573 .get::<Name<'_>>(FONT_FAMILY)
574 .map(|n| n.as_str().to_string());
575 data.font_stretch = descriptor
576 .get::<Name<'_>>(FONT_STRETCH)
577 .map(|n| FontStretch::from_string(n.as_str()))
578 .unwrap_or(FontStretch::Normal);
579 data.font_weight = descriptor.get::<u32>(FONT_WEIGHT).unwrap_or(400);
580
581 if let Some(flags) = descriptor
582 .get::<u32>(FLAGS)
583 .map(FontFlags::from_bits_truncate)
584 {
585 data.is_serif = flags.contains(FontFlags::SERIF);
586 data.is_italic = flags.contains(FontFlags::ITALIC);
587 data.is_small_cap = flags.contains(FontFlags::SMALL_CAP);
588 }
589 }
590
591 data.is_italic |= data
592 .post_script_name
593 .as_ref()
594 .is_some_and(|s| s.contains("Italic"));
595
596 data.is_bold |= data
597 .post_script_name
598 .as_ref()
599 .is_some_and(|s| s.contains("Bold"));
600
601 data
602 }
603
604 pub fn pick_standard_font(&self) -> StandardFont {
606 if self.is_fixed_pitch {
607 match (self.is_bold, self.is_italic) {
608 (true, true) => StandardFont::CourierBoldOblique,
609 (true, false) => StandardFont::CourierBold,
610 (false, true) => StandardFont::CourierOblique,
611 (false, false) => StandardFont::Courier,
612 }
613 } else if !self.is_serif {
614 match (self.is_bold, self.is_italic) {
615 (true, true) => StandardFont::HelveticaBoldOblique,
616 (true, false) => StandardFont::HelveticaBold,
617 (false, true) => StandardFont::HelveticaOblique,
618 (false, false) => StandardFont::Helvetica,
619 }
620 } else {
621 match (self.is_bold, self.is_italic) {
622 (true, true) => StandardFont::TimesBoldItalic,
623 (true, false) => StandardFont::TimesBold,
624 (false, true) => StandardFont::TimesItalic,
625 (false, false) => StandardFont::TimesRoman,
626 }
627 }
628 }
629}
630
631impl Default for FallbackFontQuery {
632 fn default() -> Self {
633 Self {
634 post_script_name: None,
635 font_name: None,
636 font_family: None,
637 font_stretch: FontStretch::Normal,
638 font_weight: 400,
639 is_fixed_pitch: false,
640 is_serif: false,
641 is_italic: false,
642 is_bold: false,
643 is_small_cap: false,
644 character_collection: None,
645 }
646 }
647}
648
649pub(crate) fn glyph_name_to_unicode(name: &str) -> Option<char> {
653 if let Some(unicode_str) = glyph_names::get(name) {
654 return unicode_str.chars().next();
655 }
656
657 unicode_from_name(name).or_else(|| {
658 warn!("failed to map glyph name {} to unicode", name);
659
660 None
661 })
662}
663
664pub(crate) fn unicode_from_name(name: &str) -> Option<char> {
665 let convert = |input: &str| u32::from_str_radix(input, 16).ok().and_then(char::from_u32);
666
667 name.starts_with("uni")
668 .then(|| name.get(3..).and_then(convert))
669 .or_else(|| {
670 name.starts_with("u")
671 .then(|| name.get(1..).and_then(convert))
672 })
673 .flatten()
674}
675
676pub(crate) fn read_to_unicode(dict: &Dict<'_>, cmap_resolver: &CMapResolverFn) -> Option<CMap> {
677 dict.get::<Stream<'_>>(TO_UNICODE)
678 .and_then(|s| s.decoded().ok())
679 .or_else(|| {
682 dict.get::<Name<'_>>(TO_UNICODE)
683 .and_then(|name| (cmap_resolver)(CMapName::from_bytes(name.as_ref())))
684 .map(|d| Cow::Owned(d.to_vec()))
685 })
686 .and_then(|data| {
687 let cmap_resolver = cmap_resolver.clone();
688 CMap::parse(&data, move |name| (cmap_resolver)(name))
689 })
690}
691
692pub(crate) fn normalized_glyph_name(mut name: &str) -> &str {
695 if name == "nbspace" {
696 name = "space";
697 }
698
699 if name == "sfthyphen" {
700 name = "hyphen";
701 }
702
703 name
704}