1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum FontEncoding {
7 WinAnsiEncoding,
10 MacRomanEncoding,
13 StandardEncoding,
16 MacExpertEncoding,
19 Custom(&'static str),
22}
23
24impl FontEncoding {
25 pub fn pdf_name(&self) -> &'static str {
27 match self {
28 FontEncoding::WinAnsiEncoding => "WinAnsiEncoding",
29 FontEncoding::MacRomanEncoding => "MacRomanEncoding",
30 FontEncoding::StandardEncoding => "StandardEncoding",
31 FontEncoding::MacExpertEncoding => "MacExpertEncoding",
32 FontEncoding::Custom(name) => name,
33 }
34 }
35
36 pub fn recommended_for_font(font: &Font) -> Option<Self> {
39 match font {
40 Font::Helvetica
42 | Font::HelveticaBold
43 | Font::HelveticaOblique
44 | Font::HelveticaBoldOblique
45 | Font::TimesRoman
46 | Font::TimesBold
47 | Font::TimesItalic
48 | Font::TimesBoldItalic
49 | Font::Courier
50 | Font::CourierBold
51 | Font::CourierOblique
52 | Font::CourierBoldOblique => Some(FontEncoding::WinAnsiEncoding),
53 Font::Symbol | Font::ZapfDingbats => None,
55 Font::Custom(_) => Some(FontEncoding::Custom("Identity-H")),
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Hash)]
66pub struct FontWithEncoding {
67 pub font: Font,
69 pub encoding: Option<FontEncoding>,
72}
73
74impl FontWithEncoding {
75 pub fn new(font: Font, encoding: Option<FontEncoding>) -> Self {
77 Self { font, encoding }
78 }
79
80 pub fn with_recommended_encoding(font: Font) -> Self {
82 Self {
83 font: font.clone(),
84 encoding: FontEncoding::recommended_for_font(&font),
85 }
86 }
87
88 pub fn with_encoding(font: Font, encoding: FontEncoding) -> Self {
90 Self {
91 font,
92 encoding: Some(encoding),
93 }
94 }
95
96 pub fn without_encoding(font: Font) -> Self {
98 Self {
99 font,
100 encoding: None,
101 }
102 }
103}
104
105impl From<Font> for FontWithEncoding {
107 fn from(font: Font) -> Self {
108 Self::without_encoding(font)
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Hash)]
117pub enum Font {
118 Helvetica,
121 HelveticaBold,
123 HelveticaOblique,
125 HelveticaBoldOblique,
127 TimesRoman,
129 TimesBold,
131 TimesItalic,
133 TimesBoldItalic,
135 Courier,
137 CourierBold,
139 CourierOblique,
141 CourierBoldOblique,
143 Symbol,
145 ZapfDingbats,
147 Custom(String),
149}
150
151impl Font {
152 pub fn get_metrics(&self) -> Option<&'static crate::text::fonts::StandardFontMetrics> {
154 crate::text::fonts::get_standard_font_metrics(self)
155 }
156
157 pub fn get_char_width(&self, ch: u8) -> Option<i32> {
159 self.get_metrics().map(|m| m.get_char_width(ch))
160 }
161
162 pub fn get_string_width(&self, text: &str, font_size: f64) -> Option<f64> {
164 self.get_metrics().map(|m| {
165 let width_units = m.get_string_width(text);
166 m.to_user_space(width_units, font_size)
167 })
168 }
169
170 pub fn pdf_name(&self) -> String {
172 match self {
173 Font::Helvetica => "Helvetica".to_string(),
174 Font::HelveticaBold => "Helvetica-Bold".to_string(),
175 Font::HelveticaOblique => "Helvetica-Oblique".to_string(),
176 Font::HelveticaBoldOblique => "Helvetica-BoldOblique".to_string(),
177 Font::TimesRoman => "Times-Roman".to_string(),
178 Font::TimesBold => "Times-Bold".to_string(),
179 Font::TimesItalic => "Times-Italic".to_string(),
180 Font::TimesBoldItalic => "Times-BoldItalic".to_string(),
181 Font::Courier => "Courier".to_string(),
182 Font::CourierBold => "Courier-Bold".to_string(),
183 Font::CourierOblique => "Courier-Oblique".to_string(),
184 Font::CourierBoldOblique => "Courier-BoldOblique".to_string(),
185 Font::Symbol => "Symbol".to_string(),
186 Font::ZapfDingbats => "ZapfDingbats".to_string(),
187 Font::Custom(name) => name.clone(),
188 }
189 }
190
191 pub fn is_symbolic(&self) -> bool {
193 matches!(self, Font::Symbol | Font::ZapfDingbats)
194 }
195
196 pub fn with_encoding(self, encoding: FontEncoding) -> FontWithEncoding {
198 FontWithEncoding::with_encoding(self, encoding)
199 }
200
201 pub fn with_recommended_encoding(self) -> FontWithEncoding {
203 FontWithEncoding::with_recommended_encoding(self)
204 }
205
206 pub fn without_encoding(self) -> FontWithEncoding {
208 FontWithEncoding::without_encoding(self)
209 }
210
211 pub fn is_custom(&self) -> bool {
213 matches!(self, Font::Custom(_))
214 }
215
216 pub fn custom(name: impl Into<String>) -> Self {
218 Font::Custom(name.into())
219 }
220}
221
222#[derive(Debug, Clone, Copy, PartialEq)]
223pub enum FontFamily {
224 Helvetica,
225 Times,
226 Courier,
227}
228
229impl FontFamily {
230 pub fn regular(self) -> Font {
231 match self {
232 FontFamily::Helvetica => Font::Helvetica,
233 FontFamily::Times => Font::TimesRoman,
234 FontFamily::Courier => Font::Courier,
235 }
236 }
237
238 pub fn bold(self) -> Font {
239 match self {
240 FontFamily::Helvetica => Font::HelveticaBold,
241 FontFamily::Times => Font::TimesBold,
242 FontFamily::Courier => Font::CourierBold,
243 }
244 }
245
246 pub fn italic(self) -> Font {
247 match self {
248 FontFamily::Helvetica => Font::HelveticaOblique,
249 FontFamily::Times => Font::TimesItalic,
250 FontFamily::Courier => Font::CourierOblique,
251 }
252 }
253
254 pub fn bold_italic(self) -> Font {
255 match self {
256 FontFamily::Helvetica => Font::HelveticaBoldOblique,
257 FontFamily::Times => Font::TimesBoldItalic,
258 FontFamily::Courier => Font::CourierBoldOblique,
259 }
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn test_font_pdf_names() {
269 assert_eq!(Font::Helvetica.pdf_name(), "Helvetica");
270 assert_eq!(Font::HelveticaBold.pdf_name(), "Helvetica-Bold");
271 assert_eq!(Font::HelveticaOblique.pdf_name(), "Helvetica-Oblique");
272 assert_eq!(
273 Font::HelveticaBoldOblique.pdf_name(),
274 "Helvetica-BoldOblique"
275 );
276
277 assert_eq!(Font::TimesRoman.pdf_name(), "Times-Roman");
278 assert_eq!(Font::TimesBold.pdf_name(), "Times-Bold");
279 assert_eq!(Font::TimesItalic.pdf_name(), "Times-Italic");
280 assert_eq!(Font::TimesBoldItalic.pdf_name(), "Times-BoldItalic");
281
282 assert_eq!(Font::Courier.pdf_name(), "Courier");
283 assert_eq!(Font::CourierBold.pdf_name(), "Courier-Bold");
284 assert_eq!(Font::CourierOblique.pdf_name(), "Courier-Oblique");
285 assert_eq!(Font::CourierBoldOblique.pdf_name(), "Courier-BoldOblique");
286
287 assert_eq!(Font::Symbol.pdf_name(), "Symbol");
288 assert_eq!(Font::ZapfDingbats.pdf_name(), "ZapfDingbats");
289 }
290
291 #[test]
292 fn test_font_is_symbolic() {
293 assert!(!Font::Helvetica.is_symbolic());
294 assert!(!Font::HelveticaBold.is_symbolic());
295 assert!(!Font::TimesRoman.is_symbolic());
296 assert!(!Font::Courier.is_symbolic());
297
298 assert!(Font::Symbol.is_symbolic());
299 assert!(Font::ZapfDingbats.is_symbolic());
300 }
301
302 #[test]
303 fn test_font_equality() {
304 assert_eq!(Font::Helvetica, Font::Helvetica);
305 assert_ne!(Font::Helvetica, Font::HelveticaBold);
306 assert_ne!(Font::TimesRoman, Font::TimesBold);
307 }
308
309 #[test]
310 fn test_font_debug() {
311 let font = Font::HelveticaBold;
312 let debug_str = format!("{font:?}");
313 assert_eq!(debug_str, "HelveticaBold");
314 }
315
316 #[test]
317 fn test_font_clone() {
318 let font1 = Font::TimesItalic;
319 let font2 = font1.clone();
320 assert_eq!(font1, font2);
321 }
322
323 #[test]
324 fn test_font_hash() {
325 use std::collections::HashSet;
326
327 let mut fonts = HashSet::new();
328 fonts.insert(Font::Helvetica);
329 fonts.insert(Font::HelveticaBold);
330 fonts.insert(Font::Helvetica); assert_eq!(fonts.len(), 2);
333 assert!(fonts.contains(&Font::Helvetica));
334 assert!(fonts.contains(&Font::HelveticaBold));
335 assert!(!fonts.contains(&Font::TimesRoman));
336 }
337
338 #[test]
339 fn test_font_family_regular() {
340 assert_eq!(FontFamily::Helvetica.regular(), Font::Helvetica);
341 assert_eq!(FontFamily::Times.regular(), Font::TimesRoman);
342 assert_eq!(FontFamily::Courier.regular(), Font::Courier);
343 }
344
345 #[test]
346 fn test_font_family_bold() {
347 assert_eq!(FontFamily::Helvetica.bold(), Font::HelveticaBold);
348 assert_eq!(FontFamily::Times.bold(), Font::TimesBold);
349 assert_eq!(FontFamily::Courier.bold(), Font::CourierBold);
350 }
351
352 #[test]
353 fn test_font_family_italic() {
354 assert_eq!(FontFamily::Helvetica.italic(), Font::HelveticaOblique);
355 assert_eq!(FontFamily::Times.italic(), Font::TimesItalic);
356 assert_eq!(FontFamily::Courier.italic(), Font::CourierOblique);
357 }
358
359 #[test]
360 fn test_font_family_bold_italic() {
361 assert_eq!(
362 FontFamily::Helvetica.bold_italic(),
363 Font::HelveticaBoldOblique
364 );
365 assert_eq!(FontFamily::Times.bold_italic(), Font::TimesBoldItalic);
366 assert_eq!(FontFamily::Courier.bold_italic(), Font::CourierBoldOblique);
367 }
368
369 #[test]
370 fn test_font_family_equality() {
371 assert_eq!(FontFamily::Helvetica, FontFamily::Helvetica);
372 assert_ne!(FontFamily::Helvetica, FontFamily::Times);
373 assert_ne!(FontFamily::Times, FontFamily::Courier);
374 }
375
376 #[test]
377 fn test_font_family_debug() {
378 let family = FontFamily::Times;
379 let debug_str = format!("{family:?}");
380 assert_eq!(debug_str, "Times");
381 }
382
383 #[test]
384 fn test_font_family_clone() {
385 let family1 = FontFamily::Courier;
386 let family2 = family1;
387 assert_eq!(family1, family2);
388 }
389
390 #[test]
391 fn test_font_family_copy() {
392 let family1 = FontFamily::Helvetica;
393 let family2 = family1; assert_eq!(family1, family2);
395
396 assert_eq!(family1, FontFamily::Helvetica);
398 assert_eq!(family2, FontFamily::Helvetica);
399 }
400
401 #[test]
402 fn test_all_helvetica_variants() {
403 let helvetica = FontFamily::Helvetica;
404
405 assert_eq!(helvetica.regular(), Font::Helvetica);
406 assert_eq!(helvetica.bold(), Font::HelveticaBold);
407 assert_eq!(helvetica.italic(), Font::HelveticaOblique);
408 assert_eq!(helvetica.bold_italic(), Font::HelveticaBoldOblique);
409 }
410
411 #[test]
412 fn test_all_times_variants() {
413 let times = FontFamily::Times;
414
415 assert_eq!(times.regular(), Font::TimesRoman);
416 assert_eq!(times.bold(), Font::TimesBold);
417 assert_eq!(times.italic(), Font::TimesItalic);
418 assert_eq!(times.bold_italic(), Font::TimesBoldItalic);
419 }
420
421 #[test]
422 fn test_all_courier_variants() {
423 let courier = FontFamily::Courier;
424
425 assert_eq!(courier.regular(), Font::Courier);
426 assert_eq!(courier.bold(), Font::CourierBold);
427 assert_eq!(courier.italic(), Font::CourierOblique);
428 assert_eq!(courier.bold_italic(), Font::CourierBoldOblique);
429 }
430
431 #[test]
434 fn test_font_encoding_pdf_names() {
435 assert_eq!(FontEncoding::WinAnsiEncoding.pdf_name(), "WinAnsiEncoding");
436 assert_eq!(
437 FontEncoding::MacRomanEncoding.pdf_name(),
438 "MacRomanEncoding"
439 );
440 assert_eq!(
441 FontEncoding::StandardEncoding.pdf_name(),
442 "StandardEncoding"
443 );
444 assert_eq!(
445 FontEncoding::MacExpertEncoding.pdf_name(),
446 "MacExpertEncoding"
447 );
448 assert_eq!(FontEncoding::Custom("MyEncoding").pdf_name(), "MyEncoding");
449 }
450
451 #[test]
452 fn test_font_encoding_recommended_for_font() {
453 assert_eq!(
455 FontEncoding::recommended_for_font(&Font::Helvetica),
456 Some(FontEncoding::WinAnsiEncoding)
457 );
458 assert_eq!(
459 FontEncoding::recommended_for_font(&Font::TimesRoman),
460 Some(FontEncoding::WinAnsiEncoding)
461 );
462 assert_eq!(
463 FontEncoding::recommended_for_font(&Font::CourierBold),
464 Some(FontEncoding::WinAnsiEncoding)
465 );
466
467 assert_eq!(FontEncoding::recommended_for_font(&Font::Symbol), None);
469 assert_eq!(
470 FontEncoding::recommended_for_font(&Font::ZapfDingbats),
471 None
472 );
473 }
474
475 #[test]
476 fn test_font_encoding_equality() {
477 assert_eq!(FontEncoding::WinAnsiEncoding, FontEncoding::WinAnsiEncoding);
478 assert_ne!(
479 FontEncoding::WinAnsiEncoding,
480 FontEncoding::MacRomanEncoding
481 );
482 assert_eq!(FontEncoding::Custom("Test"), FontEncoding::Custom("Test"));
483 assert_ne!(FontEncoding::Custom("Test1"), FontEncoding::Custom("Test2"));
484 }
485
486 #[test]
489 fn test_font_with_encoding_new() {
490 let font_enc = FontWithEncoding::new(Font::Helvetica, Some(FontEncoding::WinAnsiEncoding));
491 assert_eq!(font_enc.font, Font::Helvetica);
492 assert_eq!(font_enc.encoding, Some(FontEncoding::WinAnsiEncoding));
493
494 let font_no_enc = FontWithEncoding::new(Font::Symbol, None);
495 assert_eq!(font_no_enc.font, Font::Symbol);
496 assert_eq!(font_no_enc.encoding, None);
497 }
498
499 #[test]
500 fn test_font_with_encoding_with_recommended() {
501 let helvetica = FontWithEncoding::with_recommended_encoding(Font::Helvetica);
502 assert_eq!(helvetica.font, Font::Helvetica);
503 assert_eq!(helvetica.encoding, Some(FontEncoding::WinAnsiEncoding));
504
505 let symbol = FontWithEncoding::with_recommended_encoding(Font::Symbol);
506 assert_eq!(symbol.font, Font::Symbol);
507 assert_eq!(symbol.encoding, None);
508 }
509
510 #[test]
511 fn test_font_with_encoding_with_specific() {
512 let font_enc =
513 FontWithEncoding::with_encoding(Font::TimesRoman, FontEncoding::MacRomanEncoding);
514 assert_eq!(font_enc.font, Font::TimesRoman);
515 assert_eq!(font_enc.encoding, Some(FontEncoding::MacRomanEncoding));
516 }
517
518 #[test]
519 fn test_font_with_encoding_without_encoding() {
520 let font_no_enc = FontWithEncoding::without_encoding(Font::Courier);
521 assert_eq!(font_no_enc.font, Font::Courier);
522 assert_eq!(font_no_enc.encoding, None);
523 }
524
525 #[test]
526 fn test_font_with_encoding_from_font() {
527 let font_enc: FontWithEncoding = Font::HelveticaBold.into();
528 assert_eq!(font_enc.font, Font::HelveticaBold);
529 assert_eq!(font_enc.encoding, None);
530 }
531
532 #[test]
533 fn test_font_convenience_methods() {
534 let helvetica_with_enc = Font::Helvetica.with_encoding(FontEncoding::MacRomanEncoding);
535 assert_eq!(helvetica_with_enc.font, Font::Helvetica);
536 assert_eq!(
537 helvetica_with_enc.encoding,
538 Some(FontEncoding::MacRomanEncoding)
539 );
540
541 let times_recommended = Font::TimesRoman.with_recommended_encoding();
542 assert_eq!(times_recommended.font, Font::TimesRoman);
543 assert_eq!(
544 times_recommended.encoding,
545 Some(FontEncoding::WinAnsiEncoding)
546 );
547
548 let courier_no_enc = Font::Courier.without_encoding();
549 assert_eq!(courier_no_enc.font, Font::Courier);
550 assert_eq!(courier_no_enc.encoding, None);
551 }
552
553 #[test]
554 fn test_font_with_encoding_equality() {
555 let font1 = FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::WinAnsiEncoding);
556 let font2 = FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::WinAnsiEncoding);
557 let font3 =
558 FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::MacRomanEncoding);
559 let font4 =
560 FontWithEncoding::with_encoding(Font::TimesRoman, FontEncoding::WinAnsiEncoding);
561
562 assert_eq!(font1, font2);
563 assert_ne!(font1, font3);
564 assert_ne!(font1, font4);
565 }
566
567 #[test]
568 fn test_font_with_encoding_debug() {
569 let font_enc =
570 FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::WinAnsiEncoding);
571 let debug_str = format!("{font_enc:?}");
572 assert!(debug_str.contains("Helvetica"));
573 assert!(debug_str.contains("WinAnsiEncoding"));
574 }
575
576 #[test]
577 fn test_font_with_encoding_clone() {
578 let font1 =
579 FontWithEncoding::with_encoding(Font::TimesRoman, FontEncoding::StandardEncoding);
580 let font2 = font1.clone();
581 assert_eq!(font1, font2);
582 }
583
584 #[test]
585 fn test_font_with_encoding_copy() {
586 let font1 = FontWithEncoding::with_encoding(Font::Courier, FontEncoding::WinAnsiEncoding);
587 let font2 = font1.clone(); assert_eq!(font1, font2);
589
590 assert_eq!(font1.font, Font::Courier);
592 assert_eq!(font2.font, Font::Courier);
593 }
594
595 #[test]
596 fn test_custom_encoding() {
597 let custom_enc = FontEncoding::Custom("MyCustomEncoding");
598 assert_eq!(custom_enc.pdf_name(), "MyCustomEncoding");
599
600 let font_with_custom = FontWithEncoding::with_encoding(Font::Helvetica, custom_enc);
601 assert_eq!(font_with_custom.encoding, Some(custom_enc));
602 }
603}