summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-11-27 23:17:25 +0100
committerLaurenz <laurmaedje@gmail.com>2020-11-27 23:17:25 +0100
commit982e7671a6c89ab189881b7b5c73aa18fee85a5f (patch)
treee4f4afb826bf96e65ebac6fe3a474a2ab44b36ca
parent98f77e4d80324f58b1c390e555057f96836fed63 (diff)
Embed each image only once into the PDF 🥔
-rw-r--r--src/export/pdf.rs103
-rw-r--r--tests/ref/image.pngbin952426 -> 952767 bytes
-rw-r--r--tests/typ/image.typ1
3 files changed, 66 insertions, 38 deletions
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index 43237dc7..fcd451d1 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -1,6 +1,8 @@
//! Exporting into _PDF_ documents.
+use std::cmp::Eq;
use std::collections::HashMap;
+use std::hash::Hash;
use fontdock::FaceId;
use image::{DynamicImage, GenericImageView};
@@ -29,21 +31,9 @@ struct PdfExporter<'a> {
writer: PdfWriter,
layouts: &'a [BoxLayout],
env: &'a Env,
- /// We need to know exactly which indirect reference id will be used for
- /// which objects up-front to correctly declare the document catalogue, page
- /// tree and so on. These offsets are computed in the beginning and stored
- /// here.
refs: Refs,
- /// We assign a new PDF-internal index to each used face.
- /// There are two mappings:
- /// Forwards from the old face ids to the new pdf indices.
- fonts_to_pdf: HashMap<FaceId, usize>,
- /// Backwards from the pdf indices to the old face ids.
- fonts_to_layout: Vec<FaceId>,
- /// The already visited images.
- images: Vec<ResourceId>,
- /// The total number of images.
- image_count: usize,
+ fonts: Remapper<FaceId>,
+ images: Remapper<ResourceId>,
}
impl<'a> PdfExporter<'a> {
@@ -51,36 +41,27 @@ impl<'a> PdfExporter<'a> {
let mut writer = PdfWriter::new(1, 7);
writer.set_indent(2);
- let mut fonts_to_pdf = HashMap::new();
- let mut fonts_to_layout = vec![];
- let mut image_count = 0;
+ let mut fonts = Remapper::new();
+ let mut images = Remapper::new();
for layout in layouts {
for (_, element) in &layout.elements {
match element {
- LayoutElement::Text(shaped) => {
- fonts_to_pdf.entry(shaped.face).or_insert_with(|| {
- let next_id = fonts_to_layout.len();
- fonts_to_layout.push(shaped.face);
- next_id
- });
- }
- LayoutElement::Image(_) => image_count += 1,
+ LayoutElement::Text(shaped) => fonts.insert(shaped.face),
+ LayoutElement::Image(image) => images.insert(image.resource),
}
}
}
- let refs = Refs::new(layouts.len(), fonts_to_pdf.len(), image_count);
+ let refs = Refs::new(layouts.len(), fonts.len(), images.len());
Self {
writer,
layouts,
env,
refs,
- fonts_to_pdf,
- fonts_to_layout,
- images: vec![],
- image_count,
+ fonts,
+ images,
}
}
@@ -102,7 +83,7 @@ impl<'a> PdfExporter<'a> {
let mut resources = pages.resources();
let mut fonts = resources.fonts();
- for (refs, f) in self.refs.fonts().zip(0 .. self.fonts_to_pdf.len()) {
+ for (refs, f) in self.refs.fonts().zip(self.fonts.pdf_indices()) {
let name = format!("F{}", f);
fonts.pair(Name(name.as_bytes()), refs.type0_font);
}
@@ -110,7 +91,7 @@ impl<'a> PdfExporter<'a> {
drop(fonts);
let mut images = resources.x_objects();
- for (id, im) in self.refs.images().zip(0 .. self.image_count) {
+ for (id, im) in self.refs.images().zip(self.images.pdf_indices()) {
let name = format!("Im{}", im);
images.pair(Name(name.as_bytes()), id);
}
@@ -158,7 +139,7 @@ impl<'a> PdfExporter<'a> {
face = shaped.face;
size = shaped.font_size;
- let name = format!("F{}", self.fonts_to_pdf[&shaped.face]);
+ let name = format!("F{}", self.fonts.map(shaped.face));
text.font(Name(name.as_bytes()), size.to_pt() as f32);
}
@@ -173,7 +154,7 @@ impl<'a> PdfExporter<'a> {
for (pos, element) in &page.elements {
if let LayoutElement::Image(image) = element {
- let name = format!("Im{}", self.images.len());
+ let name = format!("Im{}", self.images.map(image.resource));
let size = image.size;
let x = pos.x.to_pt() as f32;
let y = (page.size.height - pos.y - size.height).to_pt() as f32;
@@ -184,8 +165,6 @@ impl<'a> PdfExporter<'a> {
content.matrix(w, 0.0, 0.0, h, x, y);
content.x_object(Name(name.as_bytes()));
content.restore_state();
-
- self.images.push(image.resource);
}
}
@@ -193,7 +172,7 @@ impl<'a> PdfExporter<'a> {
}
fn write_fonts(&mut self) {
- for (refs, &face_id) in self.refs.fonts().zip(&self.fonts_to_layout) {
+ for (refs, face_id) in self.refs.fonts().zip(self.fonts.layout_indices()) {
let owned_face = self.env.fonts.get_loaded(face_id);
let face = owned_face.get();
@@ -302,7 +281,7 @@ impl<'a> PdfExporter<'a> {
}
fn write_images(&mut self) {
- for (id, &resource) in self.refs.images().zip(&self.images) {
+ for (id, resource) in self.refs.images().zip(self.images.layout_indices()) {
let image = self.env.resources.get_loaded::<DynamicImage>(resource);
let data = image.to_rgb8().into_raw();
self.writer
@@ -315,6 +294,9 @@ impl<'a> PdfExporter<'a> {
}
}
+/// We need to know exactly which indirect reference id will be used for which
+/// objects up-front to correctly declare the document catalogue, page tree and
+/// so on. These offsets are computed in the beginning and stored here.
struct Refs {
catalog: Ref,
page_tree: Ref,
@@ -380,3 +362,48 @@ impl Refs {
(self.images_start .. self.end).map(Ref::new)
}
}
+
+/// Used to assign new, consecutive PDF-internal indices to things.
+struct Remapper<Index> {
+ /// Forwards from the old indices to the new pdf indices.
+ to_pdf: HashMap<Index, usize>,
+ /// Backwards from the pdf indices to the old indices.
+ to_layout: Vec<Index>,
+}
+
+impl<Index> Remapper<Index>
+where
+ Index: Copy + Eq + Hash,
+{
+ fn new() -> Self {
+ Self {
+ to_pdf: HashMap::new(),
+ to_layout: vec![],
+ }
+ }
+
+ fn len(&self) -> usize {
+ self.to_layout.len()
+ }
+
+ fn insert(&mut self, index: Index) {
+ let to_layout = &mut self.to_layout;
+ self.to_pdf.entry(index).or_insert_with(|| {
+ let pdf_index = to_layout.len();
+ to_layout.push(index);
+ pdf_index
+ });
+ }
+
+ fn map(&self, index: Index) -> usize {
+ self.to_pdf[&index]
+ }
+
+ fn pdf_indices(&self) -> impl Iterator<Item = usize> {
+ 0 .. self.to_pdf.len()
+ }
+
+ fn layout_indices(&self) -> impl Iterator<Item = Index> + '_ {
+ self.to_layout.iter().copied()
+ }
+}
diff --git a/tests/ref/image.png b/tests/ref/image.png
index 932f0c8b..41fa6a9b 100644
--- a/tests/ref/image.png
+++ b/tests/ref/image.png
Binary files differ
diff --git a/tests/typ/image.typ b/tests/typ/image.typ
index b0fd4b51..ee82024f 100644
--- a/tests/typ/image.typ
+++ b/tests/typ/image.typ
@@ -4,6 +4,7 @@
[pagebreak]
+# Tiger
[image: "res/tiger.jpg", width=3cm]
[image: "res/tiger.jpg", height=3cm]