From 5d0b966548fa8f4bb72ad7480429abd1165c6f4e Mon Sep 17 00:00:00 2001 From: 00xJ0 <56056764+00xJ0@users.noreply.github.com> Date: Sun, 18 Oct 2026 18:06:24 -0700 Subject: [PATCH] PDF presets: Press Quality converts to the document's profile, keeping its numbers (#2950) Press Quality left Output at No conversion, so in a CMYK document RGB images went to the press as DeviceRGB. It now converts to the destination with the numbers kept, the destination blank (the document's profile): RGB comes out CMYK in a CMYK document, CMYK values stay exactly, RGB documents are unchanged, and no profiles are included, as a printer's published [Press Quality] settings list. --- crates/pdf/src/presets.rs | 16 ++++++++++++---- crates/pdf/src/tests_cmykimages.rs | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/pdf/src/presets.rs b/crates/pdf/src/presets.rs index cf7ae0fe3..a386e7493 100644 --- a/crates/pdf/src/presets.rs +++ b/crates/pdf/src/presets.rs @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize}; use crate::{ - BleedSettings, Compatibility, CompressionSettings, Downsample, ImageCodec, ImageCompression, JpegQuality, MonoCodec, MonoCompression, - PdfSettings, Standard, + BleedSettings, ColorConversion, Compatibility, CompressionSettings, Downsample, ImageCodec, ImageCompression, JpegQuality, MonoCodec, + MonoCompression, OutputSettings, PdfSettings, Standard, }; /// The built-in preset every PDF export starts from. @@ -62,10 +62,18 @@ pub fn builtin_presets() -> Vec { PdfSettings { preserve_editing: true, ..Default::default() }, ), preset("High Quality Print", "For desktop printers and proofs: images stay at print resolution.", print(ImageCodec::Auto, false)), + // Converted to the document's profile, its own numbers kept: in a CMYK document RGB colours + // and images come out CMYK, as a press needs (#2950). A printer's published settings for + // the [Press Quality] preset list the same Output: Convert to Destination (Preserve + // Numbers), Document CMYK, no profiles included + // (https://postmediasolutions.com/hubfs/Website/Client%20Resources/PostmediaPrintPDFSpecSheet.pdf). preset( "Press Quality", - "For commercial printing: high-resolution images compressed losslessly, the document's bleed.", - print(ImageCodec::Zip, true), + "For commercial printing: high-resolution images compressed losslessly, the document's bleed, colours converted to the document's profile with its own numbers kept (RGB comes out CMYK in a CMYK document).", + PdfSettings { + output: OutputSettings { conversion: ColorConversion::PreserveNumbers, ..Default::default() }, + ..print(ImageCodec::Zip, true) + }, ), preset( "Smallest File Size", diff --git a/crates/pdf/src/tests_cmykimages.rs b/crates/pdf/src/tests_cmykimages.rs index 65f93d3dc..afa71f44d 100644 --- a/crates/pdf/src/tests_cmykimages.rs +++ b/crates/pdf/src/tests_cmykimages.rs @@ -429,3 +429,21 @@ fn a_cmyk_image_masked_by_a_grey_image_of_its_grid_writes_it_as_its_smask() { assert_eq!(smasks, [None]); assert!(String::from_utf8_lossy(&bytes).contains("/S/Luminosity"), "a luminosity soft mask"); } + +/// #2950: Press Quality converts to the document's profile with its numbers kept, so in a CMYK +/// document an RGB image comes out CMYK, as the press needs; with no conversion (the default +/// preset) it stays RGB, as before. +#[test] +fn press_quality_writes_an_rgb_image_in_a_cmyk_document_as_cmyk() { + let rgb = image::RgbImage::from_fn(4, 4, |x, y| image::Rgb([(x * 60) as u8, (y * 60) as u8, 128])); + let blob = ImageBlob { mime: "image/png".into(), bytes: Arc::new(png(rgb)), proxy: None }; + let d = image_doc(blob, 4, 4); + let press = builtin_presets().into_iter().find(|p| p.name == "Press Quality").unwrap().settings; + assert_eq!(press.output.conversion, ColorConversion::PreserveNumbers); + let options = PdfOptions { settings: press, created: Some(1_700_000_000), ..Default::default() }; + let w = written(&export_with_report(&d, &options).unwrap().bytes); + assert_eq!(w.len(), 1, "{w:?}"); + assert_eq!((w[0].space.as_str(), w[0].icc_n), ("DeviceCMYK", None), "Press Quality"); + let w = written(&export_with_report(&d, &opts(json!({}))).unwrap().bytes); + assert_eq!(w[0].space, "DeviceRGB", "no conversion"); +}