From ce875d32f0c8638e89229f165d092dcc6afa4472 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 12 Jun 2023 13:51:10 +0200 Subject: [PATCH] Memoize subsetting and image encoding --- src/export/pdf/font.rs | 25 ++++++++++++++----------- src/export/pdf/image.rs | 37 +++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/export/pdf/font.rs b/src/export/pdf/font.rs index c61425995..d5de51188 100644 --- a/src/export/pdf/font.rs +++ b/src/export/pdf/font.rs @@ -7,7 +7,8 @@ use ttf_parser::{name_id, GlyphId, Tag}; use unicode_general_category::GeneralCategory; use super::{deflate, EmExt, PdfContext, RefExt}; -use crate::util::SliceExt; +use crate::font::Font; +use crate::util::{Buffer, SliceExt}; const CMAP_NAME: Name = Name(b"Custom"); const SYSTEM_INFO: SystemInfo = SystemInfo { @@ -138,16 +139,8 @@ pub fn write_fonts(ctx: &mut PdfContext) { ctx.writer.cmap(cmap_ref, &cmap.finish()); // Subset and write the font's bytes. - let data = font.data(); - let subsetted = { - let glyphs: Vec<_> = glyph_set.keys().copied().collect(); - let profile = subsetter::Profile::pdf(&glyphs); - subsetter::subset(data, font.index(), profile) - }; - - // Compress and write the font's bytes. - let data = subsetted.as_deref().unwrap_or(data); - let data = deflate(data); + let glyphs: Vec<_> = glyph_set.keys().copied().collect(); + let data = subset_font(font, &glyphs); let mut stream = ctx.writer.stream(data_ref, &data); stream.filter(Filter::FlateDecode); @@ -159,6 +152,16 @@ pub fn write_fonts(ctx: &mut PdfContext) { } } +/// Subset a font to the given glyphs. +#[comemo::memoize] +fn subset_font(font: &Font, glyphs: &[u16]) -> Buffer { + let data = font.data(); + let profile = subsetter::Profile::pdf(glyphs); + let subsetted = subsetter::subset(data, font.index(), profile); + let data = subsetted.as_deref().unwrap_or(data); + deflate(data).into() +} + /// Create a /ToUnicode CMap. fn create_cmap( ttf: &ttf_parser::Face, diff --git a/src/export/pdf/image.rs b/src/export/pdf/image.rs index 7d5656ca0..a7ec47443 100644 --- a/src/export/pdf/image.rs +++ b/src/export/pdf/image.rs @@ -1,10 +1,11 @@ use std::io::Cursor; -use image::{DynamicImage, GenericImageView, ImageResult, Rgba}; +use image::{DynamicImage, GenericImageView, Rgba}; use pdf_writer::{Filter, Finish}; use super::{deflate, PdfContext, RefExt}; -use crate::image::{DecodedImage, RasterFormat}; +use crate::image::{DecodedImage, Image, RasterFormat}; +use crate::util::Buffer; /// Embed all used images into the PDF. #[tracing::instrument(skip_all)] @@ -20,9 +21,9 @@ pub fn write_images(ctx: &mut PdfContext) { // Add the primary image. // TODO: Error if image could not be encoded. match image.decoded().as_ref() { - DecodedImage::Raster(dynamic, icc, format) => { + DecodedImage::Raster(dynamic, icc, _) => { // TODO: Error if image could not be encoded. - let (data, filter, has_color) = encode_image(*format, dynamic).unwrap(); + let (data, filter, has_color) = encode_image(image); let mut image = ctx.writer.image_xobject(image_ref, &data); image.filter(filter); image.width(width as i32); @@ -86,24 +87,28 @@ pub fn write_images(ctx: &mut PdfContext) { /// whether the image has color. /// /// Skips the alpha channel as that's encoded separately. +#[comemo::memoize] #[tracing::instrument(skip_all)] -fn encode_image( - format: RasterFormat, - dynamic: &DynamicImage, -) -> ImageResult<(Vec, Filter, bool)> { - Ok(match (format, dynamic) { +fn encode_image(image: &Image) -> (Buffer, Filter, bool) { + let decoded = image.decoded(); + let (dynamic, format) = match decoded.as_ref() { + DecodedImage::Raster(dynamic, _, format) => (dynamic, *format), + _ => panic!("can only encode raster image"), + }; + + match (format, dynamic) { // 8-bit gray JPEG. (RasterFormat::Jpg, DynamicImage::ImageLuma8(_)) => { let mut data = Cursor::new(vec![]); - dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?; - (data.into_inner(), Filter::DctDecode, false) + dynamic.write_to(&mut data, image::ImageFormat::Jpeg).unwrap(); + (data.into_inner().into(), Filter::DctDecode, false) } // 8-bit RGB JPEG (CMYK JPEGs get converted to RGB earlier). (RasterFormat::Jpg, DynamicImage::ImageRgb8(_)) => { let mut data = Cursor::new(vec![]); - dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?; - (data.into_inner(), Filter::DctDecode, true) + dynamic.write_to(&mut data, image::ImageFormat::Jpeg).unwrap(); + (data.into_inner().into(), Filter::DctDecode, true) } // TODO: Encode flate streams with PNG-predictor? @@ -111,7 +116,7 @@ fn encode_image( // 8-bit gray PNG. (RasterFormat::Png, DynamicImage::ImageLuma8(luma)) => { let data = deflate(luma.as_raw()); - (data, Filter::FlateDecode, false) + (data.into(), Filter::FlateDecode, false) } // Anything else (including Rgb(a) PNGs). @@ -125,9 +130,9 @@ fn encode_image( } let data = deflate(&pixels); - (data, Filter::FlateDecode, true) + (data.into(), Filter::FlateDecode, true) } - }) + } } /// Encode an image's alpha channel if present.