mirror of
https://github.com/typst/typst
synced 2025-05-17 02:25:27 +08:00
Memoize subsetting and image encoding
This commit is contained in:
parent
638a534273
commit
ce875d32f0
@ -7,7 +7,8 @@ use ttf_parser::{name_id, GlyphId, Tag};
|
|||||||
use unicode_general_category::GeneralCategory;
|
use unicode_general_category::GeneralCategory;
|
||||||
|
|
||||||
use super::{deflate, EmExt, PdfContext, RefExt};
|
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 CMAP_NAME: Name = Name(b"Custom");
|
||||||
const SYSTEM_INFO: SystemInfo = SystemInfo {
|
const SYSTEM_INFO: SystemInfo = SystemInfo {
|
||||||
@ -138,16 +139,8 @@ pub fn write_fonts(ctx: &mut PdfContext) {
|
|||||||
ctx.writer.cmap(cmap_ref, &cmap.finish());
|
ctx.writer.cmap(cmap_ref, &cmap.finish());
|
||||||
|
|
||||||
// Subset and write the font's bytes.
|
// Subset and write the font's bytes.
|
||||||
let data = font.data();
|
let glyphs: Vec<_> = glyph_set.keys().copied().collect();
|
||||||
let subsetted = {
|
let data = subset_font(font, &glyphs);
|
||||||
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 mut stream = ctx.writer.stream(data_ref, &data);
|
let mut stream = ctx.writer.stream(data_ref, &data);
|
||||||
stream.filter(Filter::FlateDecode);
|
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.
|
/// Create a /ToUnicode CMap.
|
||||||
fn create_cmap(
|
fn create_cmap(
|
||||||
ttf: &ttf_parser::Face,
|
ttf: &ttf_parser::Face,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use image::{DynamicImage, GenericImageView, ImageResult, Rgba};
|
use image::{DynamicImage, GenericImageView, Rgba};
|
||||||
use pdf_writer::{Filter, Finish};
|
use pdf_writer::{Filter, Finish};
|
||||||
|
|
||||||
use super::{deflate, PdfContext, RefExt};
|
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.
|
/// Embed all used images into the PDF.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
@ -20,9 +21,9 @@ pub fn write_images(ctx: &mut PdfContext) {
|
|||||||
// Add the primary image.
|
// Add the primary image.
|
||||||
// TODO: Error if image could not be encoded.
|
// TODO: Error if image could not be encoded.
|
||||||
match image.decoded().as_ref() {
|
match image.decoded().as_ref() {
|
||||||
DecodedImage::Raster(dynamic, icc, format) => {
|
DecodedImage::Raster(dynamic, icc, _) => {
|
||||||
// TODO: Error if image could not be encoded.
|
// 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);
|
let mut image = ctx.writer.image_xobject(image_ref, &data);
|
||||||
image.filter(filter);
|
image.filter(filter);
|
||||||
image.width(width as i32);
|
image.width(width as i32);
|
||||||
@ -86,24 +87,28 @@ pub fn write_images(ctx: &mut PdfContext) {
|
|||||||
/// whether the image has color.
|
/// whether the image has color.
|
||||||
///
|
///
|
||||||
/// Skips the alpha channel as that's encoded separately.
|
/// Skips the alpha channel as that's encoded separately.
|
||||||
|
#[comemo::memoize]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn encode_image(
|
fn encode_image(image: &Image) -> (Buffer, Filter, bool) {
|
||||||
format: RasterFormat,
|
let decoded = image.decoded();
|
||||||
dynamic: &DynamicImage,
|
let (dynamic, format) = match decoded.as_ref() {
|
||||||
) -> ImageResult<(Vec<u8>, Filter, bool)> {
|
DecodedImage::Raster(dynamic, _, format) => (dynamic, *format),
|
||||||
Ok(match (format, dynamic) {
|
_ => panic!("can only encode raster image"),
|
||||||
|
};
|
||||||
|
|
||||||
|
match (format, dynamic) {
|
||||||
// 8-bit gray JPEG.
|
// 8-bit gray JPEG.
|
||||||
(RasterFormat::Jpg, DynamicImage::ImageLuma8(_)) => {
|
(RasterFormat::Jpg, DynamicImage::ImageLuma8(_)) => {
|
||||||
let mut data = Cursor::new(vec![]);
|
let mut data = Cursor::new(vec![]);
|
||||||
dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?;
|
dynamic.write_to(&mut data, image::ImageFormat::Jpeg).unwrap();
|
||||||
(data.into_inner(), Filter::DctDecode, false)
|
(data.into_inner().into(), Filter::DctDecode, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8-bit RGB JPEG (CMYK JPEGs get converted to RGB earlier).
|
// 8-bit RGB JPEG (CMYK JPEGs get converted to RGB earlier).
|
||||||
(RasterFormat::Jpg, DynamicImage::ImageRgb8(_)) => {
|
(RasterFormat::Jpg, DynamicImage::ImageRgb8(_)) => {
|
||||||
let mut data = Cursor::new(vec![]);
|
let mut data = Cursor::new(vec![]);
|
||||||
dynamic.write_to(&mut data, image::ImageFormat::Jpeg)?;
|
dynamic.write_to(&mut data, image::ImageFormat::Jpeg).unwrap();
|
||||||
(data.into_inner(), Filter::DctDecode, true)
|
(data.into_inner().into(), Filter::DctDecode, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Encode flate streams with PNG-predictor?
|
// TODO: Encode flate streams with PNG-predictor?
|
||||||
@ -111,7 +116,7 @@ fn encode_image(
|
|||||||
// 8-bit gray PNG.
|
// 8-bit gray PNG.
|
||||||
(RasterFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
(RasterFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
||||||
let data = deflate(luma.as_raw());
|
let data = deflate(luma.as_raw());
|
||||||
(data, Filter::FlateDecode, false)
|
(data.into(), Filter::FlateDecode, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything else (including Rgb(a) PNGs).
|
// Anything else (including Rgb(a) PNGs).
|
||||||
@ -125,9 +130,9 @@ fn encode_image(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data = deflate(&pixels);
|
let data = deflate(&pixels);
|
||||||
(data, Filter::FlateDecode, true)
|
(data.into(), Filter::FlateDecode, true)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode an image's alpha channel if present.
|
/// Encode an image's alpha channel if present.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user