summaryrefslogtreecommitdiff
path: root/src/export
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-02-03 12:22:02 +0100
committerLaurenz <laurmaedje@gmail.com>2020-02-03 12:22:02 +0100
commit3150fd56437ecf8b2a5902c18e3f9ace800b768c (patch)
treedb8a7e9fc868145804db97da81bd0669aaf55454 /src/export
parent40ea35cbe7482ce04096c4d63a848c8601cc1848 (diff)
Better Debug/Display and Derives 🧽
Diffstat (limited to 'src/export')
-rw-r--r--src/export/pdf.rs228
1 files changed, 123 insertions, 105 deletions
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index f877d458..5c38084b 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -1,6 +1,8 @@
//! Exporting of layouts into _PDF_ documents.
use std::collections::{HashMap, HashSet};
+use std::error::Error;
+use std::fmt::{self, Display, Formatter};
use std::io::{self, Write};
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
@@ -74,8 +76,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
font_loader: &GlobalFontLoader,
target: W,
) -> PdfResult<PdfExporter<'a, W>> {
- let (fonts, font_remap) = Self::subset_fonts(layouts, font_loader)?;
- let offsets = Self::calculate_offsets(layouts.len(), fonts.len());
+ let (fonts, font_remap) = subset_fonts(layouts, font_loader)?;
+ let offsets = calculate_offsets(layouts.len(), fonts.len());
Ok(PdfExporter {
writer: PdfWriter::new(target),
@@ -86,95 +88,6 @@ impl<'a, W: Write> PdfExporter<'a, W> {
})
}
- /// Subsets all fonts and assign a new PDF-internal index to each one. The
- /// returned hash map maps the old indices (used by the layouts) to the new
- /// one used in the PDF. The new ones index into the returned vector of
- /// owned fonts.
- fn subset_fonts(
- layouts: &'a MultiLayout,
- font_loader: &GlobalFontLoader,
- ) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
- let mut fonts = Vec::new();
- let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
- let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new();
- let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new();
- let mut active_font = FontIndex::MAX;
-
- // We want to find out which fonts are used at all and which chars are
- // used for those. We use this information to create subsetted fonts.
- for layout in layouts {
- for action in &layout.actions {
- match action {
- LayoutAction::WriteText(text) => {
- font_chars
- .entry(active_font)
- .or_insert_with(HashSet::new)
- .extend(text.chars());
- },
-
- LayoutAction::SetFont(index, _) => {
- active_font = *index;
-
- let next_id = old_to_new.len();
- let new_id = *old_to_new
- .entry(active_font)
- .or_insert(next_id);
-
- new_to_old
- .entry(new_id)
- .or_insert(active_font);
- },
-
- _ => {}
- }
- }
- }
-
- let num_fonts = old_to_new.len();
- let mut font_loader = font_loader.borrow_mut();
-
- // All tables not listed here are dropped.
- let tables: Vec<_> = [
- b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
- b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
- ].iter().map(|&s| Tag(*s)).collect();
-
- // Do the subsetting.
- for index in 0 .. num_fonts {
- let old_index = new_to_old[&index];
- let font = font_loader.get_with_index(old_index);
-
- let chars = font_chars[&old_index].iter().cloned();
- let subsetted = match font.subsetted(chars, tables.iter().copied()) {
- Ok(data) => Font::from_bytes(data)?,
- Err(_) => font.clone(),
- };
-
- fonts.push(subsetted);
- }
-
- Ok((fonts, old_to_new))
- }
-
- /// We need to know in advance which IDs to use for which objects to
- /// cross-reference them. Therefore, we calculate the indices in the
- /// beginning.
- fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
- let catalog = 1;
- let page_tree = catalog + 1;
- let pages = (page_tree + 1, page_tree + layout_count as Ref);
- let contents = (pages.1 + 1, pages.1 + layout_count as Ref);
- let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref);
-
- Offsets {
- catalog,
- page_tree,
- pages,
- contents,
- fonts: font_offsets,
- }
- }
-
/// Write everything (writing entry point).
fn write(&mut self) -> PdfResult<usize> {
self.writer.write_header(Version::new(1, 7))?;
@@ -395,12 +308,100 @@ impl<'a, W: Write> PdfExporter<'a, W> {
}
}
+/// Subsets all fonts and assign a new PDF-internal index to each one. The
+/// returned hash map maps the old indices (used by the layouts) to the new one
+/// used in the PDF. The new ones index into the returned vector of owned fonts.
+fn subset_fonts(
+ layouts: &MultiLayout,
+ font_loader: &GlobalFontLoader,
+) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> {
+ let mut fonts = Vec::new();
+ let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new();
+ let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new();
+ let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new();
+ let mut active_font = FontIndex::MAX;
+
+ // We want to find out which fonts are used at all and which chars are used
+ // for those. We use this information to create subsetted fonts.
+ for layout in layouts {
+ for action in &layout.actions {
+ match action {
+ LayoutAction::WriteText(text) => {
+ font_chars
+ .entry(active_font)
+ .or_insert_with(HashSet::new)
+ .extend(text.chars());
+ },
+
+ LayoutAction::SetFont(index, _) => {
+ active_font = *index;
+
+ let next_id = old_to_new.len();
+ let new_id = *old_to_new
+ .entry(active_font)
+ .or_insert(next_id);
+
+ new_to_old
+ .entry(new_id)
+ .or_insert(active_font);
+ },
+
+ _ => {}
+ }
+ }
+ }
+
+ let num_fonts = old_to_new.len();
+ let mut font_loader = font_loader.borrow_mut();
+
+ // All tables not listed here are dropped.
+ let tables: Vec<_> = [
+ b"name", b"OS/2", b"post", b"head", b"hhea", b"hmtx", b"maxp",
+ b"cmap", b"cvt ", b"fpgm", b"prep", b"loca", b"glyf",
+ ].iter().map(|&s| Tag(*s)).collect();
+
+ // Do the subsetting.
+ for index in 0 .. num_fonts {
+ let old_index = new_to_old[&index];
+ let font = font_loader.get_with_index(old_index);
+
+ let chars = font_chars[&old_index].iter().cloned();
+ let subsetted = match font.subsetted(chars, tables.iter().copied()) {
+ Ok(data) => Font::from_bytes(data)?,
+ Err(_) => font.clone(),
+ };
+
+ fonts.push(subsetted);
+ }
+
+ Ok((fonts, old_to_new))
+}
+
+/// We need to know in advance which IDs to use for which objects to
+/// cross-reference them. Therefore, we calculate the indices in the beginning.
+fn calculate_offsets(layout_count: usize, font_count: usize) -> Offsets {
+ let catalog = 1;
+ let page_tree = catalog + 1;
+ let pages = (page_tree + 1, page_tree + layout_count as Ref);
+ let contents = (pages.1 + 1, pages.1 + layout_count as Ref);
+ let font_offsets = (contents.1 + 1, contents.1 + 5 * font_count as Ref);
+
+ Offsets {
+ catalog,
+ page_tree,
+ pages,
+ contents,
+ fonts: font_offsets,
+ }
+}
+
/// Create an iterator from a reference pair.
-fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item = Ref> {
+fn ids((start, end): (Ref, Ref)) -> impl Iterator<Item=Ref> {
start ..= end
}
/// The error type for _PDF_ exporting.
+#[derive(Debug)]
pub enum PdfExportError {
/// An error occured while subsetting the font for the _PDF_.
Font(LoadError),
@@ -408,17 +409,34 @@ pub enum PdfExportError {
Io(io::Error),
}
-error_type! {
- self: PdfExportError,
- res: PdfResult,
- show: f => match self {
- PdfExportError::Font(err) => err.fmt(f),
- PdfExportError::Io(err) => err.fmt(f),
- },
- source: match self {
- PdfExportError::Font(err) => Some(err),
- PdfExportError::Io(err) => Some(err),
- },
- from: (err: io::Error, PdfExportError::Io(err)),
- from: (err: LoadError, PdfExportError::Font(err)),
+type PdfResult<T> = Result<T, PdfExportError>;
+
+impl Error for PdfExportError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ PdfExportError::Font(err) => Some(err),
+ PdfExportError::Io(err) => Some(err),
+ }
+ }
+}
+
+impl Display for PdfExportError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ PdfExportError::Font(err) => err.fmt(f),
+ PdfExportError::Io(err) => err.fmt(f),
+ }
+ }
+}
+
+impl From<LoadError> for PdfExportError {
+ fn from(err: LoadError) -> PdfExportError {
+ PdfExportError::Font(err)
+ }
+}
+
+impl From<io::Error> for PdfExportError {
+ fn from(err: io::Error) -> PdfExportError {
+ PdfExportError::Io(err)
+ }
}