diff --git a/crates/typst/src/export/pdf/color.rs b/crates/typst/src/export/pdf/color.rs index eb27f9693..fccaafab0 100644 --- a/crates/typst/src/export/pdf/color.rs +++ b/crates/typst/src/export/pdf/color.rs @@ -1,5 +1,4 @@ -use std::sync::Arc; - +use once_cell::sync::Lazy; use pdf_writer::types::DeviceNSubtype; use pdf_writer::{writers, Chunk, Dict, Filter, Name, Ref}; @@ -27,13 +26,18 @@ const HSL_S: Name<'static> = Name(b"S"); const HSL_L: Name<'static> = Name(b"L"); // The ICC profiles. -const SRGB_ICC: &[u8] = include_bytes!("./icc/sRGB-v4.icc"); -const GRAY_ICC: &[u8] = include_bytes!("./icc/sGrey-v4.icc"); +static SRGB_ICC_DEFLATED: Lazy> = + Lazy::new(|| deflate(include_bytes!("icc/sRGB-v4.icc"))); +static GRAY_ICC_DEFLATED: Lazy> = + Lazy::new(|| deflate(include_bytes!("icc/sGrey-v4.icc"))); // The PostScript functions for color spaces. -const OKLAB_SOURCE: &str = include_str!("./postscript/oklab.ps"); -const HSL_SOURCE: &str = include_str!("./postscript/hsl.ps"); -const HSV_SOURCE: &str = include_str!("./postscript/hsv.ps"); +static OKLAB_DEFLATED: Lazy> = + Lazy::new(|| deflate(minify(include_str!("postscript/oklab.ps")).as_bytes())); +static HSV_DEFLATED: Lazy> = + Lazy::new(|| deflate(minify(include_str!("postscript/hsl.ps")).as_bytes())); +static HSL_DEFLATED: Lazy> = + Lazy::new(|| deflate(minify(include_str!("postscript/hsv.ps")).as_bytes())); /// The color spaces present in the PDF document #[derive(Default)] @@ -161,87 +165,54 @@ impl ColorSpaces { /// Write the necessary color spaces functions and ICC profiles to the /// PDF file. - pub fn write_functions(&self, writer: &mut Chunk) { - // Write the Oklab function & color space + pub fn write_functions(&self, chunk: &mut Chunk) { + // Write the Oklab function & color space. if let Some(oklab) = self.oklab { - let code = oklab_function(); - writer - .post_script_function(oklab, &code) + chunk + .post_script_function(oklab, &OKLAB_DEFLATED) .domain([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) .range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) .filter(Filter::FlateDecode); } - // Write the HSV function & color space + // Write the HSV function & color space. if let Some(hsv) = self.hsv { - let code = hsv_function(); - writer - .post_script_function(hsv, &code) + chunk + .post_script_function(hsv, &HSV_DEFLATED) .domain([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) .range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) .filter(Filter::FlateDecode); } - // Write the HSL function & color space + // Write the HSL function & color space. if let Some(hsl) = self.hsl { - let code = hsl_function(); - writer - .post_script_function(hsl, &code) + chunk + .post_script_function(hsl, &HSL_DEFLATED) .domain([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) .range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) .filter(Filter::FlateDecode); } - // Write the sRGB color space + // Write the sRGB color space. if let Some(srgb) = self.srgb { - let profile = srgb_icc(); - writer - .icc_profile(srgb, &profile) + chunk + .icc_profile(srgb, &SRGB_ICC_DEFLATED) .n(3) - .range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]); + .range([0.0, 1.0, 0.0, 1.0, 0.0, 1.0]) + .filter(Filter::FlateDecode); } - // Write the gray color space + // Write the gray color space. if let Some(gray) = self.d65_gray { - let profile = gray_icc(); - writer.icc_profile(gray, &profile).n(1).range([0.0, 1.0]); + chunk + .icc_profile(gray, &GRAY_ICC_DEFLATED) + .n(1) + .range([0.0, 1.0]) + .filter(Filter::FlateDecode); } } } -/// Deflated sRGB ICC profile -#[comemo::memoize] -fn srgb_icc() -> Arc> { - Arc::new(deflate(SRGB_ICC)) -} - -/// Deflated gray ICC profile -#[comemo::memoize] -fn gray_icc() -> Arc> { - Arc::new(deflate(GRAY_ICC)) -} - -/// Deflated Oklab PostScript function -#[comemo::memoize] -fn oklab_function() -> Arc> { - let code = minify(OKLAB_SOURCE); - Arc::new(deflate(code.as_bytes())) -} - -/// Deflated HSV PostScript function -#[comemo::memoize] -fn hsv_function() -> Arc> { - let code = minify(HSV_SOURCE); - Arc::new(deflate(code.as_bytes())) -} - -/// Deflated HSL PostScript function -#[comemo::memoize] -fn hsl_function() -> Arc> { - let code = minify(HSL_SOURCE); - Arc::new(deflate(code.as_bytes())) -} - /// This function removes comments, line spaces and carriage returns from a /// PostScript program. This is necessary to optimize the size of the PDF file. fn minify(source: &str) -> String { diff --git a/crates/typst/src/export/pdf/extg.rs b/crates/typst/src/export/pdf/extg.rs index 47393b54b..f7bd116dd 100644 --- a/crates/typst/src/export/pdf/extg.rs +++ b/crates/typst/src/export/pdf/extg.rs @@ -1,23 +1,21 @@ -use pdf_writer::Finish; - -use crate::export::pdf::PdfContext; +use super::PdfContext; /// A PDF external graphics state. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct ExternalGraphicsState { +pub struct ExtGState { // In the range 0-255, needs to be divided before being written into the graphics state! pub stroke_opacity: u8, // In the range 0-255, needs to be divided before being written into the graphics state! pub fill_opacity: u8, } -impl Default for ExternalGraphicsState { +impl Default for ExtGState { fn default() -> Self { Self { stroke_opacity: 255, fill_opacity: 255 } } } -impl ExternalGraphicsState { +impl ExtGState { pub fn uses_opacities(&self) -> bool { self.stroke_opacity != 255 || self.fill_opacity != 255 } @@ -26,13 +24,12 @@ impl ExternalGraphicsState { /// Embed all used external graphics states into the PDF. #[tracing::instrument(skip_all)] pub fn write_external_graphics_states(ctx: &mut PdfContext) { - for external_gs in ctx.ext_gs_map.items() { - let gs_ref = ctx.alloc.bump(); - ctx.ext_gs_refs.push(gs_ref); - - let mut gs = ctx.writer.ext_graphics(gs_ref); - gs.non_stroking_alpha(external_gs.fill_opacity as f32 / 255.0) + for external_gs in ctx.extg_map.items() { + let id = ctx.alloc.bump(); + ctx.ext_gs_refs.push(id); + ctx.pdf + .ext_graphics(id) + .non_stroking_alpha(external_gs.fill_opacity as f32 / 255.0) .stroking_alpha(external_gs.stroke_opacity as f32 / 255.0); - gs.finish(); } } diff --git a/crates/typst/src/export/pdf/font.rs b/crates/typst/src/export/pdf/font.rs index e1faf1b19..bd7cfb312 100644 --- a/crates/typst/src/export/pdf/font.rs +++ b/crates/typst/src/export/pdf/font.rs @@ -58,7 +58,7 @@ pub fn write_fonts(ctx: &mut PdfContext) { }; // Write the base font object referencing the CID font. - ctx.writer + ctx.pdf .type0_font(type0_ref) .base_font(Name(base_font_type0.as_bytes())) .encoding_predefined(Name(b"Identity-H")) @@ -66,7 +66,7 @@ pub fn write_fonts(ctx: &mut PdfContext) { .to_unicode(cmap_ref); // Write the CID font referencing the font descriptor. - let mut cid = ctx.writer.cid_font(cid_ref); + let mut cid = ctx.pdf.cid_font(cid_ref); cid.subtype(if is_cff { CidFontType::Type0 } else { CidFontType::Type2 }); cid.base_font(Name(base_font.as_bytes())); cid.system_info(SYSTEM_INFO); @@ -125,7 +125,7 @@ pub fn write_fonts(ctx: &mut PdfContext) { let stem_v = 10.0 + 0.244 * (f32::from(ttf.weight().to_number()) - 50.0); // Write the font descriptor (contains metrics about the font). - let mut font_descriptor = ctx.writer.font_descriptor(descriptor_ref); + let mut font_descriptor = ctx.pdf.font_descriptor(descriptor_ref); font_descriptor .name(Name(base_font.as_bytes())) .flags(flags) @@ -147,13 +147,13 @@ pub fn write_fonts(ctx: &mut PdfContext) { // Write the /ToUnicode character map, which maps glyph ids back to // unicode codepoints to enable copying out of the PDF. let cmap = create_cmap(ttf, glyph_set); - ctx.writer.cmap(cmap_ref, &cmap.finish()); + ctx.pdf.cmap(cmap_ref, &cmap.finish()); // Subset and write the font's bytes. let glyphs: Vec<_> = glyph_set.keys().copied().collect(); let data = subset_font(font, &glyphs); - let mut stream = ctx.writer.stream(data_ref, &data); + let mut stream = ctx.pdf.stream(data_ref, &data); stream.filter(Filter::FlateDecode); if is_cff { stream.pair(Name(b"Subtype"), Name(b"CIDFontType0C")); diff --git a/crates/typst/src/export/pdf/gradient.rs b/crates/typst/src/export/pdf/gradient.rs index 23ab687cf..842c76669 100644 --- a/crates/typst/src/export/pdf/gradient.rs +++ b/crates/typst/src/export/pdf/gradient.rs @@ -35,7 +35,7 @@ pub fn write_gradients(ctx: &mut PdfContext) { let mut shading_pattern = match &gradient { Gradient::Linear(linear) => { let shading_function = shading_function(ctx, &gradient); - let mut shading_pattern = ctx.writer.shading_pattern(shading); + let mut shading_pattern = ctx.pdf.shading_pattern(shading); let mut shading = shading_pattern.function_shading(); shading.shading_type(FunctionShadingType::Axial); @@ -108,7 +108,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref { // These need to be individual function to encode 360.0 correctly. let func1 = ctx.alloc.bump(); - ctx.writer + ctx.pdf .exponential_function(func1) .range(gradient.space().range()) .c0(gradient.space().convert(first.0)) @@ -117,7 +117,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref { .n(1.0); let func2 = ctx.alloc.bump(); - ctx.writer + ctx.pdf .exponential_function(func2) .range(gradient.space().range()) .c0([1.0, s1 * (1.0 - t) + s2 * t, x1 * (1.0 - t) + x2 * t]) @@ -126,7 +126,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref { .n(1.0); let func3 = ctx.alloc.bump(); - ctx.writer + ctx.pdf .exponential_function(func3) .range(gradient.space().range()) .c0([0.0, s1 * (1.0 - t) + s2 * t, x1 * (1.0 - t) + x2 * t]) @@ -157,7 +157,7 @@ fn shading_function(ctx: &mut PdfContext, gradient: &Gradient) -> Ref { bounds.pop(); // Create the stitching function. - ctx.writer + ctx.pdf .stitching_function(function) .domain([0.0, 1.0]) .range(gradient.space().range()) @@ -178,7 +178,7 @@ fn single_gradient( ) -> Ref { let reference = ctx.alloc.bump(); - ctx.writer + ctx.pdf .exponential_function(reference) .range(color_space.range()) .c0(color_space.convert(first_color)) diff --git a/crates/typst/src/export/pdf/image.rs b/crates/typst/src/export/pdf/image.rs index 12c5fb34c..cce75bc35 100644 --- a/crates/typst/src/export/pdf/image.rs +++ b/crates/typst/src/export/pdf/image.rs @@ -15,26 +15,28 @@ use crate::{ pub fn write_images(ctx: &mut PdfContext) { for image in ctx.image_map.items() { let image_ref = ctx.alloc.bump(); - let icc_ref = ctx.alloc.bump(); ctx.image_refs.push(image_ref); - let width = image.width(); - let height = image.height(); - // Add the primary image. match image.kind() { ImageKind::Raster(raster) => { // TODO: Error if image could not be encoded. - let (data, filter, has_color) = encode_image(raster); - let mut image = ctx.writer.image_xobject(image_ref, &data); + let (data, filter, has_color) = encode_raster_image(raster); + let width = image.width(); + let height = image.height(); + + let mut image = ctx.pdf.image_xobject(image_ref, &data); image.filter(filter); image.width(width as i32); image.height(height as i32); image.bits_per_component(8); + let mut icc_ref = None; let space = image.color_space(); if raster.icc().is_some() { - space.icc_based(icc_ref); + let id = ctx.alloc.bump(); + space.icc_based(id); + icc_ref = Some(id); } else if has_color { ctx.colors.write(ColorSpace::Srgb, space, &mut ctx.alloc); } else { @@ -49,7 +51,7 @@ pub fn write_images(ctx: &mut PdfContext) { image.s_mask(mask_ref); image.finish(); - let mut mask = ctx.writer.image_xobject(mask_ref, &alpha_data); + let mut mask = ctx.pdf.image_xobject(mask_ref, &alpha_data); mask.filter(alpha_filter); mask.width(width as i32); mask.height(height as i32); @@ -59,9 +61,9 @@ pub fn write_images(ctx: &mut PdfContext) { image.finish(); } - if let Some(icc) = raster.icc() { + if let (Some(icc), Some(icc_ref)) = (raster.icc(), icc_ref) { let compressed = deflate(icc); - let mut stream = ctx.writer.icc_profile(icc_ref, &compressed); + let mut stream = ctx.pdf.icc_profile(icc_ref, &compressed); stream.filter(Filter::FlateDecode); if has_color { stream.n(3); @@ -79,7 +81,7 @@ pub fn write_images(ctx: &mut PdfContext) { let next_ref = svg2pdf::convert_tree_into( tree, svg2pdf::Options::default(), - &mut ctx.writer, + &mut ctx.pdf, image_ref, ); ctx.alloc = next_ref; @@ -95,7 +97,7 @@ pub fn write_images(ctx: &mut PdfContext) { /// Skips the alpha channel as that's encoded separately. #[comemo::memoize] #[tracing::instrument(skip_all)] -fn encode_image(image: &RasterImage) -> (Arc>, Filter, bool) { +fn encode_raster_image(image: &RasterImage) -> (Arc>, Filter, bool) { let dynamic = image.dynamic(); match (image.format(), dynamic) { // 8-bit gray JPEG. diff --git a/crates/typst/src/export/pdf/mod.rs b/crates/typst/src/export/pdf/mod.rs index 587c53617..51784fbcd 100644 --- a/crates/typst/src/export/pdf/mod.rs +++ b/crates/typst/src/export/pdf/mod.rs @@ -30,7 +30,7 @@ use crate::geom::{Abs, Dir, Em}; use crate::image::Image; use crate::model::Introspector; -use extg::ExternalGraphicsState; +use extg::ExtGState; /// Export a document into a PDF file. /// @@ -45,28 +45,21 @@ pub fn pdf(document: &Document) -> Vec { extg::write_external_graphics_states(&mut ctx); page::write_page_tree(&mut ctx); write_catalog(&mut ctx); - ctx.writer.finish() + ctx.pdf.finish() } /// Context for exporting a whole PDF document. pub struct PdfContext<'a> { + /// The document that we're currently exporting. document: &'a Document, + /// An introspector for the document, used to resolve locations links and + /// the document outline. introspector: Introspector, - writer: Pdf, - colors: ColorSpaces, + + /// The writer we are writing the PDF into. + pdf: Pdf, + /// Content of exported pages. pages: Vec, - page_heights: Vec, - alloc: Ref, - page_tree_ref: Ref, - font_refs: Vec, - image_refs: Vec, - gradient_refs: Vec, - ext_gs_refs: Vec, - page_refs: Vec, - font_map: Remapper, - image_map: Remapper, - gradient_map: Remapper, - ext_gs_map: Remapper, /// For each font a mapping from used glyphs to their text representation. /// May contain multiple chars in case of ligatures or similar things. The /// same glyph can have a different text representation within one document, @@ -74,7 +67,35 @@ pub struct PdfContext<'a> { /// PDF's /ToUnicode map for glyphs that don't have an entry in the font's /// cmap. This is important for copy-paste and searching. glyph_sets: HashMap>, + /// The number of glyphs for all referenced languages in the document. + /// We keep track of this to determine the main document language. languages: HashMap, + + /// Allocator for indirect reference IDs. + alloc: Ref, + /// The ID of the page tree. + page_tree_ref: Ref, + /// The IDs of written pages. + page_refs: Vec, + /// The IDs of written fonts. + font_refs: Vec, + /// The IDs of written images. + image_refs: Vec, + /// The IDs of written gradients. + gradient_refs: Vec, + /// The IDs of written external graphics states. + ext_gs_refs: Vec, + /// Handles color space writing. + colors: ColorSpaces, + + /// Deduplicates fonts used across the document. + font_map: Remapper, + /// Deduplicates images used across the document. + image_map: Remapper, + /// Deduplicates gradients used across the document. + gradient_map: Remapper, + /// Deduplicates external graphics states used across the document. + extg_map: Remapper, } impl<'a> PdfContext<'a> { @@ -84,10 +105,10 @@ impl<'a> PdfContext<'a> { Self { document, introspector: Introspector::new(&document.pages), - writer: Pdf::new(), - colors: ColorSpaces::default(), + pdf: Pdf::new(), pages: vec![], - page_heights: vec![], + glyph_sets: HashMap::new(), + languages: HashMap::new(), alloc, page_tree_ref, page_refs: vec![], @@ -95,12 +116,11 @@ impl<'a> PdfContext<'a> { image_refs: vec![], gradient_refs: vec![], ext_gs_refs: vec![], + colors: ColorSpaces::default(), font_map: Remapper::new(), image_map: Remapper::new(), gradient_map: Remapper::new(), - ext_gs_map: Remapper::new(), - glyph_sets: HashMap::new(), - languages: HashMap::new(), + extg_map: Remapper::new(), } } } @@ -127,7 +147,7 @@ fn write_catalog(ctx: &mut PdfContext) { let page_labels = write_page_labels(ctx); // Write the document information. - let mut info = ctx.writer.document_info(ctx.alloc.bump()); + let mut info = ctx.pdf.document_info(ctx.alloc.bump()); let mut xmp = XmpWriter::new(); if let Some(title) = &ctx.document.title { info.title(TextStr(title)); @@ -160,13 +180,13 @@ fn write_catalog(ctx: &mut PdfContext) { let xmp_buf = xmp.finish(None); let meta_ref = ctx.alloc.bump(); - let mut meta_stream = ctx.writer.stream(meta_ref, xmp_buf.as_bytes()); - meta_stream.pair(Name(b"Type"), Name(b"Metadata")); - meta_stream.pair(Name(b"Subtype"), Name(b"XML")); - meta_stream.finish(); + ctx.pdf + .stream(meta_ref, xmp_buf.as_bytes()) + .pair(Name(b"Type"), Name(b"Metadata")) + .pair(Name(b"Subtype"), Name(b"XML")); // Write the document catalog. - let mut catalog = ctx.writer.catalog(ctx.alloc.bump()); + let mut catalog = ctx.pdf.catalog(ctx.alloc.bump()); catalog.pages(ctx.page_tree_ref); catalog.viewer_preferences().direction(dir); catalog.pair(Name(b"Metadata"), meta_ref); @@ -215,7 +235,7 @@ fn write_page_labels(ctx: &mut PdfContext) -> Vec<(NonZeroUsize, Ref)> { } let id = ctx.alloc.bump(); - let mut entry = ctx.writer.indirect(id).start::(); + let mut entry = ctx.pdf.indirect(id).start::(); // Only add what is actually provided. Don't add empty prefix string if // it wasn't given for example. @@ -309,17 +329,3 @@ impl EmExt for Em { 1000.0 * self.get() as f32 } } - -/// Additional methods for [`Ref`]. -trait RefExt { - /// Bump the reference up by one and return the previous one. - fn bump(&mut self) -> Self; -} - -impl RefExt for Ref { - fn bump(&mut self) -> Self { - let prev = *self; - *self = Self::new(prev.get() + 1); - prev - } -} diff --git a/crates/typst/src/export/pdf/outline.rs b/crates/typst/src/export/pdf/outline.rs index 58db7a935..7aa15c417 100644 --- a/crates/typst/src/export/pdf/outline.rs +++ b/crates/typst/src/export/pdf/outline.rs @@ -92,7 +92,7 @@ pub fn write_outline(ctx: &mut PdfContext) -> Option { prev_ref = Some(write_outline_item(ctx, node, root_id, prev_ref, i + 1 == len)); } - ctx.writer + ctx.pdf .outline(root_id) .first(start_ref) .last(Ref::new(ctx.alloc.get() - 1)) @@ -140,7 +140,7 @@ fn write_outline_item( let id = ctx.alloc.bump(); let next_ref = Ref::new(id.get() + node.len() as i32); - let mut outline = ctx.writer.outline_item(id); + let mut outline = ctx.pdf.outline_item(id); outline.parent(parent_ref); if !is_last { @@ -164,11 +164,11 @@ fn write_outline_item( let loc = node.element.location().unwrap(); let pos = ctx.introspector.position(loc); let index = pos.page.get() - 1; - if let Some(&height) = ctx.page_heights.get(index) { + if let Some(page) = ctx.pages.get(index) { let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero()); outline.dest().page(ctx.page_refs[index]).xyz( pos.point.x.to_f32(), - height - y.to_f32(), + (page.size.y - y).to_f32(), None, ); } diff --git a/crates/typst/src/export/pdf/page.rs b/crates/typst/src/export/pdf/page.rs index ef023f229..412d753da 100644 --- a/crates/typst/src/export/pdf/page.rs +++ b/crates/typst/src/export/pdf/page.rs @@ -8,7 +8,7 @@ use pdf_writer::types::{ use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str}; use super::color::PaintEncode; -use super::extg::ExternalGraphicsState; +use super::extg::ExtGState; use super::{deflate, AbsExt, EmExt, PdfContext}; use crate::doc::{Destination, Frame, FrameItem, GroupItem, Meta, TextItem}; use crate::eval::Repr; @@ -32,7 +32,6 @@ pub fn construct_pages(ctx: &mut PdfContext, frames: &[Frame]) { pub fn construct_page(ctx: &mut PdfContext, frame: &Frame) { let page_ref = ctx.alloc.bump(); ctx.page_refs.push(page_ref); - ctx.page_heights.push(frame.height().to_f32()); let mut ctx = PageContext { parent: ctx, @@ -81,7 +80,7 @@ pub fn write_page_tree(ctx: &mut PdfContext) { write_page(ctx, i); } - let mut pages = ctx.writer.pages(ctx.page_tree_ref); + let mut pages = ctx.pdf.pages(ctx.page_tree_ref); pages .count(ctx.page_refs.len() as i32) .kids(ctx.page_refs.iter().copied()); @@ -115,7 +114,7 @@ pub fn write_page_tree(ctx: &mut PdfContext) { patterns.finish(); let mut ext_gs_states = resources.ext_g_states(); - for (gs_ref, gs) in ctx.ext_gs_map.pdf_indices(&ctx.ext_gs_refs) { + for (gs_ref, gs) in ctx.extg_map.pdf_indices(&ctx.ext_gs_refs) { let name = eco_format!("Gs{}", gs); ext_gs_states.pair(Name(name.as_bytes()), gs_ref); } @@ -125,7 +124,7 @@ pub fn write_page_tree(ctx: &mut PdfContext) { pages.finish(); // Write all of the functions used by the document. - ctx.colors.write_functions(&mut ctx.writer); + ctx.colors.write_functions(&mut ctx.pdf); } /// Write a page tree node. @@ -134,7 +133,7 @@ fn write_page(ctx: &mut PdfContext, i: usize) { let page = &ctx.pages[i]; let content_id = ctx.alloc.bump(); - let mut page_writer = ctx.writer.page(page.id); + let mut page_writer = ctx.pdf.page(page.id); page_writer.parent(ctx.page_tree_ref); let w = page.size.x.to_f32(); @@ -172,13 +171,13 @@ fn write_page(ctx: &mut PdfContext, i: usize) { let index = pos.page.get() - 1; let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero()); - if let Some(&height) = ctx.page_heights.get(index) { + if let Some(page) = ctx.pages.get(index) { annotation .action() .action_type(ActionType::GoTo) .destination() .page(ctx.page_refs[index]) - .xyz(pos.point.x.to_f32(), height - y.to_f32(), None); + .xyz(pos.point.x.to_f32(), (page.size.y - y).to_f32(), None); } } @@ -186,7 +185,7 @@ fn write_page(ctx: &mut PdfContext, i: usize) { page_writer.finish(); let data = deflate(&page.content); - ctx.writer.stream(content_id, &data).filter(Filter::FlateDecode); + ctx.pdf.stream(content_id, &data).filter(Filter::FlateDecode); } /// Data for an exported page. @@ -231,7 +230,7 @@ struct State { font: Option<(Font, Abs)>, fill: Option, fill_space: Option>, - external_graphics_state: Option, + external_graphics_state: Option, stroke: Option, stroke_space: Option>, } @@ -287,11 +286,11 @@ impl PageContext<'_, '_> { self.state = self.saves.pop().expect("missing state save"); } - fn set_external_graphics_state(&mut self, graphics_state: &ExternalGraphicsState) { + fn set_external_graphics_state(&mut self, graphics_state: &ExtGState) { let current_state = self.state.external_graphics_state.as_ref(); if current_state != Some(graphics_state) { - self.parent.ext_gs_map.insert(*graphics_state); - let name = eco_format!("Gs{}", self.parent.ext_gs_map.map(graphics_state)); + self.parent.extg_map.insert(*graphics_state); + let name = eco_format!("Gs{}", self.parent.extg_map.map(graphics_state)); self.content.set_parameters(Name(name.as_bytes())); if graphics_state.uses_opacities() { @@ -321,10 +320,7 @@ impl PageContext<'_, '_> { color.alpha().map_or(255, |v| (v * 255.0).round() as u8) }) .unwrap_or(255); - self.set_external_graphics_state(&ExternalGraphicsState { - stroke_opacity, - fill_opacity, - }); + self.set_external_graphics_state(&ExtGState { stroke_opacity, fill_opacity }); } fn transform(&mut self, transform: Transform) {