pdf_canvas/
fontsource.rs

1use crate::encoding::{
2    Encoding, SYMBOL_ENCODING, WIN_ANSI_ENCODING, ZAPFDINGBATS_ENCODING,
3};
4use crate::fontmetrics::{get_builtin_metrics, FontMetrics};
5use crate::Pdf;
6use std::cmp::Eq;
7use std::hash::Hash;
8use std::io::{self, Write};
9
10/// The "Base14" built-in fonts in PDF.
11/// Underscores in these names are hyphens in the real names.
12#[allow(non_camel_case_types, missing_docs)]
13#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
14pub enum BuiltinFont {
15    Courier,
16    Courier_Bold,
17    Courier_Oblique,
18    Courier_BoldOblique,
19    Helvetica,
20    Helvetica_Bold,
21    Helvetica_Oblique,
22    Helvetica_BoldOblique,
23    Times_Roman,
24    Times_Bold,
25    Times_Italic,
26    Times_BoldItalic,
27    Symbol,
28    ZapfDingbats,
29}
30
31/// This trait is implemented by any kind of font that the pdf library
32/// supports.
33///
34/// Currently, only BuiltinFont implements this.
35/// TODO Add implementation(s) for other fonts.
36pub trait FontSource: PartialEq + Eq + Hash {
37    /// Write the object(s) for this font to a pdf file.
38    ///
39    /// This is called automatically for each font used in a document.
40    /// There should be no need to call this method from user code.
41    fn write_object(&self, pdf: &mut Pdf) -> io::Result<usize>;
42
43    /// Get the PDF name of this font.
44    ///
45    /// # Examples
46    /// ```
47    /// use pdf_canvas::{BuiltinFont, FontSource};
48    /// assert_eq!("Times-Roman", BuiltinFont::Times_Roman.pdf_name());
49    /// ```
50    fn pdf_name(&self) -> String;
51
52    /// Get the encoding that this font uses.
53    fn get_encoding(&self) -> &Encoding;
54
55    /// Get the width of a string in this font at given size.
56    ///
57    /// # Examples
58    /// ```
59    /// use pdf_canvas::{BuiltinFont, FontSource};
60    /// let proportional = BuiltinFont::Helvetica;
61    /// assert_eq!(62.004, proportional.get_width(12.0, "Hello World"));
62    /// let fixed = BuiltinFont::Courier;
63    /// assert_eq!(60.0, fixed.get_width(10.0, "0123456789"));
64    /// ```
65    fn get_width(&self, size: f32, text: &str) -> f32;
66
67    /// Get the width of a string in thousands of unit of text space.
68    /// This unit is what is used in some places internally in pdf files.
69    ///
70    /// # Examples
71    /// ```
72    /// use pdf_canvas::{BuiltinFont, FontSource};
73    /// assert_eq!(5167, BuiltinFont::Helvetica.get_width_raw("Hello World"));
74    /// assert_eq!(600, BuiltinFont::Courier.get_width_raw("A"));
75    /// ```
76    fn get_width_raw(&self, text: &str) -> u32;
77
78    /// Get the font metrics for font.
79    fn get_metrics(&self) -> FontMetrics;
80}
81
82impl FontSource for BuiltinFont {
83    fn write_object(&self, pdf: &mut Pdf) -> io::Result<usize> {
84        // Note: This is enough for a Base14 font, other fonts will
85        // require a stream for the actual font, and probably another
86        // object for metrics etc
87        pdf.write_new_object(|font_object_id, pdf| {
88            writeln!(
89                pdf.output,
90                "<< /Type /Font /Subtype /Type1 /BaseFont /{} /Encoding /{} >>",
91                self.pdf_name(),
92                self.get_encoding().get_name()
93            )?;
94            Ok(font_object_id)
95        })
96    }
97
98    fn pdf_name(&self) -> String {
99        format!("{:?}", self).replace("_", "-")
100    }
101
102    /// The encoding is WinAnsiEncoding for all builtin fonts except
103    /// Symbol, for which it is SymbolEncoding, and
104    /// ZapfDingbats, which uses ZapfDingbatsEncoding.
105    fn get_encoding(&self) -> &'static Encoding {
106        match *self {
107            BuiltinFont::Symbol => &SYMBOL_ENCODING,
108            BuiltinFont::ZapfDingbats => &ZAPFDINGBATS_ENCODING,
109            _ => &WIN_ANSI_ENCODING,
110        }
111    }
112
113    fn get_width(&self, size: f32, text: &str) -> f32 {
114        size * self.get_width_raw(text) as f32 / 1000.0
115    }
116
117    fn get_width_raw(&self, text: &str) -> u32 {
118        let metrics = self.get_metrics();
119        self.get_encoding().encode_string(text).iter().fold(
120            0,
121            |result, &ch| {
122                result + u32::from(metrics.get_width(ch).unwrap_or(100))
123            },
124        )
125    }
126
127    fn get_metrics(&self) -> FontMetrics {
128        get_builtin_metrics(*self).clone()
129    }
130}