mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #3012 from 00xJS/fix/2950-press-quality-cmyk
PDF presets: Press Quality converts to Document CMYK, keeping its numbers (#2950, #1242)
This commit is contained in:
@@ -160,6 +160,8 @@ fn destination(set: &PdfSettings, mode: ColorMode, source: &Cms) -> Result<Optio
|
||||
let name = o.destination.trim();
|
||||
let fallback = if cmyk_only { crate::pdfx::cmyk_destination(set, source) } else { String::new() };
|
||||
let name = match (name.is_empty(), mode) {
|
||||
// Document CMYK: the CMYK profile in effect, in an RGB document too (#2950).
|
||||
(false, _) if name.eq_ignore_ascii_case(crate::DOCUMENT_CMYK) => source.settings().cmyk.as_str(),
|
||||
(false, _) => name,
|
||||
(true, _) if cmyk_only => fallback.as_str(),
|
||||
(true, ColorMode::Cmyk) => source.settings().cmyk.as_str(),
|
||||
|
||||
@@ -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, DOCUMENT_CMYK, 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<PdfPreset> {
|
||||
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 Document CMYK, the numbers of colours already in it kept: RGB colours and
|
||||
// images come out CMYK, in an RGB document too, 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, and colours converted to the CMYK profile in effect (Document CMYK), keeping the numbers of colours already in it.",
|
||||
PdfSettings {
|
||||
output: OutputSettings { conversion: ColorConversion::PreserveNumbers, destination: DOCUMENT_CMYK.into(), ..Default::default() },
|
||||
..print(ImageCodec::Zip, true)
|
||||
},
|
||||
),
|
||||
preset(
|
||||
"Smallest File Size",
|
||||
|
||||
@@ -347,12 +347,17 @@ impl BleedSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Output destination that names the CMYK profile of the colour settings in effect, in an RGB
|
||||
/// document as in a CMYK one (Illustrator's "Document CMYK"; #2950).
|
||||
pub const DOCUMENT_CMYK: &str = "Document CMYK";
|
||||
|
||||
/// The Output section.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct OutputSettings {
|
||||
pub conversion: ColorConversion,
|
||||
/// Destination profile name (empty: the document's profile).
|
||||
/// Destination profile name (empty: the document's profile; [`DOCUMENT_CMYK`]: the CMYK
|
||||
/// profile in effect, whatever the document's colour mode).
|
||||
pub destination: String,
|
||||
pub profiles: ProfileInclusion,
|
||||
/// PDF/X output intent profile name.
|
||||
|
||||
@@ -429,3 +429,47 @@ 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");
|
||||
}
|
||||
|
||||
/// #3012 review: in an RGB document Press Quality converts to Document CMYK too, the CMYK
|
||||
/// profile in effect: a CMYK image keeps its inks (not converted to RGB), and an RGB image comes
|
||||
/// out CMYK.
|
||||
#[test]
|
||||
fn press_quality_writes_cmyk_in_an_rgb_document_too() {
|
||||
let press = || PdfOptions { settings: builtin_preset("Press Quality").unwrap().settings, created: Some(1_700_000_000), ..Default::default() };
|
||||
assert_eq!(press().settings.output.destination, DOCUMENT_CMYK);
|
||||
let ink = [0u8, 102, 255, 0];
|
||||
let tiff = ImageBlob::cmyk_tiff(&Inks::new(4, 4, flat(ink, 16)).unwrap()).unwrap();
|
||||
let rgb = image::RgbImage::from_fn(4, 4, |x, y| image::Rgb([(x * 60) as u8, (y * 60) as u8, 128]));
|
||||
let png_blob = ImageBlob { mime: "image/png".into(), bytes: Arc::new(png(rgb)), proxy: None };
|
||||
for (what, blob) in [("CMYK image", tiff), ("RGB image", png_blob)] {
|
||||
let mut d = image_doc(blob, 4, 4);
|
||||
d.color_mode = ColorMode::Rgb;
|
||||
let out = export_with_report(&d, &press()).unwrap();
|
||||
let w = written(&out.bytes);
|
||||
assert_eq!(w.len(), 1, "{what}: {w:?}");
|
||||
assert_eq!(w[0].space, "DeviceCMYK", "{what} in an RGB document");
|
||||
// Preserve Numbers: the CMYK image comes back with the inks it had.
|
||||
if what == "CMYK image" {
|
||||
let back = import(&out.bytes).unwrap();
|
||||
assert_eq!(all_images(&back)[0].cmyk().unwrap().data, flat(ink, 16), "{what} inks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +599,12 @@ fn output(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
});
|
||||
// PDF/X-1a converts to CMYK even without a conversion.
|
||||
let converting = get(d, "output.conversion") != ColorConversion::None.id() || standard.cmyk_only();
|
||||
row(ui, tl!("Destination:"), |ui| profile_pick(ui, d, "output.destination", &names(DOCUMENT_PROFILE), 1, converting));
|
||||
// The document's own profile (blank), the CMYK profile in effect, then every profile.
|
||||
let destinations: Vec<&str> = std::iter::once(DOCUMENT_PROFILE)
|
||||
.chain(std::iter::once(vectorcraft_pdf::DOCUMENT_CMYK))
|
||||
.chain(profiles.iter().map(|p| p.name.as_str()))
|
||||
.collect();
|
||||
row(ui, tl!("Destination:"), |ui| profile_pick(ui, d, "output.destination", &destinations, 2, converting));
|
||||
row(ui, tl!("Profile inclusion:"), |ui| {
|
||||
// A standard decides whether colours are tagged.
|
||||
ui.add_enabled_ui(standard == Standard::None, |ui| pick::<ProfileInclusion>(ui, d, "output.profiles", 300.0, |_| true));
|
||||
|
||||
Reference in New Issue
Block a user