From 5589869386d07f03d2b34c3088a99135b0188f9d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 2 Oct 2023 15:23:00 +0200 Subject: [PATCH 1/2] Allow writing into chunks --- Cargo.lock | 4 +-- Cargo.toml | 3 +++ src/lib.rs | 56 ++++++++++++++++++++--------------------- src/render/clip_path.rs | 16 ++++++------ src/render/gradient.rs | 52 +++++++++++++++++++------------------- src/render/group.rs | 24 +++++++++--------- src/render/image.rs | 22 ++++++++-------- src/render/mask.rs | 16 ++++++------ src/render/mod.rs | 16 ++++++------ src/render/path.rs | 40 ++++++++++++++--------------- src/render/pattern.rs | 8 +++--- 11 files changed, 130 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3fb4c7e..9562c3c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,11 +912,11 @@ dependencies = [ [[package]] name = "pdf-writer" version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d77bc47c8968aa63f86a7e6693e270a6cbd1e3b784c364f1711a0ddecc71447" +source = "git+https://github.com/typst/pdf-writer?branch=chunks#1b03c04c9bc0e91bc956a7b30972cdc798cb65f8" dependencies = [ "bitflags 1.3.2", "itoa", + "memchr", "ryu", ] diff --git a/Cargo.toml b/Cargo.toml index 2b41aac9..3e7fd051 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,6 @@ fontdb = { version = "0.14", optional= true } [dev-dependencies] usvg = { version = "0.35.0" } + +[patch.crates-io] +pdf-writer = { git = "https://github.com/typst/pdf-writer", branch = "chunks" } diff --git a/src/lib.rs b/src/lib.rs index f5c2e409..38e0430d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ Among the unsupported features are currently: mod render; mod util; -use pdf_writer::{Content, Filter, Finish, PdfWriter, Rect, Ref, TextStr}; +use pdf_writer::{Chunk, Content, Filter, Finish, Pdf, Rect, Ref, TextStr}; use usvg::utils::view_box_to_transform; use usvg::{Align, AspectRatio, NonZeroRect, Size, Transform, Tree, TreeParsing}; @@ -161,35 +161,35 @@ pub fn convert_str(src: &str, options: Options) -> Result, usvg::Error> pub fn convert_tree(tree: &Tree, options: Options) -> Vec { let pdf_size = pdf_size(tree, options); let mut ctx = Context::new(tree, options, None); - let mut writer = PdfWriter::new(); + let mut pdf = Pdf::new(); let catalog_ref = ctx.alloc_ref(); let page_tree_ref = ctx.alloc_ref(); let page_ref = ctx.alloc_ref(); let content_ref = ctx.alloc_ref(); - writer.catalog(catalog_ref).pages(page_tree_ref); - writer.pages(page_tree_ref).count(1).kids([page_ref]); + pdf.catalog(catalog_ref).pages(page_tree_ref); + pdf.pages(page_tree_ref).count(1).kids([page_ref]); // Generate main content ctx.deferrer.push(); let mut content = Content::new(); tree_to_stream( tree, - &mut writer, + &mut pdf, &mut content, &mut ctx, initial_transform(options.aspect, tree, pdf_size), ); let content_stream = ctx.finish_content(content); - let mut stream = writer.stream(content_ref, &content_stream); + let mut stream = pdf.stream(content_ref, &content_stream); if ctx.options.compress { stream.filter(Filter::FlateDecode); } stream.finish(); - let mut page = writer.page(page_ref); + let mut page = pdf.page(page_ref); let mut page_resources = page.resources(); ctx.deferrer.pop(&mut page_resources); page_resources.finish(); @@ -206,27 +206,27 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec { page.finish(); let document_info_id = ctx.alloc_ref(); - writer.document_info(document_info_id).producer(TextStr("svg2pdf")); + pdf.document_info(document_info_id).producer(TextStr("svg2pdf")); - writer.finish() + pdf.finish() } /// Convert a [`usvg` tree](Tree) into a Form XObject that can be used as /// part of a larger document. /// -/// This method is intended for use in an existing [`PdfWriter`] workflow. It -/// will always return an XObject with the width and height of one printer's +/// This method is intended for use in an existing [`pdf-writer`] workflow. It +/// will always produce an XObject with the width and height of one printer's /// point, just like an [`ImageXObject`](pdf_writer::writers::ImageXObject) /// would. /// -/// The resulting object can be used by registering a name and the `id` with a -/// page's [`/XObject`](pdf_writer::writers::Resources::x_objects) resources -/// dictionary and then invoking the [`Do`](Content::x_object) operator with the -/// name in the page's content stream. +/// The resulting object can be used by registering a name and the `start_ref` +/// with a page's [`/XObject`](pdf_writer::writers::Resources::x_objects) +/// resources dictionary and then invoking the [`Do`](Content::x_object) +/// operator with the name in the page's content stream. /// /// As the conversion process may need to create multiple indirect objects in -/// the PDF, this function allocates consecutive IDs starting at `id` for its -/// objects and returns the next available ID for your future writing. +/// the PDF, this function allocates consecutive IDs starting at `start_ref` for +/// its objects and returns the next available ID for your future writing. /// /// ## Example /// Write a PDF file with some text and an SVG graphic. @@ -248,12 +248,12 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec { /// let svg_name = Name(b"S1"); /// /// // Start writing a PDF. -/// let mut writer = PdfWriter::new(); -/// writer.catalog(catalog_id).pages(page_tree_id); -/// writer.pages(page_tree_id).kids([page_id]).count(1); +/// let mut pdf = Pdf::new(); +/// pdf.catalog(catalog_id).pages(page_tree_id); +/// pdf.pages(page_tree_id).kids([page_id]).count(1); /// /// // Set up a simple A4 page. -/// let mut page = writer.page(page_id); +/// let mut page = pdf.page(page_id); /// page.media_box(Rect::new(0.0, 0.0, 595.0, 842.0)); /// page.parent(page_tree_id); /// page.contents(content_id); @@ -267,7 +267,7 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec { /// page.finish(); /// /// // Set a predefined font, so we do not have to load anything extra. -/// writer.type1_font(font_id).base_font(Name(b"Helvetica")); +/// pdf.type1_font(font_id).base_font(Name(b"Helvetica")); /// /// // Let's add an SVG graphic to this file. /// // We need to load its source first and manually parse it into a usvg Tree. @@ -280,7 +280,7 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec { /// // This call allocates some indirect object reference IDs for itself. If we /// // wanted to write some more indirect objects afterwards, we could use the /// // return value as the next unused reference ID. -/// svg2pdf::convert_tree_into(&tree, svg2pdf::Options::default(), &mut writer, svg_id); +/// svg2pdf::convert_tree_into(&tree, svg2pdf::Options::default(), &mut pdf, svg_id); /// /// // Write a content stream with some text and our SVG. /// let mut content = Content::new(); @@ -297,14 +297,14 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec { /// .x_object(svg_name); /// /// // Write the file to the disk. -/// writer.stream(content_id, &content.finish()); -/// std::fs::write("target/embedded.pdf", writer.finish())?; +/// pdf.stream(content_id, &content.finish()); +/// std::fs::write("target/embedded.pdf", pdf.finish())?; /// # Ok(()) } /// ``` pub fn convert_tree_into( tree: &Tree, options: Options, - writer: &mut PdfWriter, + chunk: &mut Chunk, start_ref: Ref, ) -> Ref { let pdf_size = pdf_size(tree, options); @@ -316,14 +316,14 @@ pub fn convert_tree_into( let mut content = Content::new(); tree_to_stream( tree, - writer, + chunk, &mut content, &mut ctx, initial_transform(options.aspect, tree, pdf_size), ); let content_stream = ctx.finish_content(content); - let mut x_object = writer.form_xobject(x_ref, &content_stream); + let mut x_object = chunk.form_xobject(x_ref, &content_stream); x_object.bbox(Rect::new(0.0, 0.0, pdf_size.width(), pdf_size.height())); x_object.matrix([ 1.0 / pdf_size.width(), diff --git a/src/render/clip_path.rs b/src/render/clip_path.rs index 2cc88926..6ac91425 100644 --- a/src/render/clip_path.rs +++ b/src/render/clip_path.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use pdf_writer::types::MaskType; -use pdf_writer::{Content, Filter, Finish, PdfWriter}; +use pdf_writer::{Chunk, Content, Filter, Finish}; use usvg::tiny_skia_path::PathSegment; use usvg::{ClipPath, FillRule, Node, NodeKind, Transform, Units, Visibility}; @@ -14,7 +14,7 @@ use crate::util::helper::{plain_bbox, NameExt, RectExt, TransformExt}; pub fn render( node: &Node, clip_path: Rc, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, ) { @@ -41,7 +41,7 @@ pub fn render( create_simple_clip_path(node, clip_path, content); } else { content.set_parameters( - create_complex_clip_path(node, clip_path, writer, ctx).to_pdf_name(), + create_complex_clip_path(node, clip_path, chunk, ctx).to_pdf_name(), ); } } @@ -139,7 +139,7 @@ fn extend_segments_from_node( fn create_complex_clip_path( parent: &Node, clip_path: Rc, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Rc { ctx.deferrer.push(); @@ -149,7 +149,7 @@ fn create_complex_clip_path( content.save_state(); if let Some(recursive_clip_path) = &clip_path.clip_path { - render(parent, recursive_clip_path.clone(), writer, &mut content, ctx); + render(parent, recursive_clip_path.clone(), chunk, &mut content, ctx); } content.transform(clip_path.transform.to_pdf_transform()); @@ -166,7 +166,7 @@ fn create_complex_clip_path( group::render( &clip_path.root, group, - writer, + chunk, &mut content, ctx, Transform::default(), @@ -178,7 +178,7 @@ fn create_complex_clip_path( content.restore_state(); let content_stream = ctx.finish_content(content); - let mut x_object = writer.form_xobject(x_object_reference, &content_stream); + let mut x_object = chunk.form_xobject(x_object_reference, &content_stream); if ctx.options.compress { x_object.filter(Filter::FlateDecode); @@ -198,7 +198,7 @@ fn create_complex_clip_path( x_object.finish(); let gs_ref = ctx.alloc_ref(); - let mut gs = writer.ext_graphics(gs_ref); + let mut gs = chunk.ext_graphics(gs_ref); gs.soft_mask().subtype(MaskType::Alpha).group(x_object_reference); ctx.deferrer.add_graphics_state(gs_ref) diff --git a/src/render/gradient.rs b/src/render/gradient.rs index 9d087122..27f8af17 100644 --- a/src/render/gradient.rs +++ b/src/render/gradient.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use pdf_writer::types::{FunctionShadingType, MaskType}; -use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Ref}; +use pdf_writer::{Chunk, Content, Filter, Finish, Name, Ref}; use usvg::{NonZeroRect, NormalizedF32, Paint, StopOffset, Transform, Units}; use crate::util::context::Context; @@ -51,12 +51,12 @@ impl GradientProperties { pub fn create_shading_pattern( paint: &Paint, parent_bbox: &NonZeroRect, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, accumulated_transform: &Transform, ) -> Rc { let properties = GradientProperties::try_from_paint(paint).unwrap(); - shading_pattern(&properties, parent_bbox, writer, ctx, accumulated_transform) + shading_pattern(&properties, parent_bbox, chunk, ctx, accumulated_transform) } /// Return a soft mask that will render the stop opacities of a gradient into a gray scale @@ -64,12 +64,12 @@ pub fn create_shading_pattern( pub fn create_shading_soft_mask( paint: &Paint, parent_bbox: &NonZeroRect, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Option> { let properties = GradientProperties::try_from_paint(paint).unwrap(); if properties.stops.iter().any(|stop| stop.opacity.get() < 1.0) { - Some(shading_soft_mask(&properties, parent_bbox, writer, ctx)) + Some(shading_soft_mask(&properties, parent_bbox, chunk, ctx)) } else { None } @@ -78,7 +78,7 @@ pub fn create_shading_soft_mask( fn shading_pattern( properties: &GradientProperties, parent_bbox: &NonZeroRect, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, accumulated_transform: &Transform, ) -> Rc { @@ -92,8 +92,8 @@ fn shading_pattern( }) .pre_concat(properties.transform); - let shading_ref = shading_function(properties, writer, ctx, false); - let mut shading_pattern = writer.shading_pattern(pattern_ref); + let shading_ref = shading_function(properties, chunk, ctx, false); + let mut shading_pattern = chunk.shading_pattern(pattern_ref); shading_pattern.pair(Name(b"Shading"), shading_ref); shading_pattern.matrix(matrix.to_pdf_transform()); shading_pattern.finish(); @@ -104,12 +104,12 @@ fn shading_pattern( fn shading_soft_mask( properties: &GradientProperties, parent_bbox: &NonZeroRect, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Rc { ctx.deferrer.push(); let x_object_id = ctx.alloc_ref(); - let shading_ref = shading_function(properties, writer, ctx, true); + let shading_ref = shading_function(properties, chunk, ctx, true); let shading_name = ctx.deferrer.add_shading(shading_ref); let bbox = ctx.get_rect().to_pdf_rect(); @@ -126,7 +126,7 @@ fn shading_soft_mask( content.shading(shading_name.to_pdf_name()); let content_stream = ctx.finish_content(content); - let mut x_object = writer.form_xobject(x_object_id, &content_stream); + let mut x_object = chunk.form_xobject(x_object_id, &content_stream); ctx.deferrer.pop(&mut x_object.resources()); x_object @@ -145,7 +145,7 @@ fn shading_soft_mask( x_object.finish(); let gs_ref = ctx.alloc_ref(); - let mut gs = writer.ext_graphics(gs_ref); + let mut gs = chunk.ext_graphics(gs_ref); gs.soft_mask() .subtype(MaskType::Luminosity) .group(x_object_id) @@ -156,14 +156,14 @@ fn shading_soft_mask( fn shading_function( properties: &GradientProperties, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, use_opacities: bool, ) -> Ref { let shading_ref = ctx.alloc_ref(); - let function_ref = function(&properties.stops, writer, ctx, use_opacities); + let function_ref = function(&properties.stops, chunk, ctx, use_opacities); - let mut shading = writer.function_shading(shading_ref); + let mut shading = chunk.function_shading(shading_ref); shading.shading_type(properties.shading_type); if use_opacities { shading.color_space().d65_gray(); @@ -180,7 +180,7 @@ fn shading_function( fn function( stops: &[usvg::Stop], - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, use_opacities: bool, ) -> Ref { @@ -209,29 +209,29 @@ fn function( if use_opacities { let stops = stops.iter().map(|s| s.opacity_stops()).collect::>>(); - select_function(&stops, writer, ctx) + select_function(&stops, chunk, ctx) } else { let stops = stops.iter().map(|s| s.color_stops()).collect::>>(); - select_function(&stops, writer, ctx) + select_function(&stops, chunk, ctx) } } fn select_function( stops: &[Stop], - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Ref { if stops.len() == 2 { - exponential_function(&stops[0], &stops[1], writer, ctx) + exponential_function(&stops[0], &stops[1], chunk, ctx) } else { - stitching_function(stops, writer, ctx) + stitching_function(stops, chunk, ctx) } } /// Create a stitching function for multiple gradient stops. fn stitching_function( stops: &[Stop], - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Ref { assert!(!stops.is_empty()); @@ -245,13 +245,13 @@ fn stitching_function( for window in stops.windows(2) { let (first, second) = (&window[0], &window[1]); bounds.push(second.offset); - functions.push(exponential_function(first, second, writer, ctx)); + functions.push(exponential_function(first, second, chunk, ctx)); encode.extend([0.0, 1.0]); } bounds.pop(); - let mut stitching_function = writer.stitching_function(reference); + let mut stitching_function = chunk.stitching_function(reference); stitching_function.domain([0.0, 1.0]); stitching_function.range(get_function_range(COUNT)); stitching_function.functions(functions); @@ -264,11 +264,11 @@ fn stitching_function( fn exponential_function( first_stop: &Stop, second_stop: &Stop, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Ref { let reference = ctx.alloc_ref(); - let mut exp = writer.exponential_function(reference); + let mut exp = chunk.exponential_function(reference); exp.range(get_function_range(COUNT)); exp.c0(first_stop.color); diff --git a/src/render/group.rs b/src/render/group.rs index 76745ce3..181510d1 100644 --- a/src/render/group.rs +++ b/src/render/group.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use pdf_writer::{Content, Filter, Finish, PdfWriter}; +use pdf_writer::{Chunk, Content, Filter, Finish}; use usvg::{Node, Transform}; use super::{clip_path, mask, Render}; @@ -13,7 +13,7 @@ use crate::util::helper::{ pub fn render( node: &Node, group: &usvg::Group, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, @@ -21,7 +21,7 @@ pub fn render( if group.is_isolated() { content.save_state(); let gs_ref = ctx.alloc_ref(); - let mut gs = writer.ext_graphics(gs_ref); + let mut gs = chunk.ext_graphics(gs_ref); gs.non_stroking_alpha(group.opacity.get()) .stroking_alpha(group.opacity.get()) .blend_mode(group.blend_mode.to_pdf_blend_mode()); @@ -34,11 +34,11 @@ pub fn render( // that it will also be affected by the transforms in the content stream. If we passed on the // accumulated transform, they would be applied twice. content.x_object( - create_x_object(node, group, writer, ctx, Transform::default()).to_pdf_name(), + create_x_object(node, group, chunk, ctx, Transform::default()).to_pdf_name(), ); content.restore_state(); } else { - create_to_stream(node, group, writer, content, ctx, accumulated_transform); + create_to_stream(node, group, chunk, content, ctx, accumulated_transform); } } @@ -47,7 +47,7 @@ pub fn render( fn create_x_object( node: &Node, group: &usvg::Group, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, accumulated_transform: Transform, ) -> Rc { @@ -61,11 +61,11 @@ fn create_x_object( let mut content = Content::new(); - create_to_stream(node, group, writer, &mut content, ctx, accumulated_transform); + create_to_stream(node, group, chunk, &mut content, ctx, accumulated_transform); let content_stream = ctx.finish_content(content); - let mut x_object = writer.form_xobject(x_ref, &content_stream); + let mut x_object = chunk.form_xobject(x_ref, &content_stream); ctx.deferrer.pop(&mut x_object.resources()); if ctx.options.compress { @@ -91,7 +91,7 @@ fn create_x_object( fn create_to_stream( node: &Node, group: &usvg::Group, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, @@ -101,15 +101,15 @@ fn create_to_stream( let accumulated_transform = accumulated_transform.pre_concat(group.transform); if let Some(mask) = &group.mask { - mask::render(node, mask.clone(), writer, content, ctx); + mask::render(node, mask.clone(), chunk, content, ctx); } if let Some(clip_path) = &group.clip_path { - clip_path::render(node, clip_path.clone(), writer, content, ctx); + clip_path::render(node, clip_path.clone(), chunk, content, ctx); } for child in node.children() { - child.render(writer, content, ctx, accumulated_transform); + child.render(chunk, content, ctx, accumulated_transform); } content.restore_state(); diff --git a/src/render/image.rs b/src/render/image.rs index 32bbbbdb..c1255253 100644 --- a/src/render/image.rs +++ b/src/render/image.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use image::{ColorType, DynamicImage, ImageFormat, Luma, Rgb, Rgba}; use miniz_oxide::deflate::{compress_to_vec_zlib, CompressionLevel}; -use pdf_writer::{Content, Filter, Finish, PdfWriter}; +use pdf_writer::{Chunk, Content, Filter, Finish}; use usvg::{ImageKind, Size, Transform, Tree, Visibility}; use crate::util::context::Context; @@ -13,7 +13,7 @@ use crate::{convert_tree_into, Options}; /// Render an image into a content stream. pub fn render( image: &usvg::Image, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, ) { @@ -30,7 +30,7 @@ pub fn render( image::load_from_memory_with_format(content, ImageFormat::Jpeg).unwrap(); // JPEGs don't support alphas, so no extra processing is required. create_raster_image( - writer, + chunk, ctx, content, Filter::DctDecode, @@ -45,7 +45,7 @@ pub fn render( // step. let (samples, filter, alpha_mask) = handle_transparent_image(&dynamic_image); create_raster_image( - writer, + chunk, ctx, &samples, filter, @@ -60,7 +60,7 @@ pub fn render( // step. let (samples, filter, alpha_mask) = handle_transparent_image(&dynamic_image); create_raster_image( - writer, + chunk, ctx, &samples, filter, @@ -69,7 +69,7 @@ pub fn render( ) } // SVGs just get rendered recursively. - ImageKind::SVG(tree) => create_svg_image(tree, writer, ctx), + ImageKind::SVG(tree) => create_svg_image(tree, chunk, ctx), }; // Get the dimensions of the actual rect that is needed to scale the image into the image view @@ -151,7 +151,7 @@ fn handle_transparent_image(image: &DynamicImage) -> (Vec, Filter, Option i32 { fn create_svg_image( tree: &Tree, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> (Rc, Size) { let image_ref = ctx.alloc_ref(); let image_name = ctx.deferrer.add_x_object(image_ref); // convert_tree_into will automatically scale it in a way so that its dimensions are 1x1, like // regular ImageXObjects. So afterwards, we can just treat them the same. - let next_ref = convert_tree_into(tree, Options::default(), writer, image_ref); + let next_ref = convert_tree_into(tree, Options::default(), chunk, image_ref); ctx.deferrer.set_next_ref(next_ref.get()); (image_name, tree.size) } diff --git a/src/render/mask.rs b/src/render/mask.rs index 04f0476f..c0bdae05 100644 --- a/src/render/mask.rs +++ b/src/render/mask.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use pdf_writer::{Content, Filter, Finish, PdfWriter}; +use pdf_writer::{Chunk, Content, Filter, Finish}; use usvg::{Mask, Node, NodeKind, Transform, Units}; use super::group; @@ -13,11 +13,11 @@ use crate::util::helper::{ pub fn render( node: &Node, mask: Rc, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, ) { - content.set_parameters(create(node, mask, writer, ctx).to_pdf_name()); + content.set_parameters(create(node, mask, chunk, ctx).to_pdf_name()); } /// Turn a mask into an graphics state object. Returns the name (= the name in the `Resources` dictionary) of @@ -25,7 +25,7 @@ pub fn render( pub fn create( parent: &Node, mask: Rc, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, ) -> Rc { let x_ref = ctx.alloc_ref(); @@ -35,7 +35,7 @@ pub fn create( content.save_state(); if let Some(recursive_mask) = &mask.mask { - render(parent, recursive_mask.clone(), writer, &mut content, ctx); + render(parent, recursive_mask.clone(), chunk, &mut content, ctx); } let parent_svg_bbox = plain_bbox(parent, true); @@ -62,7 +62,7 @@ pub fn create( group::render( &mask.root, group, - writer, + chunk, &mut content, ctx, accumulated_transform, @@ -74,7 +74,7 @@ pub fn create( content.restore_state(); let content_stream = ctx.finish_content(content); - let mut x_object = writer.form_xobject(x_ref, &content_stream); + let mut x_object = chunk.form_xobject(x_ref, &content_stream); ctx.deferrer.pop(&mut x_object.resources()); if ctx.options.compress { @@ -93,7 +93,7 @@ pub fn create( x_object.finish(); let gs_ref = ctx.alloc_ref(); - let mut gs = writer.ext_graphics(gs_ref); + let mut gs = chunk.ext_graphics(gs_ref); gs.soft_mask().subtype(mask.kind.to_pdf_mask_type()).group(x_ref); ctx.deferrer.add_graphics_state(gs_ref) diff --git a/src/render/mod.rs b/src/render/mod.rs index 17ce3b23..14e5589c 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,4 +1,4 @@ -use pdf_writer::{Content, PdfWriter}; +use pdf_writer::{Chunk, Content}; use usvg::{Node, NodeKind, Transform, Tree}; use crate::util::context::Context; @@ -17,7 +17,7 @@ pub mod pattern; /// right bounding boxes. pub fn tree_to_stream( tree: &Tree, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, initial_transform: Transform, @@ -26,14 +26,14 @@ pub fn tree_to_stream( let initial_transform = initial_transform.pre_concat(ctx.get_view_box_transform()); content.transform(initial_transform.to_pdf_transform()); - tree.root.render(writer, content, ctx, initial_transform); + tree.root.render(chunk, content, ctx, initial_transform); content.restore_state(); } trait Render { fn render( &self, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, @@ -43,20 +43,20 @@ trait Render { impl Render for Node { fn render( &self, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, ) { match *self.borrow() { NodeKind::Path(ref path) => { - path::render(self, path, writer, content, ctx, accumulated_transform) + path::render(self, path, chunk, content, ctx, accumulated_transform) } NodeKind::Group(ref group) => { - group::render(self, group, writer, content, ctx, accumulated_transform) + group::render(self, group, chunk, content, ctx, accumulated_transform) } #[cfg(feature = "image")] - NodeKind::Image(ref image) => image::render(image, writer, content, ctx), + NodeKind::Image(ref image) => image::render(image, chunk, content, ctx), // Texts should be converted beforehand. _ => {} } diff --git a/src/render/path.rs b/src/render/path.rs index b040c693..abc00383 100644 --- a/src/render/path.rs +++ b/src/render/path.rs @@ -1,6 +1,6 @@ use pdf_writer::types::ColorSpaceOperand; use pdf_writer::types::ColorSpaceOperand::Pattern; -use pdf_writer::{Content, Finish, PdfWriter}; +use pdf_writer::{Chunk, Content, Finish}; use usvg::tiny_skia_path::PathSegment; use usvg::{Fill, FillRule, Node, Opacity, Paint, PaintOrder}; use usvg::{Path, Visibility}; @@ -17,7 +17,7 @@ use crate::util::helper::{ pub fn render( node: &Node, path: &Path, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, @@ -37,12 +37,12 @@ pub fn render( // higher file sizes depending on the SVG. match path.paint_order { PaintOrder::FillAndStroke => { - fill(path, node, writer, content, ctx, accumulated_transform); - stroke(path, node, writer, content, ctx, accumulated_transform); + fill(path, node, chunk, content, ctx, accumulated_transform); + stroke(path, node, chunk, content, ctx, accumulated_transform); } PaintOrder::StrokeAndFill => { - stroke(path, node, writer, content, ctx, accumulated_transform); - fill(path, node, writer, content, ctx, accumulated_transform); + stroke(path, node, chunk, content, ctx, accumulated_transform); + fill(path, node, chunk, content, ctx, accumulated_transform); } } } @@ -95,7 +95,7 @@ pub fn draw_path(path_data: impl Iterator, content: &mut Con fn stroke( path: &Path, node: &Node, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, @@ -111,7 +111,7 @@ fn stroke( match paint { Paint::Color(c) => { - set_opacity_gs(writer, content, ctx, Some(stroke.opacity), None); + set_opacity_gs(chunk, content, ctx, Some(stroke.opacity), None); content.set_stroke_color_space(ColorSpaceOperand::Named(SRGB)); content.set_stroke_color(c.to_pdf_color()); } @@ -124,7 +124,7 @@ fn stroke( let pattern_name = pattern::create( p.clone(), &path_bbox_without_stroke, - writer, + chunk, ctx, accumulated_transform, Some(stroke.opacity), @@ -136,7 +136,7 @@ fn stroke( // In XPDF, the opacity will only be applied to the gradient if we also set the // fill opacity. Unfortunately, in muPDF it still doesn't work. set_opacity_gs( - writer, + chunk, content, ctx, Some(stroke.opacity), @@ -146,7 +146,7 @@ fn stroke( if let Some(soft_mask) = gradient::create_shading_soft_mask( paint, &path_bbox_with_stroke, - writer, + chunk, ctx, ) { content.set_parameters(soft_mask.to_pdf_name()); @@ -155,7 +155,7 @@ fn stroke( let pattern_name = gradient::create_shading_pattern( paint, &path_bbox_without_stroke, - writer, + chunk, ctx, &accumulated_transform, ); @@ -182,7 +182,7 @@ fn stroke( fn fill( path: &Path, node: &Node, - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, accumulated_transform: Transform, @@ -197,7 +197,7 @@ fn fill( match paint { Paint::Color(c) => { - set_opacity_gs(writer, content, ctx, None, Some(fill.opacity)); + set_opacity_gs(chunk, content, ctx, None, Some(fill.opacity)); content.set_fill_color_space(ColorSpaceOperand::Named(SRGB)); content.set_fill_color(c.to_pdf_color()); } @@ -206,7 +206,7 @@ fn fill( let pattern_name = pattern::create( p.clone(), &path_bbox, - writer, + chunk, ctx, accumulated_transform, Some(fill.opacity), @@ -215,10 +215,10 @@ fn fill( content.set_fill_pattern(None, pattern_name.to_pdf_name()); } Paint::LinearGradient(_) | Paint::RadialGradient(_) => { - set_opacity_gs(writer, content, ctx, None, Some(fill.opacity)); + set_opacity_gs(chunk, content, ctx, None, Some(fill.opacity)); if let Some(soft_mask) = - gradient::create_shading_soft_mask(paint, &path_bbox, writer, ctx) + gradient::create_shading_soft_mask(paint, &path_bbox, chunk, ctx) { content.set_parameters(soft_mask.to_pdf_name()); }; @@ -226,7 +226,7 @@ fn fill( let pattern_name = gradient::create_shading_pattern( paint, &path_bbox, - writer, + chunk, ctx, &accumulated_transform, ); @@ -253,7 +253,7 @@ fn finish_path(stroke: Option<&Stroke>, fill: Option<&Fill>, content: &mut Conte } fn set_opacity_gs( - writer: &mut PdfWriter, + chunk: &mut Chunk, content: &mut Content, ctx: &mut Context, stroke_opacity: Option, @@ -267,7 +267,7 @@ fn set_opacity_gs( } let gs_ref = ctx.alloc_ref(); - let mut gs = writer.ext_graphics(gs_ref); + let mut gs = chunk.ext_graphics(gs_ref); gs.non_stroking_alpha(fill_opacity) .stroking_alpha(stroke_opacity) .finish(); diff --git a/src/render/pattern.rs b/src/render/pattern.rs index b89bbcbb..22616781 100644 --- a/src/render/pattern.rs +++ b/src/render/pattern.rs @@ -2,7 +2,7 @@ use std::ops::Mul; use std::rc::Rc; use pdf_writer::types::{PaintType, TilingType}; -use pdf_writer::{Content, Filter, PdfWriter}; +use pdf_writer::{Chunk, Content, Filter}; use usvg::utils::view_box_to_transform; use usvg::{NodeKind, NonZeroRect, Opacity, Size, Transform, Units}; @@ -15,7 +15,7 @@ use crate::util::helper::TransformExt; pub fn create( pattern: Rc, parent_bbox: &NonZeroRect, - writer: &mut PdfWriter, + chunk: &mut Chunk, ctx: &mut Context, matrix: Transform, initial_opacity: Option, @@ -75,7 +75,7 @@ pub fn create( group::render( &pattern.root, group, - writer, + chunk, &mut content, ctx, Transform::default(), @@ -85,7 +85,7 @@ pub fn create( let content_stream = ctx.finish_content(content); - let mut tiling_pattern = writer.tiling_pattern(pattern_ref, &content_stream); + let mut tiling_pattern = chunk.tiling_pattern(pattern_ref, &content_stream); if ctx.options.compress { tiling_pattern.filter(Filter::FlateDecode); From e7bc9d3cb72b276e82448cd830b85bc39dd36996 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 4 Oct 2023 13:06:36 +0200 Subject: [PATCH 2/2] Bump pdf-writer to 0.9 --- Cargo.lock | 5 +++-- Cargo.toml | 5 +---- tests/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9562c3c8..6fe52712 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -911,8 +911,9 @@ dependencies = [ [[package]] name = "pdf-writer" -version = "0.8.1" -source = "git+https://github.com/typst/pdf-writer?branch=chunks#1b03c04c9bc0e91bc956a7b30972cdc798cb65f8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b651409cd03bf702052d7491f08d27fbd6b25f96dc8b9b873ada7d0b3342b8d" dependencies = [ "bitflags 1.3.2", "itoa", diff --git a/Cargo.toml b/Cargo.toml index 3e7fd051..98895a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ required-features = ["cli"] [dependencies] miniz_oxide = "0.7" -pdf-writer = "0.8" +pdf-writer = "0.9" usvg = { version = "0.35", default-features = false } image = { version = "0.24", default-features = false, features = ["jpeg", "png", "gif"], optional = true } termcolor = { version = "1", optional = true } @@ -45,6 +45,3 @@ fontdb = { version = "0.14", optional= true } [dev-dependencies] usvg = { version = "0.35.0" } - -[patch.crates-io] -pdf-writer = { git = "https://github.com/typst/pdf-writer", branch = "chunks" } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 8687b4a9..eb4b1cb3 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -34,7 +34,7 @@ usvg = "0.35.0" pdfium-render = "0.8.6" walkdir = "2.3.3" lazy_static = "1.4.0" -pdf-writer = "0.8" +pdf-writer = "0.9" image = "0.24" indicatif = "0.17.5" oxipng = { version = "8.0.0", default-features = false, features = ["filetime", "parallel", "zopfli"] }