1use crate::font::FontData;
2use crate::font::blob::{CffFontBlob, OpenTypeFontBlob};
3use crate::font::generated::{metrics, standard, symbol, zapf_dings};
4use hayro_syntax::object::Dict;
5use hayro_syntax::object::Name;
6use hayro_syntax::object::dict::keys::BASE_FONT;
7use kurbo::BezPath;
8use skrifa::GlyphId16;
9use skrifa::raw::TableProvider;
10use std::collections::HashMap;
11use std::ops::Deref;
12
13#[derive(Copy, Clone, Debug)]
15pub enum StandardFont {
16 Helvetica,
18 HelveticaBold,
20 HelveticaOblique,
22 HelveticaBoldOblique,
24 Courier,
26 CourierBold,
28 CourierOblique,
30 CourierBoldOblique,
32 TimesRoman,
34 TimesBold,
36 TimesItalic,
38 TimesBoldItalic,
40 ZapfDingBats,
42 Symbol,
44}
45
46impl StandardFont {
47 pub(crate) fn code_to_name(&self, code: u8) -> Option<&'static str> {
48 match self {
49 Self::Symbol => symbol::get(code),
50 Self::ZapfDingBats => zapf_dings::get(code),
53 _ => standard::get(code),
54 }
55 }
56
57 pub(crate) fn get_width(&self, mut name: &str) -> Option<f32> {
58 if name == ".notdef" {
60 return Some(250.0);
61 }
62
63 if name == "nbspace" {
64 name = "space";
65 }
66
67 if name == "sfthyphen" {
68 name = "hyphen";
69 }
70
71 match self {
72 Self::Helvetica => metrics::HELVETICA.get(name).copied(),
73 Self::HelveticaBold => metrics::HELVETICA_BOLD.get(name).copied(),
74 Self::HelveticaOblique => metrics::HELVETICA_OBLIQUE.get(name).copied(),
75 Self::HelveticaBoldOblique => metrics::HELVETICA_BOLD_OBLIQUE.get(name).copied(),
76 Self::Courier => metrics::COURIER.get(name).copied(),
77 Self::CourierBold => metrics::COURIER_BOLD.get(name).copied(),
78 Self::CourierOblique => metrics::COURIER_OBLIQUE.get(name).copied(),
79 Self::CourierBoldOblique => metrics::COURIER_BOLD_OBLIQUE.get(name).copied(),
80 Self::TimesRoman => metrics::TIMES_ROMAN.get(name).copied(),
81 Self::TimesBold => metrics::TIMES_BOLD.get(name).copied(),
82 Self::TimesItalic => metrics::TIMES_ITALIC.get(name).copied(),
83 Self::TimesBoldItalic => metrics::TIMES_BOLD_ITALIC.get(name).copied(),
84 Self::ZapfDingBats => metrics::ZAPF_DING_BATS.get(name).copied(),
85 Self::Symbol => metrics::SYMBOL.get(name).copied(),
86 }
87 }
88
89 pub(crate) fn as_str(&self) -> &'static str {
90 match self {
91 Self::Helvetica => "Helvetica",
92 Self::HelveticaBold => "Helvetica Bold",
93 Self::HelveticaOblique => "Helvetica Oblique",
94 Self::HelveticaBoldOblique => "Helvetica Bold Oblique",
95 Self::Courier => "Courier",
96 Self::CourierBold => "Courier Bold",
97 Self::CourierOblique => "Courier Oblique",
98 Self::CourierBoldOblique => "Courier Bold Oblique",
99 Self::TimesRoman => "Times Roman",
100 Self::TimesBold => "Times Bold",
101 Self::TimesItalic => "Times Italic",
102 Self::TimesBoldItalic => "Times Bold Italic",
103 Self::ZapfDingBats => "Zapf Dingbats",
104 Self::Symbol => "Symbol",
105 }
106 }
107
108 pub fn postscript_name(&self) -> &'static str {
110 match self {
111 Self::Helvetica => "Helvetica",
112 Self::HelveticaBold => "Helvetica-Bold",
113 Self::HelveticaOblique => "Helvetica-Oblique",
114 Self::HelveticaBoldOblique => "Helvetica-BoldOblique",
115 Self::Courier => "Courier",
116 Self::CourierBold => "Courier-Bold",
117 Self::CourierOblique => "Courier-Oblique",
118 Self::CourierBoldOblique => "Courier-BoldOblique",
119 Self::TimesRoman => "Times-Roman",
120 Self::TimesBold => "Times-Bold",
121 Self::TimesItalic => "Times-Italic",
122 Self::TimesBoldItalic => "Times-BoldItalic",
123 Self::ZapfDingBats => "ZapfDingbats",
124 Self::Symbol => "Symbol",
125 }
126 }
127
128 #[cfg(feature = "embed-fonts")]
137 pub fn get_font_data(&self) -> (FontData, u32) {
138 use std::sync::Arc;
139
140 let data = match self {
141 Self::Helvetica => &include_bytes!("../../assets/FoxitSans.pfb")[..],
142 Self::HelveticaBold => &include_bytes!("../../assets/FoxitSansBold.pfb")[..],
143 Self::HelveticaOblique => &include_bytes!("../../assets/FoxitSansItalic.pfb")[..],
144 Self::HelveticaBoldOblique => {
145 &include_bytes!("../../assets/FoxitSansBoldItalic.pfb")[..]
146 }
147 Self::Courier => &include_bytes!("../../assets/FoxitFixed.pfb")[..],
148 Self::CourierBold => &include_bytes!("../../assets/FoxitFixedBold.pfb")[..],
149 Self::CourierOblique => &include_bytes!("../../assets/FoxitFixedItalic.pfb")[..],
150 Self::CourierBoldOblique => {
151 &include_bytes!("../../assets/FoxitFixedBoldItalic.pfb")[..]
152 }
153 Self::TimesRoman => &include_bytes!("../../assets/FoxitSerif.pfb")[..],
154 Self::TimesBold => &include_bytes!("../../assets/FoxitSerifBold.pfb")[..],
155 Self::TimesItalic => &include_bytes!("../../assets/FoxitSerifItalic.pfb")[..],
156 Self::TimesBoldItalic => &include_bytes!("../../assets/FoxitSerifBoldItalic.pfb")[..],
157 Self::ZapfDingBats => &include_bytes!("../../assets/FoxitDingbats.pfb")[..],
158 Self::Symbol => {
159 include_bytes!("../../assets/FoxitSymbol.pfb")
160 }
161 };
162
163 (Arc::new(data), 0)
164 }
165}
166
167pub(crate) fn select_standard_font(dict: &Dict<'_>) -> Option<StandardFont> {
168 match dict.get::<Name<'_>>(BASE_FONT)?.deref() {
170 b"Helvetica" | b"ArialMT" | b"Arial" | b"LiberationSans" | b"NimbusSanL-Regu" => {
171 Some(StandardFont::Helvetica)
172 }
173 b"Helvetica-Bold"
174 | b"Arial-BoldMT"
175 | b"Arial-Bold"
176 | b"Arial,Bold"
177 | b"LiberationSans-Bold"
178 | b"NimbusSanL-Bold" => Some(StandardFont::HelveticaBold),
179 b"Helvetica-Oblique"
180 | b"Arial-ItalicMT"
181 | b"Arial-Italic"
182 | b"Helvetica-Italic"
183 | b"LiberationSans-Italic"
184 | b"NimbusSanL-ReguItal" => Some(StandardFont::HelveticaOblique),
185 b"Helvetica-BoldOblique"
186 | b"Arial-BoldItalicMT"
187 | b"Helvetica-BoldItalic"
188 | b"LiberationSans-BoldItalic"
189 | b"NimbusSanL-BoldItal" => Some(StandardFont::HelveticaBoldOblique),
190 b"Courier" | b"CourierNew" | b"CourierNewPSMT" | b"LiberationMono" | b"NimbusMonL-Regu" => {
191 Some(StandardFont::Courier)
192 }
193 b"Courier-Bold"
194 | b"CourierNewPS-BoldMT"
195 | b"CourierNew-Bold"
196 | b"LiberationMono-Bold"
197 | b"NimbusMonL-Bold" => Some(StandardFont::CourierBold),
198 b"Courier-Oblique"
199 | b"CourierNewPS-ItalicMT"
200 | b"CourierNew-Italic"
201 | b"LiberationMono-Italic"
202 | b"NimbusMonL-ReguObli" => Some(StandardFont::CourierOblique),
203 b"Courier-BoldOblique"
204 | b"CourierNewPS-BoldItalicMT"
205 | b"CourierNew-BoldItalic"
206 | b"LiberationMono-BoldItalic"
207 | b"NimbusMonL-BoldObli" => Some(StandardFont::CourierBoldOblique),
208 b"Times-Roman"
209 | b"Times New Roman"
210 | b"TimesNewRomanPSMT"
211 | b"TimesNewRoman"
212 | b"TimesNewRomanPS"
213 | b"LiberationSerif"
214 | b"NimbusRomNo9L-Regu" => Some(StandardFont::TimesRoman),
215 b"Times-Bold"
216 | b"TimesNewRomanPS-BoldMT"
217 | b"TimesNewRomanPS-Bold"
218 | b"TimesNewRoman-Bold"
219 | b"LiberationSerif-Bold"
220 | b"NimbusRomNo9L-Medi" => Some(StandardFont::TimesBold),
221 b"Times-Italic"
222 | b"TimesNewRomanPS-ItalicMT"
223 | b"TimesNewRomanPS-Italic"
224 | b"TimesNewRoman-Italic"
225 | b"LiberationSerif-Italic"
226 | b"NimbusRomNo9L-ReguItal" => Some(StandardFont::TimesItalic),
227 b"Times-BoldItalic"
228 | b"TimesNewRomanPS-BoldItalicMT"
229 | b"TimesNewRomanPS-BoldItalic"
230 | b"TimesNewRoman-BoldItalic"
231 | b"LiberationSerif-BoldItalic"
232 | b"NimbusRomNo9L-MediItal" => Some(StandardFont::TimesBoldItalic),
233 b"Symbol" | b"SymbolMT" | b"StandardSymL" => Some(StandardFont::Symbol),
234 b"ZapfDingbats"
235 | b"ZapfDingbatsITCbyBT-Regular"
236 | b"ZapfDingbatsITC"
237 | b"Dingbats"
238 | b"MS-Gothic" => Some(StandardFont::ZapfDingBats),
239 _ => None,
240 }
241}
242
243#[derive(Debug)]
244pub(crate) enum StandardFontBlob {
245 Cff(CffFontBlob),
246 Otf(OpenTypeFontBlob, HashMap<String, skrifa::GlyphId>),
247}
248
249impl StandardFontBlob {
250 pub(crate) fn from_data(data: FontData, index: u32) -> Option<Self> {
251 if let Some(blob) = CffFontBlob::new(data.clone()) {
252 Some(Self::new_cff(blob))
253 } else {
254 OpenTypeFontBlob::new(data, index).map(Self::new_otf)
255 }
256 }
257
258 pub(crate) fn new_cff(blob: CffFontBlob) -> Self {
259 Self::Cff(blob)
260 }
261
262 pub(crate) fn new_otf(blob: OpenTypeFontBlob) -> Self {
263 let mut glyph_names = HashMap::new();
264
265 if let Ok(post) = blob.font_ref().post() {
266 for i in 0..blob.num_glyphs() {
267 if let Some(str) = post.glyph_name(GlyphId16::new(i)) {
268 glyph_names.insert(str.to_string(), skrifa::GlyphId::new(i as u32));
269 }
270 }
271 }
272
273 Self::Otf(blob, glyph_names)
274 }
275}
276
277impl StandardFontBlob {
278 pub(crate) fn name_to_glyph(&self, name: &str) -> Option<skrifa::GlyphId> {
279 match self {
280 Self::Cff(blob) => blob
281 .table()
282 .glyph_index_by_name(name)
283 .map(|g| skrifa::GlyphId::new(g.0 as u32)),
284 Self::Otf(_, glyph_names) => glyph_names.get(name).copied(),
285 }
286 }
287
288 pub(crate) fn unicode_to_glyph(&self, code: u32) -> Option<skrifa::GlyphId> {
289 match self {
290 Self::Cff(_) => None,
291 Self::Otf(blob, _) => blob
292 .font_ref()
293 .cmap()
294 .ok()
295 .and_then(|c| c.map_codepoint(code)),
296 }
297 }
298
299 pub(crate) fn outline_glyph(&self, glyph: skrifa::GlyphId) -> BezPath {
300 if glyph == skrifa::GlyphId::NOTDEF {
303 return BezPath::new();
304 }
305
306 match self {
307 Self::Cff(blob) => blob.outline_glyph(glyph),
308 Self::Otf(blob, _) => blob.outline_glyph(glyph),
309 }
310 }
311}