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