Enhance PDF crate API 🚀

This commit is contained in:
Laurenz 2019-02-27 18:42:33 +01:00 committed by Laurenz Mädje
parent 6b8da16be8
commit da60261376

View File

@ -4,10 +4,12 @@ use std::fmt;
use std::io::{self, Write, Cursor}; use std::io::{self, Write, Cursor};
use crate::doc::Document; use crate::doc::Document;
use pdf::{PdfWriter, Id, Rect, Version, Trailer}; use pdf::{PdfWriter, Id, Rect, Version, Trailer};
use pdf::doc::{DocumentCatalog, PageTree, Page, PageData, Resource, Content}; use pdf::doc::{Catalog, PageTree, Page, Resource, Content};
use pdf::text::Text; use pdf::text::Text;
use pdf::font::{Type0Font, CMapEncoding, CIDFont, CIDFontType, CIDSystemInfo, use pdf::font::{
WidthRecord, FontDescriptor, EmbeddedFont, GlyphUnit}; Type0Font, CMapEncoding, CIDFont, CIDFontType, CIDSystemInfo,
WidthRecord, FontDescriptor, FontFlags, EmbeddedFont, GlyphUnit
};
use opentype::{OpenTypeReader, tables}; use opentype::{OpenTypeReader, tables};
@ -52,18 +54,6 @@ impl fmt::Display for PdfWritingError {
} }
/// Shortcut macro to create bitflags from bools.
macro_rules! flags {
($($bit:expr => $value:expr),*) => {{
let mut flags = 0;
$(
flags |= if $value { 1 << ($bit - 1) } else { 0 };
)*
flags
}};
($($bit:expr => $value:expr,)*) => (flags!($($bit => $value),*));
}
/// Keeps track of the document while letting the pdf writer /// Keeps track of the document while letting the pdf writer
/// generate the _PDF_. /// generate the _PDF_.
struct PdfCreator<'a, W: Write> { struct PdfCreator<'a, W: Write> {
@ -140,9 +130,7 @@ impl<'a, W: Write> PdfCreator<'a, W> {
self.writer.write_xref_table()?; self.writer.write_xref_table()?;
// Trailer // Trailer
self.writer.write_trailer(&Trailer { self.writer.write_trailer(&Trailer::new(self.offsets.catalog))?;
root: self.offsets.catalog,
})?;
Ok(self.writer.written()) Ok(self.writer.written())
} }
@ -150,19 +138,13 @@ impl<'a, W: Write> PdfCreator<'a, W> {
/// Write the document catalog, page tree and pages. /// Write the document catalog, page tree and pages.
fn write_pages(&mut self) -> PdfResult<()> { fn write_pages(&mut self) -> PdfResult<()> {
// The document catalog // The document catalog
self.writer.write_obj(self.offsets.catalog, &DocumentCatalog { self.writer.write_obj(self.offsets.catalog, &Catalog::new(self.offsets.page_tree))?;
page_tree: self.offsets.page_tree,
})?;
// Root page tree // Root page tree
self.writer.write_obj(self.offsets.page_tree, &PageTree { self.writer.write_obj(self.offsets.page_tree, PageTree::new()
parent: None, .kids(self.offsets.pages.0 ..= self.offsets.pages.1)
kids: (self.offsets.pages.0 ..= self.offsets.pages.1).collect(), .resource(Resource::Font { nr: 1, id: self.offsets.fonts.0 })
data: PageData { )?;
resources: Some(vec![Resource::Font { nr: 1, id: self.offsets.fonts.0 }]),
.. PageData::none()
},
})?;
// The page objects // The page objects
let mut id = self.offsets.pages.0; let mut id = self.offsets.pages.0;
@ -170,15 +152,10 @@ impl<'a, W: Write> PdfCreator<'a, W> {
let width = page.size[0].to_points(); let width = page.size[0].to_points();
let height = page.size[1].to_points(); let height = page.size[1].to_points();
let contents = (self.offsets.contents.0 ..= self.offsets.contents.1).collect(); self.writer.write_obj(id, Page::new(self.offsets.page_tree)
self.writer.write_obj(id, &Page { .media_box(Rect::new(0.0, 0.0, width, height))
parent: self.offsets.page_tree, .contents(self.offsets.contents.0 ..= self.offsets.contents.1)
data: PageData { )?;
media_box: Some(Rect::new(0.0, 0.0, width, height)),
contents: Some(contents),
.. PageData::none()
},
})?;
id += 1; id += 1;
} }
@ -216,54 +193,50 @@ impl<'a, W: Write> PdfCreator<'a, W> {
let base_font = font_data.name.post_script_name.as_ref() let base_font = font_data.name.post_script_name.as_ref()
.unwrap_or(&self.doc.font); .unwrap_or(&self.doc.font);
self.writer.write_obj(id, &Type0Font { self.writer.write_obj(id, &Type0Font::new(
base_font: base_font.clone(), base_font.clone(),
encoding: CMapEncoding::Predefined("Identity-H".to_owned()), CMapEncoding::Predefined("Identity-H".to_owned()),
descendant_font: id + 1, id + 1
to_unicode: None, )).unwrap();
}).unwrap();
self.writer.write_obj(id + 1, &CIDFont { self.writer.write_obj(id + 1,
subtype: CIDFontType::Type2, CIDFont::new(
base_font: base_font.clone(), CIDFontType::Type2,
cid_system_info: CIDSystemInfo { base_font.clone(),
registry: "(Adobe)".to_owned(), CIDSystemInfo::new("(Adobe)", "(Identity)", 0),
ordering: "(Identity)".to_owned(), id + 2,
supplement: 0, ).widths(vec![
}, WidthRecord::start(0, font_data.hmtx.metrics.iter().map(|m| convert(m.advance_width))
font_descriptor: id + 2, )])
widths: Some(vec![WidthRecord::Start(0, ).unwrap();
font_data.hmtx.metrics.iter()
.map(|m| convert(m.advance_width))
.collect::<Vec<_>>()
)]),
cid_to_gid_map: Some(CMapEncoding::Predefined("Identity".to_owned())),
}).unwrap();
self.writer.write_obj(id + 2, &FontDescriptor { let mut flags = FontFlags::empty();
font_name: base_font.clone(), flags.set(FontFlags::FIXED_PITCH, font_data.post.is_fixed_pitch);
flags: flags!( flags.set(FontFlags::SERIF, base_font.contains("Serif"));
1 => font_data.post.is_fixed_pitch, flags.insert(FontFlags::SYMBOLIC);
2 => base_font.contains("Serif"), flags.set(FontFlags::ITALIC, (font_data.head.mac_style & 1) != 0);
3 => true, 4 => false, 6 => false, flags.insert(FontFlags::SMALL_CAP);
7 => (font_data.head.mac_style & 1) != 0,
17 => false, 18 => true, 19 => false, self.writer.write_obj(id + 2,
), FontDescriptor::new(
found_bbox: Rect::new( base_font.clone(),
flags,
font_data.post.italic_angle.to_f32(),
)
.font_bbox(Rect::new(
convert(font_data.head.x_min), convert(font_data.head.x_min),
convert(font_data.head.y_min), convert(font_data.head.y_min),
convert(font_data.head.x_max), convert(font_data.head.x_max),
convert(font_data.head.y_max) convert(font_data.head.y_max)
), ))
italic_angle: font_data.post.italic_angle.to_f32(), .ascent(convert(font_data.os2.s_typo_ascender))
ascent: convert(font_data.os2.s_typo_ascender), .descent(convert(font_data.os2.s_typo_descender))
descent: convert(font_data.os2.s_typo_descender), .cap_height(convert(font_data.os2.s_cap_height
cap_height: convert(font_data.os2.s_cap_height .unwrap_or(font_data.os2.s_typo_ascender)))
.unwrap_or(font_data.os2.s_typo_ascender)), .stem_v((10.0 + 220.0 * (font_data.os2.us_weight_class as f32
stem_v: (10.0 + 220.0 * - 50.0) / 900.0) as GlyphUnit)
(font_data.os2.us_weight_class as f32 - 50.0) / 900.0) as GlyphUnit, .font_file_3(id + 3)
font_file_3: Some(id + 3), ).unwrap();
}).unwrap();
self.writer.write_obj(id + 3, &EmbeddedFont::OpenType(&font_data.data)).unwrap(); self.writer.write_obj(id + 3, &EmbeddedFont::OpenType(&font_data.data)).unwrap();