Skip to main content

rust_pdf_box/font/
type1.rs

1use crate::cos::object::{COSDictionary, COSName};
2
3/// Standard 14 built-in Type 1 PDF fonts.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum PDType1Font {
6    Helvetica,
7    HelveticaBold,
8    HelveticaOblique,
9    HelveticaBoldOblique,
10    TimesRoman,
11    TimesBold,
12    TimesItalic,
13    TimesBoldItalic,
14    Courier,
15    CourierBold,
16    CourierOblique,
17    CourierBoldOblique,
18    Symbol,
19    ZapfDingbats,
20}
21
22impl PDType1Font {
23    pub fn base_font_name(&self) -> &'static str {
24        match self {
25            Self::Helvetica => "Helvetica",
26            Self::HelveticaBold => "Helvetica-Bold",
27            Self::HelveticaOblique => "Helvetica-Oblique",
28            Self::HelveticaBoldOblique => "Helvetica-BoldOblique",
29            Self::TimesRoman => "Times-Roman",
30            Self::TimesBold => "Times-Bold",
31            Self::TimesItalic => "Times-Italic",
32            Self::TimesBoldItalic => "Times-BoldItalic",
33            Self::Courier => "Courier",
34            Self::CourierBold => "Courier-Bold",
35            Self::CourierOblique => "Courier-Oblique",
36            Self::CourierBoldOblique => "Courier-BoldOblique",
37            Self::Symbol => "Symbol",
38            Self::ZapfDingbats => "ZapfDingbats",
39        }
40    }
41
42    /// Converts this Type 1 font into a PDF font dictionary.
43    pub fn to_dict(&self) -> COSDictionary {
44        let mut dict = COSDictionary::new();
45        dict.set("Type", COSName::new("Font"));
46        dict.set("Subtype", COSName::new("Type1"));
47        dict.set("BaseFont", COSName::new(self.base_font_name()));
48        if !matches!(self, Self::Symbol | Self::ZapfDingbats) {
49            dict.set("Encoding", COSName::new("WinAnsiEncoding"));
50        }
51        dict
52    }
53
54    /// Approximate or standard character width in 1/1000 font units.
55    pub fn get_char_width(&self, c: char) -> f64 {
56        match self {
57            Self::Courier | Self::CourierBold | Self::CourierOblique | Self::CourierBoldOblique => {
58                // Fixed-width Courier is 600 units for every character
59                600.0
60            }
61            Self::TimesRoman | Self::TimesBold | Self::TimesItalic | Self::TimesBoldItalic => {
62                match c {
63                    ' ' => 250.0,
64                    'i' | 'j' | 'l' | '!' | '.' | ':' | ';' | '\'' => 278.0,
65                    'f' | 't' | 'I' => 333.0,
66                    'r' => 389.0,
67                    's' => 444.0,
68                    'a' | 'b' | 'c' | 'd' | 'e' | 'g' | 'h' | 'k' | 'n' | 'o' | 'p' | 'q' | 'u'
69                    | 'v' | 'x' | 'y' | 'z' => 500.0,
70                    'w' => 722.0,
71                    'm' => 778.0,
72                    'M' | 'W' => 889.0,
73                    'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'J' | 'K' | 'N' | 'O' | 'P'
74                    | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'X' | 'Y' | 'Z' => 667.0,
75                    _ => 500.0,
76                }
77            }
78            _ => {
79                // Helvetica default widths
80                match c {
81                    ' ' => 278.0,
82                    'i' | 'l' | '!' | '.' | ':' | ';' | '\'' | '|' => 222.0,
83                    'j' | 'r' | 't' | 'I' => 278.0,
84                    'f' => 278.0,
85                    'c' | 's' | 'z' => 500.0,
86                    'a' | 'b' | 'd' | 'e' | 'g' | 'h' | 'k' | 'n' | 'o' | 'p' | 'q' | 'u' | 'v'
87                    | 'x' | 'y' => 556.0,
88                    'w' => 722.0,
89                    'm' => 833.0,
90                    'W' | 'M' => 833.0,
91                    'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'J' | 'K' | 'L' | 'N' | 'O'
92                    | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'X' | 'Y' | 'Z' => 667.0,
93                    _ => 556.0,
94                }
95            }
96        }
97    }
98
99    /// Computes the exact rendered width of a string in user space points at the given font size.
100    pub fn get_string_width(&self, text: &str, font_size: f64) -> f64 {
101        let total_units: f64 = text.chars().map(|c| self.get_char_width(c)).sum();
102        (total_units / 1000.0) * font_size
103    }
104}