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