pdf-canvas 0.7.0

Generate PDF files in pure Rust. Currently, simple vector graphics and text set in the 14 built-in fonts are supported.
Documentation
use crate::encoding::{
    Encoding, SYMBOL_ENCODING, WIN_ANSI_ENCODING, ZAPFDINGBATS_ENCODING,
};
use crate::fontmetrics::{get_builtin_metrics, FontMetrics};
use crate::Pdf;
use std::cmp::Eq;
use std::hash::Hash;
use std::io::{self, Write};

/// 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().fold(
            0,
            |result, &ch| {
                result + u32::from(metrics.get_width(ch).unwrap_or(100))
            },
        )
    }

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