mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Bump pdf-writer
This commit is contained in:
parent
d971ac9a8c
commit
2982020480
@ -23,7 +23,7 @@ fxhash = "0.2.1"
|
|||||||
image = { version = "0.23", default-features = false, features = ["png", "jpeg"] }
|
image = { version = "0.23", default-features = false, features = ["png", "jpeg"] }
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
miniz_oxide = "0.4"
|
miniz_oxide = "0.4"
|
||||||
pdf-writer = { git = "https://github.com/typst/pdf-writer", rev = "27b207a" }
|
pdf-writer = "0.4"
|
||||||
rustybuzz = "0.4"
|
rustybuzz = "0.4"
|
||||||
serde = { version = "1", features = ["derive", "rc"] }
|
serde = { version = "1", features = ["derive", "rc"] }
|
||||||
ttf-parser = "0.12"
|
ttf-parser = "0.12"
|
||||||
|
@ -7,11 +7,9 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use image::{DynamicImage, GenericImageView, ImageFormat, ImageResult, Rgba};
|
use image::{DynamicImage, GenericImageView, ImageFormat, ImageResult, Rgba};
|
||||||
use pdf_writer::types::{
|
use pdf_writer::types::{
|
||||||
ActionType, AnnotationType, CidFontType, ColorSpace, FontFlags, SystemInfo,
|
ActionType, AnnotationType, CidFontType, FontFlags, SystemInfo, UnicodeCmap,
|
||||||
};
|
|
||||||
use pdf_writer::{
|
|
||||||
Content, Filter, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr, UnicodeCmap,
|
|
||||||
};
|
};
|
||||||
|
use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr};
|
||||||
use ttf_parser::{name_id, GlyphId, Tag};
|
use ttf_parser::{name_id, GlyphId, Tag};
|
||||||
|
|
||||||
use super::subset;
|
use super::subset;
|
||||||
@ -121,13 +119,14 @@ impl<'a> PdfExporter<'a> {
|
|||||||
|
|
||||||
// Write the CID font referencing the font descriptor.
|
// Write the CID font referencing the font descriptor.
|
||||||
self.writer
|
self.writer
|
||||||
.cid_font(cid_ref, subtype)
|
.cid_font(cid_ref)
|
||||||
|
.subtype(subtype)
|
||||||
.base_font(base_font)
|
.base_font(base_font)
|
||||||
.system_info(system_info)
|
.system_info(system_info)
|
||||||
.font_descriptor(descriptor_ref)
|
.font_descriptor(descriptor_ref)
|
||||||
.cid_to_gid_map_predefined(Name(b"Identity"))
|
.cid_to_gid_map_predefined(Name(b"Identity"))
|
||||||
.widths()
|
.widths()
|
||||||
.individual(0, {
|
.consecutive(0, {
|
||||||
let num_glyphs = ttf.number_of_glyphs();
|
let num_glyphs = ttf.number_of_glyphs();
|
||||||
(0 .. num_glyphs).map(|g| {
|
(0 .. num_glyphs).map(|g| {
|
||||||
let x = ttf.glyph_hor_advance(GlyphId(g)).unwrap_or(0);
|
let x = ttf.glyph_hor_advance(GlyphId(g)).unwrap_or(0);
|
||||||
@ -159,9 +158,9 @@ impl<'a> PdfExporter<'a> {
|
|||||||
// Write the font descriptor (contains metrics about the font).
|
// Write the font descriptor (contains metrics about the font).
|
||||||
self.writer
|
self.writer
|
||||||
.font_descriptor(descriptor_ref)
|
.font_descriptor(descriptor_ref)
|
||||||
.font_name(base_font)
|
.name(base_font)
|
||||||
.font_flags(flags)
|
.flags(flags)
|
||||||
.font_bbox(bbox)
|
.bbox(bbox)
|
||||||
.italic_angle(italic_angle)
|
.italic_angle(italic_angle)
|
||||||
.ascent(ascender)
|
.ascent(ascender)
|
||||||
.descent(descender)
|
.descent(descender)
|
||||||
@ -219,14 +218,20 @@ impl<'a> PdfExporter<'a> {
|
|||||||
let height = img.height();
|
let height = img.height();
|
||||||
|
|
||||||
// Add the primary image.
|
// Add the primary image.
|
||||||
if let Ok((data, filter, color_space)) = encode_image(img) {
|
if let Ok((data, filter, has_color)) = encode_image(img) {
|
||||||
let mut image = self.writer.image(image_ref, &data);
|
let mut image = self.writer.image_xobject(image_ref, &data);
|
||||||
image.filter(filter);
|
image.filter(filter);
|
||||||
image.width(width as i32);
|
image.width(width as i32);
|
||||||
image.height(height as i32);
|
image.height(height as i32);
|
||||||
image.color_space(color_space);
|
|
||||||
image.bits_per_component(8);
|
image.bits_per_component(8);
|
||||||
|
|
||||||
|
let space = image.color_space();
|
||||||
|
if has_color {
|
||||||
|
space.device_rgb();
|
||||||
|
} else {
|
||||||
|
space.device_gray();
|
||||||
|
}
|
||||||
|
|
||||||
// Add a second gray-scale image containing the alpha values if
|
// Add a second gray-scale image containing the alpha values if
|
||||||
// this image has an alpha channel.
|
// this image has an alpha channel.
|
||||||
if img.buf.color().has_alpha() {
|
if img.buf.color().has_alpha() {
|
||||||
@ -235,21 +240,22 @@ impl<'a> PdfExporter<'a> {
|
|||||||
image.s_mask(mask_ref);
|
image.s_mask(mask_ref);
|
||||||
image.finish();
|
image.finish();
|
||||||
|
|
||||||
let mut mask = self.writer.image(mask_ref, &alpha_data);
|
let mut mask = self.writer.image_xobject(mask_ref, &alpha_data);
|
||||||
mask.filter(alpha_filter);
|
mask.filter(alpha_filter);
|
||||||
mask.width(width as i32);
|
mask.width(width as i32);
|
||||||
mask.height(height as i32);
|
mask.height(height as i32);
|
||||||
mask.color_space(ColorSpace::DeviceGray);
|
mask.color_space().device_gray();
|
||||||
mask.bits_per_component(8);
|
mask.bits_per_component(8);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Warn that image could not be encoded.
|
// TODO: Warn that image could not be encoded.
|
||||||
self.writer
|
self.writer
|
||||||
.image(image_ref, &[])
|
.image_xobject(image_ref, &[])
|
||||||
.width(0)
|
.width(0)
|
||||||
.height(0)
|
.height(0)
|
||||||
.color_space(ColorSpace::DeviceGray)
|
.bits_per_component(1)
|
||||||
.bits_per_component(1);
|
.color_space()
|
||||||
|
.device_gray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,7 +299,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut pages = self.writer.pages(page_tree_ref);
|
let mut pages = self.writer.pages(page_tree_ref);
|
||||||
pages.kids(page_refs);
|
pages.count(page_refs.len() as i32).kids(page_refs);
|
||||||
|
|
||||||
let mut resources = pages.resources();
|
let mut resources = pages.resources();
|
||||||
let mut fonts = resources.fonts();
|
let mut fonts = resources.fonts();
|
||||||
@ -314,15 +320,10 @@ impl<'a> PdfExporter<'a> {
|
|||||||
resources.finish();
|
resources.finish();
|
||||||
pages.finish();
|
pages.finish();
|
||||||
|
|
||||||
// The document information.
|
// Write the document information, catalog and wrap it up!
|
||||||
let mut doc_info = self.writer.document_info(self.alloc.bump());
|
self.writer.document_info(self.alloc.bump()).creator(TextStr("Typst"));
|
||||||
doc_info.creator(TextStr("Typst"));
|
self.writer.catalog(self.alloc.bump()).pages(page_tree_ref);
|
||||||
doc_info.finish();
|
self.writer.finish()
|
||||||
|
|
||||||
// The document catalog.
|
|
||||||
let catalog_ref = self.alloc.bump();
|
|
||||||
self.writer.catalog(catalog_ref).pages(page_tree_ref);
|
|
||||||
self.writer.finish(catalog_ref)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,23 +632,24 @@ impl<'a> PageExporter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode an image with a suitable filter.
|
/// Encode an image with a suitable filter and return the data, filter and
|
||||||
|
/// whether the image has color.
|
||||||
///
|
///
|
||||||
/// Skips the alpha channel as that's encoded separately.
|
/// Skips the alpha channel as that's encoded separately.
|
||||||
fn encode_image(img: &Image) -> ImageResult<(Vec<u8>, Filter, ColorSpace)> {
|
fn encode_image(img: &Image) -> ImageResult<(Vec<u8>, Filter, bool)> {
|
||||||
Ok(match (img.format, &img.buf) {
|
Ok(match (img.format, &img.buf) {
|
||||||
// 8-bit gray JPEG.
|
// 8-bit gray JPEG.
|
||||||
(ImageFormat::Jpeg, DynamicImage::ImageLuma8(_)) => {
|
(ImageFormat::Jpeg, DynamicImage::ImageLuma8(_)) => {
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
img.buf.write_to(&mut data, img.format)?;
|
img.buf.write_to(&mut data, img.format)?;
|
||||||
(data, Filter::DctDecode, ColorSpace::DeviceGray)
|
(data, 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).
|
||||||
(ImageFormat::Jpeg, DynamicImage::ImageRgb8(_)) => {
|
(ImageFormat::Jpeg, DynamicImage::ImageRgb8(_)) => {
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
img.buf.write_to(&mut data, img.format)?;
|
img.buf.write_to(&mut data, img.format)?;
|
||||||
(data, Filter::DctDecode, ColorSpace::DeviceRgb)
|
(data, Filter::DctDecode, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Encode flate streams with PNG-predictor?
|
// TODO: Encode flate streams with PNG-predictor?
|
||||||
@ -655,7 +657,7 @@ fn encode_image(img: &Image) -> ImageResult<(Vec<u8>, Filter, ColorSpace)> {
|
|||||||
// 8-bit gray PNG.
|
// 8-bit gray PNG.
|
||||||
(ImageFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
(ImageFormat::Png, DynamicImage::ImageLuma8(luma)) => {
|
||||||
let data = deflate(luma.as_raw());
|
let data = deflate(luma.as_raw());
|
||||||
(data, Filter::FlateDecode, ColorSpace::DeviceGray)
|
(data, Filter::FlateDecode, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything else (including Rgb(a) PNGs).
|
// Anything else (including Rgb(a) PNGs).
|
||||||
@ -669,7 +671,7 @@ fn encode_image(img: &Image) -> ImageResult<(Vec<u8>, Filter, ColorSpace)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data = deflate(&pixels);
|
let data = deflate(&pixels);
|
||||||
(data, Filter::FlateDecode, ColorSpace::DeviceRgb)
|
(data, Filter::FlateDecode, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user