PDF: PDF/X-1a test covers a tagged gray JPEG

The PDF/X-1a case of tagged_tiffs_keep_their_icc_profile also exports a
gray JPEG with a gray profile, kept as it is, resampled and compressed
with JPEG, and checks that it is written in DeviceGray without the
profile. The comments on decoded_image, recode and the test say that
PDF/X-1a allows no ICC-based color spaces.
This commit is contained in:
dotCooCoo
2026-11-14 10:48:45 -08:00
parent d2e36f689b
commit 6b9564c60d
2 changed files with 12 additions and 6 deletions
+3 -3
View File
@@ -144,8 +144,8 @@ pub(crate) fn from_decoded(img: DynamicImage, icc: Option<Vec<u8>>, interpolate:
}
/// Image `bytes` decoded within [`limits`], with the ICC profile it carries ([`source_icc`]) when
/// `profiles` allows it (not in PDF/X-1a, which has device colours only), as [`from_decoded`]
/// writes it. `None` when it can't be decoded.
/// `profiles` allows it (not in PDF/X-1a, which allows no ICC-based color spaces), as
/// [`from_decoded`] writes it. `None` when it can't be decoded.
pub(crate) fn decoded_image(bytes: &[u8], interpolate: bool, sixteen_bit: bool, profiles: bool) -> Option<Image> {
from_decoded(decoded(bytes)?, profiles.then(|| source_icc(bytes)).flatten(), interpolate, sixteen_bit)
}
@@ -371,7 +371,7 @@ fn resample_channels(img: DynamicImage, r: &Rule, (w, h): (u32, u32)) -> Dynamic
/// Encoded image `bytes` placed `size` points wide and high, as the Compression settings `c` ask:
/// resampled when its resolution is above the threshold, compressed with ZIP or JPEG, its colours
/// converted by `convert` (colour images). Pixels that keep their colours keep the ICC profile
/// they carry when `profiles` allows it (not in PDF/X-1a, which has device colours only).
/// they carry when `profiles` allows it (not in PDF/X-1a, which allows no ICC-based color spaces).
/// `interpolate` lets viewers smooth it. An image that can't be decoded is kept as it is.
pub(crate) fn recode(
bytes: &[u8],
+9 -3
View File
@@ -292,6 +292,11 @@ fn tagged(img: &DynamicImage, icc: &[u8], format: ImageFormat) -> Vec<u8> {
enc.set_icc_profile(icc.to_vec()).unwrap();
img.write_with_encoder(enc).unwrap();
}
ImageFormat::Jpeg => {
let mut enc = image::codecs::jpeg::JpegEncoder::new(&mut out);
enc.set_icc_profile(icc.to_vec()).unwrap();
img.write_with_encoder(enc).unwrap();
}
_ => {
let mut enc = image::codecs::png::PngEncoder::new(&mut out);
enc.set_icc_profile(icc.to_vec()).unwrap();
@@ -423,11 +428,12 @@ fn tagged_tiffs_keep_their_icc_profile() {
let spaces = export(&tiff(&colour(), &rgb), json!({"output": {"conversion": "destination", "destination": destination}}));
assert!(spaces.len() == 1 && spaces[0].icc.as_deref() != Some(rgb.as_slice()), "converted to {destination}: {spaces:?}");
}
// PDF/X-1a has device colours only: a tagged gray TIFF or PNG is written in DeviceGray, kept as
// it is, resampled or compressed with JPEG.
// PDF/X-1a allows no ICC-based color spaces: a tagged gray TIFF, PNG or JPEG is written in
// DeviceGray, kept as it is, resampled or compressed with JPEG.
let compressions = [json!({}), json!({"gray": {"downsample": "bicubic", "ppi": 150, "abovePpi": 225}}), json!({"gray": {"compression": "jpeg"}})];
let png = doc(tagged(&colour().grayscale(), &gray, ImageFormat::Png), "image/png");
for (format, d) in [("TIFF", tiff(&colour().grayscale(), &gray)), ("PNG", png)] {
let jpeg = doc(tagged(&colour().grayscale(), &gray, ImageFormat::Jpeg), "image/jpeg");
for (format, d) in [("TIFF", tiff(&colour().grayscale(), &gray)), ("PNG", png), ("JPEG", jpeg)] {
for compression in &compressions {
let mut settings: PdfSettings =
serde_json::from_value(json!({"standard": "pdfX1a", "compatibility": "1.3", "compression": compression.clone()})).unwrap();