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 pdf_name(&self) -> String {
154 match self {
155 Font::Helvetica => "Helvetica".to_string(),
156 Font::HelveticaBold => "Helvetica-Bold".to_string(),
157 Font::HelveticaOblique => "Helvetica-Oblique".to_string(),
158 Font::HelveticaBoldOblique => "Helvetica-BoldOblique".to_string(),
159 Font::TimesRoman => "Times-Roman".to_string(),
160 Font::TimesBold => "Times-Bold".to_string(),
161 Font::TimesItalic => "Times-Italic".to_string(),
162 Font::TimesBoldItalic => "Times-BoldItalic".to_string(),
163 Font::Courier => "Courier".to_string(),
164 Font::CourierBold => "Courier-Bold".to_string(),
165 Font::CourierOblique => "Courier-Oblique".to_string(),
166 Font::CourierBoldOblique => "Courier-BoldOblique".to_string(),
167 Font::Symbol => "Symbol".to_string(),
168 Font::ZapfDingbats => "ZapfDingbats".to_string(),
169 Font::Custom(name) => name.clone(),
170 }
171 }
172
173 pub fn is_symbolic(&self) -> bool {
175 matches!(self, Font::Symbol | Font::ZapfDingbats)
176 }
177
178 pub fn with_encoding(self, encoding: FontEncoding) -> FontWithEncoding {
180 FontWithEncoding::with_encoding(self, encoding)
181 }
182
183 pub fn with_recommended_encoding(self) -> FontWithEncoding {
185 FontWithEncoding::with_recommended_encoding(self)
186 }
187
188 pub fn without_encoding(self) -> FontWithEncoding {
190 FontWithEncoding::without_encoding(self)
191 }
192
193 pub fn is_custom(&self) -> bool {
195 matches!(self, Font::Custom(_))
196 }
197
198 pub fn custom(name: impl Into<String>) -> Self {
200 Font::Custom(name.into())
201 }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq)]
205pub enum FontFamily {
206 Helvetica,
207 Times,
208 Courier,
209}
210
211impl FontFamily {
212 pub fn regular(self) -> Font {
213 match self {
214 FontFamily::Helvetica => Font::Helvetica,
215 FontFamily::Times => Font::TimesRoman,
216 FontFamily::Courier => Font::Courier,
217 }
218 }
219
220 pub fn bold(self) -> Font {
221 match self {
222 FontFamily::Helvetica => Font::HelveticaBold,
223 FontFamily::Times => Font::TimesBold,
224 FontFamily::Courier => Font::CourierBold,
225 }
226 }
227
228 pub fn italic(self) -> Font {
229 match self {
230 FontFamily::Helvetica => Font::HelveticaOblique,
231 FontFamily::Times => Font::TimesItalic,
232 FontFamily::Courier => Font::CourierOblique,
233 }
234 }
235
236 pub fn bold_italic(self) -> Font {
237 match self {
238 FontFamily::Helvetica => Font::HelveticaBoldOblique,
239 FontFamily::Times => Font::TimesBoldItalic,
240 FontFamily::Courier => Font::CourierBoldOblique,
241 }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_font_pdf_names() {
251 assert_eq!(Font::Helvetica.pdf_name(), "Helvetica");
252 assert_eq!(Font::HelveticaBold.pdf_name(), "Helvetica-Bold");
253 assert_eq!(Font::HelveticaOblique.pdf_name(), "Helvetica-Oblique");
254 assert_eq!(
255 Font::HelveticaBoldOblique.pdf_name(),
256 "Helvetica-BoldOblique"
257 );
258
259 assert_eq!(Font::TimesRoman.pdf_name(), "Times-Roman");
260 assert_eq!(Font::TimesBold.pdf_name(), "Times-Bold");
261 assert_eq!(Font::TimesItalic.pdf_name(), "Times-Italic");
262 assert_eq!(Font::TimesBoldItalic.pdf_name(), "Times-BoldItalic");
263
264 assert_eq!(Font::Courier.pdf_name(), "Courier");
265 assert_eq!(Font::CourierBold.pdf_name(), "Courier-Bold");
266 assert_eq!(Font::CourierOblique.pdf_name(), "Courier-Oblique");
267 assert_eq!(Font::CourierBoldOblique.pdf_name(), "Courier-BoldOblique");
268
269 assert_eq!(Font::Symbol.pdf_name(), "Symbol");
270 assert_eq!(Font::ZapfDingbats.pdf_name(), "ZapfDingbats");
271 }
272
273 #[test]
274 fn test_font_is_symbolic() {
275 assert!(!Font::Helvetica.is_symbolic());
276 assert!(!Font::HelveticaBold.is_symbolic());
277 assert!(!Font::TimesRoman.is_symbolic());
278 assert!(!Font::Courier.is_symbolic());
279
280 assert!(Font::Symbol.is_symbolic());
281 assert!(Font::ZapfDingbats.is_symbolic());
282 }
283
284 #[test]
285 fn test_font_equality() {
286 assert_eq!(Font::Helvetica, Font::Helvetica);
287 assert_ne!(Font::Helvetica, Font::HelveticaBold);
288 assert_ne!(Font::TimesRoman, Font::TimesBold);
289 }
290
291 #[test]
292 fn test_font_debug() {
293 let font = Font::HelveticaBold;
294 let debug_str = format!("{:?}", font);
295 assert_eq!(debug_str, "HelveticaBold");
296 }
297
298 #[test]
299 fn test_font_clone() {
300 let font1 = Font::TimesItalic;
301 let font2 = font1.clone();
302 assert_eq!(font1, font2);
303 }
304
305 #[test]
306 fn test_font_hash() {
307 use std::collections::HashSet;
308
309 let mut fonts = HashSet::new();
310 fonts.insert(Font::Helvetica);
311 fonts.insert(Font::HelveticaBold);
312 fonts.insert(Font::Helvetica); assert_eq!(fonts.len(), 2);
315 assert!(fonts.contains(&Font::Helvetica));
316 assert!(fonts.contains(&Font::HelveticaBold));
317 assert!(!fonts.contains(&Font::TimesRoman));
318 }
319
320 #[test]
321 fn test_font_family_regular() {
322 assert_eq!(FontFamily::Helvetica.regular(), Font::Helvetica);
323 assert_eq!(FontFamily::Times.regular(), Font::TimesRoman);
324 assert_eq!(FontFamily::Courier.regular(), Font::Courier);
325 }
326
327 #[test]
328 fn test_font_family_bold() {
329 assert_eq!(FontFamily::Helvetica.bold(), Font::HelveticaBold);
330 assert_eq!(FontFamily::Times.bold(), Font::TimesBold);
331 assert_eq!(FontFamily::Courier.bold(), Font::CourierBold);
332 }
333
334 #[test]
335 fn test_font_family_italic() {
336 assert_eq!(FontFamily::Helvetica.italic(), Font::HelveticaOblique);
337 assert_eq!(FontFamily::Times.italic(), Font::TimesItalic);
338 assert_eq!(FontFamily::Courier.italic(), Font::CourierOblique);
339 }
340
341 #[test]
342 fn test_font_family_bold_italic() {
343 assert_eq!(
344 FontFamily::Helvetica.bold_italic(),
345 Font::HelveticaBoldOblique
346 );
347 assert_eq!(FontFamily::Times.bold_italic(), Font::TimesBoldItalic);
348 assert_eq!(FontFamily::Courier.bold_italic(), Font::CourierBoldOblique);
349 }
350
351 #[test]
352 fn test_font_family_equality() {
353 assert_eq!(FontFamily::Helvetica, FontFamily::Helvetica);
354 assert_ne!(FontFamily::Helvetica, FontFamily::Times);
355 assert_ne!(FontFamily::Times, FontFamily::Courier);
356 }
357
358 #[test]
359 fn test_font_family_debug() {
360 let family = FontFamily::Times;
361 let debug_str = format!("{:?}", family);
362 assert_eq!(debug_str, "Times");
363 }
364
365 #[test]
366 fn test_font_family_clone() {
367 let family1 = FontFamily::Courier;
368 let family2 = family1;
369 assert_eq!(family1, family2);
370 }
371
372 #[test]
373 fn test_font_family_copy() {
374 let family1 = FontFamily::Helvetica;
375 let family2 = family1; assert_eq!(family1, family2);
377
378 assert_eq!(family1, FontFamily::Helvetica);
380 assert_eq!(family2, FontFamily::Helvetica);
381 }
382
383 #[test]
384 fn test_all_helvetica_variants() {
385 let helvetica = FontFamily::Helvetica;
386
387 assert_eq!(helvetica.regular(), Font::Helvetica);
388 assert_eq!(helvetica.bold(), Font::HelveticaBold);
389 assert_eq!(helvetica.italic(), Font::HelveticaOblique);
390 assert_eq!(helvetica.bold_italic(), Font::HelveticaBoldOblique);
391 }
392
393 #[test]
394 fn test_all_times_variants() {
395 let times = FontFamily::Times;
396
397 assert_eq!(times.regular(), Font::TimesRoman);
398 assert_eq!(times.bold(), Font::TimesBold);
399 assert_eq!(times.italic(), Font::TimesItalic);
400 assert_eq!(times.bold_italic(), Font::TimesBoldItalic);
401 }
402
403 #[test]
404 fn test_all_courier_variants() {
405 let courier = FontFamily::Courier;
406
407 assert_eq!(courier.regular(), Font::Courier);
408 assert_eq!(courier.bold(), Font::CourierBold);
409 assert_eq!(courier.italic(), Font::CourierOblique);
410 assert_eq!(courier.bold_italic(), Font::CourierBoldOblique);
411 }
412
413 #[test]
416 fn test_font_encoding_pdf_names() {
417 assert_eq!(FontEncoding::WinAnsiEncoding.pdf_name(), "WinAnsiEncoding");
418 assert_eq!(
419 FontEncoding::MacRomanEncoding.pdf_name(),
420 "MacRomanEncoding"
421 );
422 assert_eq!(
423 FontEncoding::StandardEncoding.pdf_name(),
424 "StandardEncoding"
425 );
426 assert_eq!(
427 FontEncoding::MacExpertEncoding.pdf_name(),
428 "MacExpertEncoding"
429 );
430 assert_eq!(FontEncoding::Custom("MyEncoding").pdf_name(), "MyEncoding");
431 }
432
433 #[test]
434 fn test_font_encoding_recommended_for_font() {
435 assert_eq!(
437 FontEncoding::recommended_for_font(&Font::Helvetica),
438 Some(FontEncoding::WinAnsiEncoding)
439 );
440 assert_eq!(
441 FontEncoding::recommended_for_font(&Font::TimesRoman),
442 Some(FontEncoding::WinAnsiEncoding)
443 );
444 assert_eq!(
445 FontEncoding::recommended_for_font(&Font::CourierBold),
446 Some(FontEncoding::WinAnsiEncoding)
447 );
448
449 assert_eq!(FontEncoding::recommended_for_font(&Font::Symbol), None);
451 assert_eq!(
452 FontEncoding::recommended_for_font(&Font::ZapfDingbats),
453 None
454 );
455 }
456
457 #[test]
458 fn test_font_encoding_equality() {
459 assert_eq!(FontEncoding::WinAnsiEncoding, FontEncoding::WinAnsiEncoding);
460 assert_ne!(
461 FontEncoding::WinAnsiEncoding,
462 FontEncoding::MacRomanEncoding
463 );
464 assert_eq!(FontEncoding::Custom("Test"), FontEncoding::Custom("Test"));
465 assert_ne!(FontEncoding::Custom("Test1"), FontEncoding::Custom("Test2"));
466 }
467
468 #[test]
471 fn test_font_with_encoding_new() {
472 let font_enc = FontWithEncoding::new(Font::Helvetica, Some(FontEncoding::WinAnsiEncoding));
473 assert_eq!(font_enc.font, Font::Helvetica);
474 assert_eq!(font_enc.encoding, Some(FontEncoding::WinAnsiEncoding));
475
476 let font_no_enc = FontWithEncoding::new(Font::Symbol, None);
477 assert_eq!(font_no_enc.font, Font::Symbol);
478 assert_eq!(font_no_enc.encoding, None);
479 }
480
481 #[test]
482 fn test_font_with_encoding_with_recommended() {
483 let helvetica = FontWithEncoding::with_recommended_encoding(Font::Helvetica);
484 assert_eq!(helvetica.font, Font::Helvetica);
485 assert_eq!(helvetica.encoding, Some(FontEncoding::WinAnsiEncoding));
486
487 let symbol = FontWithEncoding::with_recommended_encoding(Font::Symbol);
488 assert_eq!(symbol.font, Font::Symbol);
489 assert_eq!(symbol.encoding, None);
490 }
491
492 #[test]
493 fn test_font_with_encoding_with_specific() {
494 let font_enc =
495 FontWithEncoding::with_encoding(Font::TimesRoman, FontEncoding::MacRomanEncoding);
496 assert_eq!(font_enc.font, Font::TimesRoman);
497 assert_eq!(font_enc.encoding, Some(FontEncoding::MacRomanEncoding));
498 }
499
500 #[test]
501 fn test_font_with_encoding_without_encoding() {
502 let font_no_enc = FontWithEncoding::without_encoding(Font::Courier);
503 assert_eq!(font_no_enc.font, Font::Courier);
504 assert_eq!(font_no_enc.encoding, None);
505 }
506
507 #[test]
508 fn test_font_with_encoding_from_font() {
509 let font_enc: FontWithEncoding = Font::HelveticaBold.into();
510 assert_eq!(font_enc.font, Font::HelveticaBold);
511 assert_eq!(font_enc.encoding, None);
512 }
513
514 #[test]
515 fn test_font_convenience_methods() {
516 let helvetica_with_enc = Font::Helvetica.with_encoding(FontEncoding::MacRomanEncoding);
517 assert_eq!(helvetica_with_enc.font, Font::Helvetica);
518 assert_eq!(
519 helvetica_with_enc.encoding,
520 Some(FontEncoding::MacRomanEncoding)
521 );
522
523 let times_recommended = Font::TimesRoman.with_recommended_encoding();
524 assert_eq!(times_recommended.font, Font::TimesRoman);
525 assert_eq!(
526 times_recommended.encoding,
527 Some(FontEncoding::WinAnsiEncoding)
528 );
529
530 let courier_no_enc = Font::Courier.without_encoding();
531 assert_eq!(courier_no_enc.font, Font::Courier);
532 assert_eq!(courier_no_enc.encoding, None);
533 }
534
535 #[test]
536 fn test_font_with_encoding_equality() {
537 let font1 = FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::WinAnsiEncoding);
538 let font2 = FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::WinAnsiEncoding);
539 let font3 =
540 FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::MacRomanEncoding);
541 let font4 =
542 FontWithEncoding::with_encoding(Font::TimesRoman, FontEncoding::WinAnsiEncoding);
543
544 assert_eq!(font1, font2);
545 assert_ne!(font1, font3);
546 assert_ne!(font1, font4);
547 }
548
549 #[test]
550 fn test_font_with_encoding_debug() {
551 let font_enc =
552 FontWithEncoding::with_encoding(Font::Helvetica, FontEncoding::WinAnsiEncoding);
553 let debug_str = format!("{:?}", font_enc);
554 assert!(debug_str.contains("Helvetica"));
555 assert!(debug_str.contains("WinAnsiEncoding"));
556 }
557
558 #[test]
559 fn test_font_with_encoding_clone() {
560 let font1 =
561 FontWithEncoding::with_encoding(Font::TimesRoman, FontEncoding::StandardEncoding);
562 let font2 = font1.clone();
563 assert_eq!(font1, font2);
564 }
565
566 #[test]
567 fn test_font_with_encoding_copy() {
568 let font1 = FontWithEncoding::with_encoding(Font::Courier, FontEncoding::WinAnsiEncoding);
569 let font2 = font1.clone(); assert_eq!(font1, font2);
571
572 assert_eq!(font1.font, Font::Courier);
574 assert_eq!(font2.font, Font::Courier);
575 }
576
577 #[test]
578 fn test_custom_encoding() {
579 let custom_enc = FontEncoding::Custom("MyCustomEncoding");
580 assert_eq!(custom_enc.pdf_name(), "MyCustomEncoding");
581
582 let font_with_custom = FontWithEncoding::with_encoding(Font::Helvetica, custom_enc);
583 assert_eq!(font_with_custom.encoding, Some(custom_enc));
584 }
585}