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}