Memoize subsetting and image encoding

This commit is contained in:
Laurenz 2023-06-12 13:51:10 +02:00
parent 638a534273
commit ce875d32f0
2 changed files with 35 additions and 27 deletions

View File

@ -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,

View File

@ -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<u8>, 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.