From c4dd6fa06227b8c4a04cf488f371f52d5379ef9b Mon Sep 17 00:00:00 2001 From: Florent Michel <56166507+FlorentCLMichel@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:05:22 +0100 Subject: [PATCH] Adjust the number of color components written to the pdf according to the color space (#4568) --- Cargo.lock | 1 + Cargo.toml | 1 + crates/typst-pdf/Cargo.toml | 1 + crates/typst-pdf/src/color.rs | 33 +++++++++++++++++++----------- crates/typst-pdf/src/gradient.rs | 35 ++++++++++++++++---------------- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f238f4f56..4c2e4d386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2774,6 +2774,7 @@ dependencies = [ name = "typst-pdf" version = "0.11.0" dependencies = [ + "arrayvec", "base64", "bytemuck", "comemo", diff --git a/Cargo.toml b/Cargo.toml index 098900620..773544ebb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ typst-timing = { path = "crates/typst-timing", version = "0.11.0" } typst-utils = { path = "crates/typst-utils", version = "0.11.0" } typst-assets = { git = "https://github.com/typst/typst-assets", rev = "4ee794c" } typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "48a924d" } +arrayvec = "0.7.4" az = "1.2" base64 = "0.22" bitflags = { version = "2", features = ["serde"] } diff --git a/crates/typst-pdf/Cargo.toml b/crates/typst-pdf/Cargo.toml index a3a693f38..cdd65e828 100644 --- a/crates/typst-pdf/Cargo.toml +++ b/crates/typst-pdf/Cargo.toml @@ -26,6 +26,7 @@ indexmap = { workspace = true } miniz_oxide = { workspace = true } once_cell = { workspace = true } pdf-writer = { workspace = true } +arrayvec = { workspace = true } subsetter = { workspace = true } svg2pdf = { workspace = true } ttf-parser = { workspace = true } diff --git a/crates/typst-pdf/src/color.rs b/crates/typst-pdf/src/color.rs index dbd07d636..e414d255d 100644 --- a/crates/typst-pdf/src/color.rs +++ b/crates/typst-pdf/src/color.rs @@ -1,3 +1,4 @@ +use arrayvec::ArrayVec; use once_cell::sync::Lazy; use pdf_writer::{types::DeviceNSubtype, writers, Chunk, Dict, Filter, Name, Ref}; use typst::visualize::{Color, ColorSpace, Paint}; @@ -377,26 +378,34 @@ impl PaintEncode for Color { /// Extra color space functions. pub(super) trait ColorSpaceExt { /// Returns the range of the color space. - fn range(self) -> [f32; 6]; + fn range(self) -> &'static [f32]; /// Converts a color to the color space. - fn convert(self, color: Color) -> [U; 3]; + fn convert(self, color: Color) -> ArrayVec; } impl ColorSpaceExt for ColorSpace { - fn range(self) -> [f32; 6] { - [0.0, 1.0, 0.0, 1.0, 0.0, 1.0] + fn range(self) -> &'static [f32] { + match self { + ColorSpace::D65Gray => &[0.0, 1.0], + ColorSpace::Oklab => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + ColorSpace::Oklch => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + ColorSpace::LinearRgb => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + ColorSpace::Srgb => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + ColorSpace::Cmyk => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + ColorSpace::Hsl => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + ColorSpace::Hsv => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0], + } } - fn convert(self, color: Color) -> [U; 3] { - let range = self.range(); - let [x, y, z, _] = self.encode(color); + fn convert(self, color: Color) -> ArrayVec { + let components = self.encode(color); - [ - U::quantize(x, [range[0], range[1]]), - U::quantize(y, [range[2], range[3]]), - U::quantize(z, [range[4], range[5]]), - ] + self.range() + .chunks(2) + .zip(components) + .map(|(range, component)| U::quantize(component, [range[0], range[1]])) + .collect() } } diff --git a/crates/typst-pdf/src/gradient.rs b/crates/typst-pdf/src/gradient.rs index de77df336..5f85f9ca9 100644 --- a/crates/typst-pdf/src/gradient.rs +++ b/crates/typst-pdf/src/gradient.rs @@ -145,10 +145,9 @@ pub fn write_gradients( .bits_per_component(16) .bits_per_flag(8) .shading_type(StreamShadingType::CoonsPatch) - .decode([ - 0.0, 1.0, 0.0, 1.0, range[0], range[1], range[2], range[3], - range[4], range[5], - ]) + .decode( + [0.0, 1.0, 0.0, 1.0].into_iter().chain(range.iter().copied()), + ) .anti_alias(gradient.anti_alias()) .filter(Filter::FlateDecode); @@ -216,7 +215,7 @@ fn shading_function( chunk .stitching_function(function) .domain([0.0, 1.0]) - .range(color_space.range()) + .range(color_space.range().iter().copied()) .functions(functions) .bounds(bounds) .encode(encode); @@ -235,7 +234,7 @@ fn single_gradient( let reference = chunk.alloc(); chunk .exponential_function(reference) - .range(color_space.range()) + .range(color_space.range().iter().copied()) .c0(color_space.convert(first_color)) .c1(color_space.convert(second_color)) .domain([0.0, 1.0]) @@ -344,13 +343,13 @@ fn register_gradient( /// Structure: /// - flag: `u8` /// - points: `[u16; 24]` -/// - colors: `[u16; 12]` +/// - colors: `[u16; 4*N]` (N = number of components) fn write_patch( target: &mut Vec, t: f32, t1: f32, - c0: [u16; 3], - c1: [u16; 3], + c0: &[u16], + c1: &[u16], angle: Angle, ) { let theta = -TAU * t + angle.to_rad() as f32 + PI; @@ -390,11 +389,13 @@ fn write_patch( p1, p1, p2, p2, cp1, cp2, p3, p3, p1, p1, p1, p1, ])); - let colors = - [c0.map(u16::to_be), c0.map(u16::to_be), c1.map(u16::to_be), c1.map(u16::to_be)]; - // Push the colors. - target.extend_from_slice(bytemuck::cast_slice(&colors)); + let colors = [c0, c0, c1, c1] + .into_iter() + .flat_map(|c| c.iter().copied().map(u16::to_be_bytes)) + .flatten(); + + target.extend(colors); } fn control_point(c: Point, r: f32, angle_start: f32, angle_end: f32) -> (Point, Point) { @@ -452,8 +453,8 @@ fn compute_vertex_stream(gradient: &Gradient, aspect_ratio: Ratio) -> Arc Arc