summaryrefslogtreecommitdiff
path: root/src/pdf.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-03-30 16:01:45 +0100
committerLaurenz <laurmaedje@gmail.com>2019-03-30 16:01:45 +0100
commit10994ebac34d027fb1937f72183859142b1b6180 (patch)
tree9893bb6a7980637f7c546bcebfeb889b19241181 /src/pdf.rs
parent094648e86b71e8850e9218a30810d10d3922b122 (diff)
Move exporting into seperate module 🧱
Diffstat (limited to 'src/pdf.rs')
-rw-r--r--src/pdf.rs330
1 files changed, 0 insertions, 330 deletions
diff --git a/src/pdf.rs b/src/pdf.rs
deleted file mode 100644
index ad2f29af..00000000
--- a/src/pdf.rs
+++ /dev/null
@@ -1,330 +0,0 @@
-//! Writing of documents in the _PDF_ format.
-
-use std::collections::HashSet;
-use std::error;
-use std::fmt;
-use std::io::{self, Write};
-use pdf::{PdfWriter, Ref, Rect, Version, Trailer, Content};
-use pdf::doc::{Catalog, PageTree, Page, Resource, Text};
-use pdf::font::{Type0Font, CMapEncoding, CIDFont, CIDFontType, CIDSystemInfo};
-use pdf::font::{GlyphUnit, WidthRecord, FontDescriptor, FontFlags, FontStream, EmbeddedFontType};
-use crate::doc::{Document, Size, Text as DocText, TextCommand};
-use crate::font::{Font, FontError};
-
-
-/// Writes documents in the _PDF_ format.
-pub struct PdfCreator<'a, W: Write> {
- writer: PdfWriter<W>,
- doc: &'a Document,
- offsets: Offsets,
- fonts: Vec<PdfFont>,
-}
-
-/// Offsets for the various groups of ids.
-struct Offsets {
- catalog: Ref,
- page_tree: Ref,
- pages: (Ref, Ref),
- contents: (Ref, Ref),
- fonts: (Ref, Ref),
-}
-
-impl<'a, W: Write> PdfCreator<'a, W> {
- /// Create a new _PDF_ Creator.
- pub fn new(doc: &'a Document, target: W) -> PdfResult<PdfCreator<'a, W>> {
- // Calculate a unique id for all object to come
- let catalog = 1;
- let page_tree = catalog + 1;
- let pages = (page_tree + 1, page_tree + doc.pages.len() as Ref);
- let content_count = doc.pages.iter().flat_map(|p| p.text.iter()).count() as Ref;
- let contents = (pages.1 + 1, pages.1 + content_count);
- let fonts = (contents.1 + 1, contents.1 + 4 * doc.fonts.len() as Ref);
-
- let offsets = Offsets {
- catalog,
- page_tree,
- pages,
- contents,
- fonts,
- };
-
- // Find out which chars are used in this document.
- let mut char_sets = vec![HashSet::new(); doc.fonts.len()];
- let mut current_font: usize = 0;
- for page in &doc.pages {
- for text in &page.text {
- for command in &text.commands {
- match command {
- TextCommand::Text(string)
- => char_sets[current_font].extend(string.chars()),
- TextCommand::SetFont(id, _) => current_font = *id,
- _ => {},
- }
- }
- }
- }
-
- // Create a subsetted pdf font.
- let fonts = doc.fonts.iter().enumerate().map(|(i, font)| {
- PdfFont::new(font, &char_sets[i])
- }).collect::<PdfResult<Vec<_>>>()?;
-
- Ok(PdfCreator {
- writer: PdfWriter::new(target),
- doc,
- offsets,
- fonts,
- })
- }
-
- /// Write the complete document.
- pub fn write(&mut self) -> PdfResult<usize> {
- // Header
- self.writer.write_header(&Version::new(1, 7))?;
-
- // Document catalog, page tree and pages
- self.write_pages()?;
-
- // Contents
- self.write_contents()?;
-
- // Fonts
- self.write_fonts()?;
-
- // Cross-reference table
- self.writer.write_xref_table()?;
-
- // Trailer
- self.writer.write_trailer(&Trailer::new(self.offsets.catalog))?;
-
- Ok(self.writer.written())
- }
-
- /// Write the document catalog, page tree and pages.
- fn write_pages(&mut self) -> PdfResult<()> {
- // The document catalog
- self.writer.write_obj(self.offsets.catalog,
- &Catalog::new(self.offsets.page_tree))?;
-
- // Root page tree
- self.writer.write_obj(self.offsets.page_tree, PageTree::new()
- .kids(self.offsets.pages.0 ..= self.offsets.pages.1)
- .resource(Resource::Font(1, self.offsets.fonts.0))
- )?;
-
- // The page objects
- let mut id = self.offsets.pages.0;
- for page in &self.doc.pages {
- self.writer.write_obj(id, Page::new(self.offsets.page_tree)
- .media_box(Rect::new(
- 0.0, 0.0,
- page.width.to_points(), page.height.to_points())
- )
- .contents(self.offsets.contents.0 ..= self.offsets.contents.1)
- )?;
-
- id += 1;
- }
-
- Ok(())
- }
-
- /// Write the page contents.
- fn write_contents(&mut self) -> PdfResult<()> {
- let mut id = self.offsets.contents.0;
- for page in &self.doc.pages {
- for text in &page.text {
- self.write_text(id, text)?;
- id += 1;
- }
- }
- Ok(())
- }
-
- fn write_text(&mut self, id: u32, text: &DocText) -> PdfResult<()> {
- let mut object = Text::new();
- let mut current_font = 0;
-
- for command in &text.commands {
- match command {
- TextCommand::Text(string) => {
- let encoded = self.fonts[current_font].encode(&string);
- object.tj(encoded);
- },
- TextCommand::SetFont(id, size) => {
- current_font = *id;
- object.tf(*id as u32 + 1, *size);
- },
- TextCommand::Move(x, y) => { object.td(x.to_points(), y.to_points()); },
- }
- }
-
- self.writer.write_obj(id, &object.to_stream())?;
-
- Ok(())
- }
-
- /// Write the fonts.
- fn write_fonts(&mut self) -> PdfResult<()> {
- let mut id = self.offsets.fonts.0;
-
- for font in &self.fonts {
- self.writer.write_obj(id, &Type0Font::new(
- font.name.clone(),
- CMapEncoding::Predefined("Identity-H".to_owned()),
- id + 1
- ))?;
-
- self.writer.write_obj(id + 1,
- CIDFont::new(
- CIDFontType::Type2,
- font.name.clone(),
- CIDSystemInfo::new("(Adobe)", "(Identity)", 0),
- id + 2,
- ).widths(vec![WidthRecord::start(0, font.widths.clone())])
- )?;
-
- self.writer.write_obj(id + 2,
- FontDescriptor::new(
- font.name.clone(),
- font.flags,
- font.italic_angle,
- )
- .font_bbox(font.bounding_box)
- .ascent(font.ascender)
- .descent(font.descender)
- .cap_height(font.cap_height)
- .stem_v(font.stem_v)
- .font_file_3(id + 3)
- )?;
-
- self.writer.write_obj(id + 3, &FontStream::new(
- &font.program,
- EmbeddedFontType::OpenType,
- ))?;
-
- id += 4;
- }
-
- Ok(())
- }
-}
-
-/// The data we need from the font.
-struct PdfFont {
- font: Font,
- widths: Vec<GlyphUnit>,
- flags: FontFlags,
- italic_angle: f32,
- bounding_box: Rect<GlyphUnit>,
- ascender: GlyphUnit,
- descender: GlyphUnit,
- cap_height: GlyphUnit,
- stem_v: GlyphUnit,
-}
-
-impl PdfFont {
- /// Create a subetted version of the font and calculate some information
- /// needed for creating the _PDF_.
- pub fn new(font: &Font, chars: &HashSet<char>) -> PdfResult<PdfFont> {
- // Subset the font using the selected characters
- let subsetted = font.subsetted(
- chars.iter().cloned(),
- &["head", "hhea", "maxp", "hmtx", "loca", "glyf"],
- &["cvt ", "prep", "fpgm", /* "OS/2", "cmap", "name", "post" */],
- )?;
-
- // Specify flags for the font
- let mut flags = FontFlags::empty();
- flags.set(FontFlags::FIXED_PITCH, font.metrics.is_fixed_pitch);
- flags.set(FontFlags::SERIF, font.name.contains("Serif"));
- flags.insert(FontFlags::SYMBOLIC);
- flags.set(FontFlags::ITALIC, font.metrics.is_italic);
- flags.insert(FontFlags::SMALL_CAP);
-
- // Transform the widths
- let widths = subsetted.widths.iter().map(|&x| size_to_glyph_unit(x)).collect();
-
- Ok(PdfFont {
- font: subsetted,
- widths,
- flags,
- italic_angle: font.metrics.italic_angle,
- bounding_box: Rect::new(
- size_to_glyph_unit(font.metrics.bounding_box[0]),
- size_to_glyph_unit(font.metrics.bounding_box[1]),
- size_to_glyph_unit(font.metrics.bounding_box[2]),
- size_to_glyph_unit(font.metrics.bounding_box[3]),
- ),
- ascender: size_to_glyph_unit(font.metrics.ascender),
- descender: size_to_glyph_unit(font.metrics.descender),
- cap_height: size_to_glyph_unit(font.metrics.cap_height),
- stem_v: (10.0 + 0.244 * (font.metrics.weight_class as f32 - 50.0)) as GlyphUnit,
- })
- }
-}
-
-/// Convert a size into a _PDF_ glyph unit.
-fn size_to_glyph_unit(size: Size) -> GlyphUnit {
- (1000.0 * size.to_points()).round() as GlyphUnit
-}
-
-impl std::ops::Deref for PdfFont {
- type Target = Font;
-
- fn deref(&self) -> &Font {
- &self.font
- }
-}
-
-/// Result type for _PDF_ creation.
-type PdfResult<T> = std::result::Result<T, PdfError>;
-
-/// The error type for _PDF_ creation.
-pub enum PdfError {
- /// An error occured while subsetting the font for the _PDF_.
- Font(FontError),
- /// An I/O Error on the underlying writable occured.
- Io(io::Error),
-}
-
-impl error::Error for PdfError {
- #[inline]
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match self {
- PdfError::Font(err) => Some(err),
- PdfError::Io(err) => Some(err),
- }
- }
-}
-
-impl fmt::Display for PdfError {
- #[inline]
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- PdfError::Font(err) => write!(f, "font error: {}", err),
- PdfError::Io(err) => write!(f, "io error: {}", err),
- }
- }
-}
-
-impl fmt::Debug for PdfError {
- #[inline]
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(self, f)
- }
-}
-
-impl From<io::Error> for PdfError {
- #[inline]
- fn from(err: io::Error) -> PdfError {
- PdfError::Io(err)
- }
-}
-
-impl From<FontError> for PdfError {
- #[inline]
- fn from(err: FontError) -> PdfError {
- PdfError::Font(err)
- }
-}