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!(Font::HelveticaBoldOblique.pdf_name(), "Helvetica-BoldOblique");
114        
115        assert_eq!(Font::TimesRoman.pdf_name(), "Times-Roman");
116        assert_eq!(Font::TimesBold.pdf_name(), "Times-Bold");
117        assert_eq!(Font::TimesItalic.pdf_name(), "Times-Italic");
118        assert_eq!(Font::TimesBoldItalic.pdf_name(), "Times-BoldItalic");
119        
120        assert_eq!(Font::Courier.pdf_name(), "Courier");
121        assert_eq!(Font::CourierBold.pdf_name(), "Courier-Bold");
122        assert_eq!(Font::CourierOblique.pdf_name(), "Courier-Oblique");
123        assert_eq!(Font::CourierBoldOblique.pdf_name(), "Courier-BoldOblique");
124        
125        assert_eq!(Font::Symbol.pdf_name(), "Symbol");
126        assert_eq!(Font::ZapfDingbats.pdf_name(), "ZapfDingbats");
127    }
128    
129    #[test]
130    fn test_font_is_symbolic() {
131        assert!(!Font::Helvetica.is_symbolic());
132        assert!(!Font::HelveticaBold.is_symbolic());
133        assert!(!Font::TimesRoman.is_symbolic());
134        assert!(!Font::Courier.is_symbolic());
135        
136        assert!(Font::Symbol.is_symbolic());
137        assert!(Font::ZapfDingbats.is_symbolic());
138    }
139    
140    #[test]
141    fn test_font_equality() {
142        assert_eq!(Font::Helvetica, Font::Helvetica);
143        assert_ne!(Font::Helvetica, Font::HelveticaBold);
144        assert_ne!(Font::TimesRoman, Font::TimesBold);
145    }
146    
147    #[test]
148    fn test_font_debug() {
149        let font = Font::HelveticaBold;
150        let debug_str = format!("{:?}", font);
151        assert_eq!(debug_str, "HelveticaBold");
152    }
153    
154    #[test]
155    fn test_font_clone() {
156        let font1 = Font::TimesItalic;
157        let font2 = font1;
158        assert_eq!(font1, font2);
159    }
160    
161    #[test]
162    fn test_font_hash() {
163        use std::collections::HashSet;
164        
165        let mut fonts = HashSet::new();
166        fonts.insert(Font::Helvetica);
167        fonts.insert(Font::HelveticaBold);
168        fonts.insert(Font::Helvetica); // Duplicate
169        
170        assert_eq!(fonts.len(), 2);
171        assert!(fonts.contains(&Font::Helvetica));
172        assert!(fonts.contains(&Font::HelveticaBold));
173        assert!(!fonts.contains(&Font::TimesRoman));
174    }
175    
176    #[test]
177    fn test_font_family_regular() {
178        assert_eq!(FontFamily::Helvetica.regular(), Font::Helvetica);
179        assert_eq!(FontFamily::Times.regular(), Font::TimesRoman);
180        assert_eq!(FontFamily::Courier.regular(), Font::Courier);
181    }
182    
183    #[test]
184    fn test_font_family_bold() {
185        assert_eq!(FontFamily::Helvetica.bold(), Font::HelveticaBold);
186        assert_eq!(FontFamily::Times.bold(), Font::TimesBold);
187        assert_eq!(FontFamily::Courier.bold(), Font::CourierBold);
188    }
189    
190    #[test]
191    fn test_font_family_italic() {
192        assert_eq!(FontFamily::Helvetica.italic(), Font::HelveticaOblique);
193        assert_eq!(FontFamily::Times.italic(), Font::TimesItalic);
194        assert_eq!(FontFamily::Courier.italic(), Font::CourierOblique);
195    }
196    
197    #[test]
198    fn test_font_family_bold_italic() {
199        assert_eq!(FontFamily::Helvetica.bold_italic(), Font::HelveticaBoldOblique);
200        assert_eq!(FontFamily::Times.bold_italic(), Font::TimesBoldItalic);
201        assert_eq!(FontFamily::Courier.bold_italic(), Font::CourierBoldOblique);
202    }
203    
204    #[test]
205    fn test_font_family_equality() {
206        assert_eq!(FontFamily::Helvetica, FontFamily::Helvetica);
207        assert_ne!(FontFamily::Helvetica, FontFamily::Times);
208        assert_ne!(FontFamily::Times, FontFamily::Courier);
209    }
210    
211    #[test]
212    fn test_font_family_debug() {
213        let family = FontFamily::Times;
214        let debug_str = format!("{:?}", family);
215        assert_eq!(debug_str, "Times");
216    }
217    
218    #[test]
219    fn test_font_family_clone() {
220        let family1 = FontFamily::Courier;
221        let family2 = family1;
222        assert_eq!(family1, family2);
223    }
224    
225    #[test]
226    fn test_font_family_copy() {
227        let family1 = FontFamily::Helvetica;
228        let family2 = family1; // Copy semantics
229        assert_eq!(family1, family2);
230        
231        // Both variables should still be usable
232        assert_eq!(family1, FontFamily::Helvetica);
233        assert_eq!(family2, FontFamily::Helvetica);
234    }
235    
236    #[test]
237    fn test_all_helvetica_variants() {
238        let helvetica = FontFamily::Helvetica;
239        
240        assert_eq!(helvetica.regular(), Font::Helvetica);
241        assert_eq!(helvetica.bold(), Font::HelveticaBold);
242        assert_eq!(helvetica.italic(), Font::HelveticaOblique);
243        assert_eq!(helvetica.bold_italic(), Font::HelveticaBoldOblique);
244    }
245    
246    #[test]
247    fn test_all_times_variants() {
248        let times = FontFamily::Times;
249        
250        assert_eq!(times.regular(), Font::TimesRoman);
251        assert_eq!(times.bold(), Font::TimesBold);
252        assert_eq!(times.italic(), Font::TimesItalic);
253        assert_eq!(times.bold_italic(), Font::TimesBoldItalic);
254    }
255    
256    #[test]
257    fn test_all_courier_variants() {
258        let courier = FontFamily::Courier;
259        
260        assert_eq!(courier.regular(), Font::Courier);
261        assert_eq!(courier.bold(), Font::CourierBold);
262        assert_eq!(courier.italic(), Font::CourierOblique);
263        assert_eq!(courier.bold_italic(), Font::CourierBoldOblique);
264    }
265}