oxidize_pdf/text/
font.rs

1/// Standard PDF fonts (Type 1 base 14 fonts).
2///
3/// These fonts are guaranteed to be available in all PDF readers
4/// and don't need to be embedded in the document.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum Font {
7    // Standard 14 PDF fonts
8    /// Helvetica (sans-serif)
9    Helvetica,
10    /// Helvetica Bold
11    HelveticaBold,
12    /// Helvetica Oblique (italic)
13    HelveticaOblique,
14    /// Helvetica Bold Oblique
15    HelveticaBoldOblique,
16    /// Times Roman (serif)
17    TimesRoman,
18    /// Times Bold
19    TimesBold,
20    /// Times Italic
21    TimesItalic,
22    /// Times Bold Italic
23    TimesBoldItalic,
24    /// Courier (monospace)
25    Courier,
26    /// Courier Bold
27    CourierBold,
28    /// Courier Oblique
29    CourierOblique,
30    /// Courier Bold Oblique
31    CourierBoldOblique,
32    /// Symbol font (mathematical symbols)
33    Symbol,
34    /// ZapfDingbats (decorative symbols)
35    ZapfDingbats,
36}
37
38impl Font {
39    pub fn pdf_name(&self) -> &'static str {
40        match self {
41            Font::Helvetica => "Helvetica",
42            Font::HelveticaBold => "Helvetica-Bold",
43            Font::HelveticaOblique => "Helvetica-Oblique",
44            Font::HelveticaBoldOblique => "Helvetica-BoldOblique",
45            Font::TimesRoman => "Times-Roman",
46            Font::TimesBold => "Times-Bold",
47            Font::TimesItalic => "Times-Italic",
48            Font::TimesBoldItalic => "Times-BoldItalic",
49            Font::Courier => "Courier",
50            Font::CourierBold => "Courier-Bold",
51            Font::CourierOblique => "Courier-Oblique",
52            Font::CourierBoldOblique => "Courier-BoldOblique",
53            Font::Symbol => "Symbol",
54            Font::ZapfDingbats => "ZapfDingbats",
55        }
56    }
57
58    pub fn is_symbolic(&self) -> bool {
59        matches!(self, Font::Symbol | Font::ZapfDingbats)
60    }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq)]
64pub enum FontFamily {
65    Helvetica,
66    Times,
67    Courier,
68}
69
70impl FontFamily {
71    pub fn regular(self) -> Font {
72        match self {
73            FontFamily::Helvetica => Font::Helvetica,
74            FontFamily::Times => Font::TimesRoman,
75            FontFamily::Courier => Font::Courier,
76        }
77    }
78
79    pub fn bold(self) -> Font {
80        match self {
81            FontFamily::Helvetica => Font::HelveticaBold,
82            FontFamily::Times => Font::TimesBold,
83            FontFamily::Courier => Font::CourierBold,
84        }
85    }
86
87    pub fn italic(self) -> Font {
88        match self {
89            FontFamily::Helvetica => Font::HelveticaOblique,
90            FontFamily::Times => Font::TimesItalic,
91            FontFamily::Courier => Font::CourierOblique,
92        }
93    }
94
95    pub fn bold_italic(self) -> Font {
96        match self {
97            FontFamily::Helvetica => Font::HelveticaBoldOblique,
98            FontFamily::Times => Font::TimesBoldItalic,
99            FontFamily::Courier => Font::CourierBoldOblique,
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_font_pdf_names() {
110        assert_eq!(Font::Helvetica.pdf_name(), "Helvetica");
111        assert_eq!(Font::HelveticaBold.pdf_name(), "Helvetica-Bold");
112        assert_eq!(Font::HelveticaOblique.pdf_name(), "Helvetica-Oblique");
113        assert_eq!(
114            Font::HelveticaBoldOblique.pdf_name(),
115            "Helvetica-BoldOblique"
116        );
117
118        assert_eq!(Font::TimesRoman.pdf_name(), "Times-Roman");
119        assert_eq!(Font::TimesBold.pdf_name(), "Times-Bold");
120        assert_eq!(Font::TimesItalic.pdf_name(), "Times-Italic");
121        assert_eq!(Font::TimesBoldItalic.pdf_name(), "Times-BoldItalic");
122
123        assert_eq!(Font::Courier.pdf_name(), "Courier");
124        assert_eq!(Font::CourierBold.pdf_name(), "Courier-Bold");
125        assert_eq!(Font::CourierOblique.pdf_name(), "Courier-Oblique");
126        assert_eq!(Font::CourierBoldOblique.pdf_name(), "Courier-BoldOblique");
127
128        assert_eq!(Font::Symbol.pdf_name(), "Symbol");
129        assert_eq!(Font::ZapfDingbats.pdf_name(), "ZapfDingbats");
130    }
131
132    #[test]
133    fn test_font_is_symbolic() {
134        assert!(!Font::Helvetica.is_symbolic());
135        assert!(!Font::HelveticaBold.is_symbolic());
136        assert!(!Font::TimesRoman.is_symbolic());
137        assert!(!Font::Courier.is_symbolic());
138
139        assert!(Font::Symbol.is_symbolic());
140        assert!(Font::ZapfDingbats.is_symbolic());
141    }
142
143    #[test]
144    fn test_font_equality() {
145        assert_eq!(Font::Helvetica, Font::Helvetica);
146        assert_ne!(Font::Helvetica, Font::HelveticaBold);
147        assert_ne!(Font::TimesRoman, Font::TimesBold);
148    }
149
150    #[test]
151    fn test_font_debug() {
152        let font = Font::HelveticaBold;
153        let debug_str = format!("{:?}", font);
154        assert_eq!(debug_str, "HelveticaBold");
155    }
156
157    #[test]
158    fn test_font_clone() {
159        let font1 = Font::TimesItalic;
160        let font2 = font1;
161        assert_eq!(font1, font2);
162    }
163
164    #[test]
165    fn test_font_hash() {
166        use std::collections::HashSet;
167
168        let mut fonts = HashSet::new();
169        fonts.insert(Font::Helvetica);
170        fonts.insert(Font::HelveticaBold);
171        fonts.insert(Font::Helvetica); // Duplicate
172
173        assert_eq!(fonts.len(), 2);
174        assert!(fonts.contains(&Font::Helvetica));
175        assert!(fonts.contains(&Font::HelveticaBold));
176        assert!(!fonts.contains(&Font::TimesRoman));
177    }
178
179    #[test]
180    fn test_font_family_regular() {
181        assert_eq!(FontFamily::Helvetica.regular(), Font::Helvetica);
182        assert_eq!(FontFamily::Times.regular(), Font::TimesRoman);
183        assert_eq!(FontFamily::Courier.regular(), Font::Courier);
184    }
185
186    #[test]
187    fn test_font_family_bold() {
188        assert_eq!(FontFamily::Helvetica.bold(), Font::HelveticaBold);
189        assert_eq!(FontFamily::Times.bold(), Font::TimesBold);
190        assert_eq!(FontFamily::Courier.bold(), Font::CourierBold);
191    }
192
193    #[test]
194    fn test_font_family_italic() {
195        assert_eq!(FontFamily::Helvetica.italic(), Font::HelveticaOblique);
196        assert_eq!(FontFamily::Times.italic(), Font::TimesItalic);
197        assert_eq!(FontFamily::Courier.italic(), Font::CourierOblique);
198    }
199
200    #[test]
201    fn test_font_family_bold_italic() {
202        assert_eq!(
203            FontFamily::Helvetica.bold_italic(),
204            Font::HelveticaBoldOblique
205        );
206        assert_eq!(FontFamily::Times.bold_italic(), Font::TimesBoldItalic);
207        assert_eq!(FontFamily::Courier.bold_italic(), Font::CourierBoldOblique);
208    }
209
210    #[test]
211    fn test_font_family_equality() {
212        assert_eq!(FontFamily::Helvetica, FontFamily::Helvetica);
213        assert_ne!(FontFamily::Helvetica, FontFamily::Times);
214        assert_ne!(FontFamily::Times, FontFamily::Courier);
215    }
216
217    #[test]
218    fn test_font_family_debug() {
219        let family = FontFamily::Times;
220        let debug_str = format!("{:?}", family);
221        assert_eq!(debug_str, "Times");
222    }
223
224    #[test]
225    fn test_font_family_clone() {
226        let family1 = FontFamily::Courier;
227        let family2 = family1;
228        assert_eq!(family1, family2);
229    }
230
231    #[test]
232    fn test_font_family_copy() {
233        let family1 = FontFamily::Helvetica;
234        let family2 = family1; // Copy semantics
235        assert_eq!(family1, family2);
236
237        // Both variables should still be usable
238        assert_eq!(family1, FontFamily::Helvetica);
239        assert_eq!(family2, FontFamily::Helvetica);
240    }
241
242    #[test]
243    fn test_all_helvetica_variants() {
244        let helvetica = FontFamily::Helvetica;
245
246        assert_eq!(helvetica.regular(), Font::Helvetica);
247        assert_eq!(helvetica.bold(), Font::HelveticaBold);
248        assert_eq!(helvetica.italic(), Font::HelveticaOblique);
249        assert_eq!(helvetica.bold_italic(), Font::HelveticaBoldOblique);
250    }
251
252    #[test]
253    fn test_all_times_variants() {
254        let times = FontFamily::Times;
255
256        assert_eq!(times.regular(), Font::TimesRoman);
257        assert_eq!(times.bold(), Font::TimesBold);
258        assert_eq!(times.italic(), Font::TimesItalic);
259        assert_eq!(times.bold_italic(), Font::TimesBoldItalic);
260    }
261
262    #[test]
263    fn test_all_courier_variants() {
264        let courier = FontFamily::Courier;
265
266        assert_eq!(courier.regular(), Font::Courier);
267        assert_eq!(courier.bold(), Font::CourierBold);
268        assert_eq!(courier.italic(), Font::CourierOblique);
269        assert_eq!(courier.bold_italic(), Font::CourierBoldOblique);
270    }
271}