1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use encoding::{
    Encoding, SYMBOL_ENCODING, WIN_ANSI_ENCODING, ZAPFDINGBATS_ENCODING,
};
use fontmetrics::{get_builtin_metrics, FontMetrics};
use std::cmp::Eq;
use std::hash::Hash;
use std::io::{self, Write};
use std::ops::Add;
use Pdf;

/// The "Base14" built-in fonts in PDF.
/// Underscores in these names are hyphens in the real names.
#[allow(non_camel_case_types, missing_docs)]
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum BuiltinFont {
    Courier,
    Courier_Bold,
    Courier_Oblique,
    Courier_BoldOblique,
    Helvetica,
    Helvetica_Bold,
    Helvetica_Oblique,
    Helvetica_BoldOblique,
    Times_Roman,
    Times_Bold,
    Times_Italic,
    Times_BoldItalic,
    Symbol,
    ZapfDingbats,
}

/// This trait is implemented by any kind of font that the pdf library
/// supports.
///
/// Currently, only BuiltinFont implements this.
/// TODO Add implementation(s) for other fonts.
pub trait FontSource: PartialEq + Eq + Hash {
    /// Write the object(s) for this font to a pdf file.
    ///
    /// This is called automatically for each font used in a document.
    /// There should be no need to call this method from user code.
    fn write_object(&self, pdf: &mut Pdf) -> io::Result<usize>;

    /// Get the PDF name of this font.
    ///
    /// # Examples
    /// ```
    /// use pdf_canvas::{BuiltinFont, FontSource};
    /// assert_eq!("Times-Roman", BuiltinFont::Times_Roman.pdf_name());
    /// ```
    fn pdf_name(&self) -> String;

    /// Get the encoding that this font uses.
    fn get_encoding(&self) -> &Encoding;

    /// Get the width of a string in this font at given size.
    ///
    /// # Examples
    /// ```
    /// use pdf_canvas::{BuiltinFont, FontSource};
    /// let proportional = BuiltinFont::Helvetica;
    /// assert_eq!(62.004, proportional.get_width(12.0, "Hello World"));
    /// let fixed = BuiltinFont::Courier;
    /// assert_eq!(60.0, fixed.get_width(10.0, "0123456789"));
    /// ```
    fn get_width(&self, size: f32, text: &str) -> f32;

    /// Get the width of a string in thousands of unit of text space.
    /// This unit is what is used in some places internally in pdf files.
    ///
    /// # Examples
    /// ```
    /// use pdf_canvas::{BuiltinFont, FontSource};
    /// assert_eq!(5167, BuiltinFont::Helvetica.get_width_raw("Hello World"));
    /// assert_eq!(600, BuiltinFont::Courier.get_width_raw("A"));
    /// ```
    fn get_width_raw(&self, text: &str) -> u32;

    /// Get the font metrics for font.
    fn get_metrics(&self) -> FontMetrics;
}

impl FontSource for BuiltinFont {
    fn write_object(&self, pdf: &mut Pdf) -> io::Result<usize> {
        // Note: This is enough for a Base14 font, other fonts will
        // require a stream for the actual font, and probably another
        // object for metrics etc
        pdf.write_new_object(|font_object_id, pdf| {
            writeln!(
                pdf.output,
                "<< /Type /Font /Subtype /Type1 /BaseFont /{} /Encoding /{} >>",
                self.pdf_name(),
                self.get_encoding().get_name()
            )?;
            Ok(font_object_id)
        })
    }

    fn pdf_name(&self) -> String {
        format!("{:?}", self).replace("_", "-")
    }

    /// The encoding is WinAnsiEncoding for all builtin fonts except
    /// Symbol, for which it is SymbolEncoding, and
    /// ZapfDingbats, which uses ZapfDingbatsEncoding.
    fn get_encoding(&self) -> &'static Encoding {
        match *self {
            BuiltinFont::Symbol => &SYMBOL_ENCODING,
            BuiltinFont::ZapfDingbats => &ZAPFDINGBATS_ENCODING,
            _ => &WIN_ANSI_ENCODING,
        }
    }

    fn get_width(&self, size: f32, text: &str) -> f32 {
        size * self.get_width_raw(text) as f32 / 1000.0
    }

    fn get_width_raw(&self, text: &str) -> u32 {
        let metrics = self.get_metrics();
        self.get_encoding()
            .encode_string(text)
            .iter()
            .map(|&ch| u32::from(metrics.get_width(ch).unwrap_or(100)))
            .fold(0, Add::add)
    }

    fn get_metrics(&self) -> FontMetrics {
        get_builtin_metrics(*self).clone()
    }
}