krilla: document that nonzero overprint mode applies to DeviceCMYK only

This commit is contained in:
aiongg committed 2026-10-24 02:53:25 +00:00
1 parent dc54ca0e71
commit 4befe2f986
5 files changed
+75 -46

No files matched your search

+3 -1
View File
@@ -110,7 +110,9 @@ Validation: ISO 19005-2 and 19005-4 (6.2.4.2) forbid OPM 1 when an ICCBased CMYK
space is used for a side that overprints. Validated PDF/A export writes all CMYK as
ICCBased, and the graphics state doesn't know the colors painted under it, so OPM 1 with
`/op` or `/OP` true under `no_device_cs` registers `ValidationError::OverprintModeIccCmyk`,
which PDF/A-2, A-3 and A-4 prohibit. PDF/A-1 and PDF/UA allow it.
which PDF/A-2, A-3 and A-4 prohibit. PDF/A-1 and PDF/UA allow it. ISO 32000-1 (8.6.7)
applies OPM 1 to DeviceCMYK only, so under `no_device_cs` it has no effect in a PDF 1.7
reader; `Overprint::nonzero` says so.
Tests: `crates/pdf/tests/print_color.rs`, and the plate-test scenes S01, S03, S04, S12,
S16, D06 and D10.
+1 -1
View File
@@ -147,7 +147,7 @@
{
"path": "src/graphics/blend.rs",
"upstream_sha256": "f2629743d9e2ea1440763231f3d4cc914224ca238d88fa0630b7c7e9628b58dd",
"sha256": "a6b85f891dd879f2159e0cf8d0e33e54a9153bcca22eb145b58f5a9fb2750cc6"
"sha256": "a3a014d76a379d378dd6893b3e41688ba2ebd358c1295ed0532a443a143fb5ab"
},
{
"path": "src/graphics/color.rs",
+42 -27
View File
@@ -1,7 +1,8 @@
diff -ruN a/src/configure/PDF_A2.md b/src/configure/PDF_A2.md
diff --git a/src/configure/PDF_A2.md b/src/configure/PDF_A2.md
index a4e4377..9609ea2 100644
--- a/src/configure/PDF_A2.md
+++ b/src/configure/PDF_A2.md
@@ -67,7 +67,8 @@
@@ -67,7 +67,8 @@ in case CMYK is used but no profile was provided, export fails.
6.2.4.2:
- sRGB/sGrey ICC profiles conform to ICC v4 specification. 🟢
@@ -11,10 +12,11 @@ diff -ruN a/src/configure/PDF_A2.md b/src/configure/PDF_A2.md
6.2.4.3:
- krilla always uses sRGB for RGB, and in addition also embeds an sRGB output intent. 🟢
diff -ruN a/src/configure/PDF_A4.md b/src/configure/PDF_A4.md
diff --git a/src/configure/PDF_A4.md b/src/configure/PDF_A4.md
index 44a294f..d5e4374 100644
--- a/src/configure/PDF_A4.md
+++ b/src/configure/PDF_A4.md
@@ -66,7 +66,8 @@
@@ -66,7 +66,8 @@ See `README.md` for the meaning of each color.
### 6.2.4 Colour spaces
- krilla only uses device-independent colors in this export mode. 🟢
- krilla uses compatible ICC profiles. 🟢
@@ -24,10 +26,11 @@ diff -ruN a/src/configure/PDF_A4.md b/src/configure/PDF_A4.md
- krilla never uses CMYK profiles as the destination profile. 🟢
- krilla does not use device color spaces in this mode. 🟢
- krilla ensures the Alternate space in Separation color spaces obeys the restrictions
diff -ruN a/src/configure/validate.rs b/src/configure/validate.rs
diff --git a/src/configure/validate.rs b/src/configure/validate.rs
index e92da05..91996ee 100644
--- a/src/configure/validate.rs
+++ b/src/configure/validate.rs
@@ -131,6 +131,16 @@
@@ -131,6 +131,16 @@ pub enum ValidationError {
Transparency(Option<Location>),
/// The PDF contains an image with `interpolate` set to `true`.
ImageInterpolation(Option<Location>),
@@ -44,7 +47,7 @@ diff -ruN a/src/configure/validate.rs b/src/configure/validate.rs
/// The PDF contains an embedded file.
EmbeddedFile(EmbedError, Option<Location>),
/// The PDF contains no tagging.
@@ -554,6 +564,7 @@
@@ -554,6 +564,7 @@ impl Archival {
(
Self::A1_A | Self::A1_B,
ValidationError::InconsistentSeparationFallback(_)
@@ -52,7 +55,7 @@ diff -ruN a/src/configure/validate.rs b/src/configure/validate.rs
| ValidationError::InvalidCodepointMapping(_, _, _, _)
| ValidationError::UnicodePrivateArea(_, _, _, _)
| ValidationError::NoDocumentTitle
@@ -592,6 +603,7 @@
@@ -592,6 +603,7 @@ impl Archival {
| ValidationError::RestrictedLicense(_)
| ValidationError::MissingDocumentDate
| ValidationError::ImageInterpolation(_)
@@ -60,7 +63,7 @@ diff -ruN a/src/configure/validate.rs b/src/configure/validate.rs
| ValidationError::EmbeddedPDF(_),
) => true,
// Allowed under all PDF/A-2 and PDF/A-3 profiles.
@@ -656,6 +668,7 @@
@@ -656,6 +668,7 @@ impl Archival {
| ValidationError::RestrictedLicense(_)
| ValidationError::MissingDocumentDate
| ValidationError::ImageInterpolation(_)
@@ -68,7 +71,7 @@ diff -ruN a/src/configure/validate.rs b/src/configure/validate.rs
| ValidationError::EmbeddedPDF(_),
) => true,
// Allowed under all PDF/A-4 profiles.
@@ -1130,6 +1143,7 @@
@@ -1130,6 +1143,7 @@ impl Accessibility {
| ValidationError::NoDocumentLanguage
| ValidationError::Transparency(_)
| ValidationError::ImageInterpolation(_)
@@ -76,10 +79,11 @@ diff -ruN a/src/configure/validate.rs b/src/configure/validate.rs
| ValidationError::EmbeddedFile(
EmbedError::Existence | EmbedError::MissingDate | EmbedError::MissingMimeType,
_,
diff -ruN a/src/content.rs b/src/content.rs
diff --git a/src/content.rs b/src/content.rs
index dd5b1cb..751cb2f 100644
--- a/src/content.rs
+++ b/src/content.rs
@@ -16,6 +16,7 @@
@@ -16,6 +16,7 @@ use crate::configure::ValidationError;
#[cfg(feature = "raster-images")]
use crate::geom::Size;
use crate::geom::{Point, Rect, Transform};
@@ -87,7 +91,7 @@ diff -ruN a/src/content.rs b/src/content.rs
use crate::graphics::color::{Color, ColorSpace};
use crate::graphics::graphics_state::{ExtGState, GraphicsStates};
#[cfg(feature = "raster-images")]
@@ -183,6 +184,12 @@
@@ -183,6 +184,12 @@ impl ContentBuilder {
}
}
@@ -100,7 +104,8 @@ diff -ruN a/src/content.rs b/src/content.rs
pub(crate) fn expand_bbox(&mut self, new_bbox: Rect) {
let new_bbox = self.graphics_states.transform_bbox(new_bbox);
if let Some(bbox) = &mut self.bbox {
diff -ruN a/src/graphics/blend.rs b/src/graphics/blend.rs
diff --git a/src/graphics/blend.rs b/src/graphics/blend.rs
index 9d0e98d..8d7c5c7 100644
--- a/src/graphics/blend.rs
+++ b/src/graphics/blend.rs
@@ -1,4 +1,4 @@
@@ -109,7 +114,7 @@ diff -ruN a/src/graphics/blend.rs b/src/graphics/blend.rs
/// How to blend source and backdrop.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@@ -44,3 +44,30 @@
@@ -44,3 +44,38 @@ impl BlendMode {
}
}
}
@@ -128,6 +133,14 @@ diff -ruN a/src/graphics/blend.rs b/src/graphics/blend.rs
+ /// Nonzero overprint mode (`/OPM 1`): a DeviceCMYK component of 0 leaves that
+ /// colorant of the backdrop unchanged. With `false` (`/OPM 0`), every DeviceCMYK
+ /// component paints, zero included.
+ ///
+ /// ISO 32000-1 (8.6.7) applies the mode to DeviceCMYK only. With
+ /// [`SerializeSettings::no_device_cs`] all CMYK is ICCBased, so a PDF 1.7 reader paints
+ /// zero components as with `false`, and PDF/A-2 and later reject the combination
+ /// ([`ValidationError::OverprintModeIccCmyk`]).
+ ///
+ /// [`SerializeSettings::no_device_cs`]: crate::SerializeSettings::no_device_cs
+ /// [`ValidationError::OverprintModeIccCmyk`]: crate::configure::ValidationError::OverprintModeIccCmyk
+ pub nonzero: bool,
+}
+
@@ -140,7 +153,8 @@ diff -ruN a/src/graphics/blend.rs b/src/graphics/blend.rs
+ }
+ }
+}
diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
diff --git a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
index 9fc913c..93f3e25 100644
--- a/src/graphics/graphics_state.rs
+++ b/src/graphics/graphics_state.rs
@@ -1,7 +1,7 @@
@@ -152,7 +166,7 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
use pdf_writer::{Finish, Name, Ref};
use crate::chunk_container::ChunkContainer;
@@ -24,6 +24,12 @@
@@ -24,6 +24,12 @@ struct Repr {
blend_mode: Option<BlendMode>,
/// An active mask.
mask: Option<Ref>,
@@ -165,7 +179,7 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
}
/// A graphics state containing information about
@@ -31,6 +37,7 @@
@@ -31,6 +37,7 @@ struct Repr {
/// - The current non-stroking alpha.
/// - The current blend mode.
/// - The current mask.
@@ -173,7 +187,7 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
///
/// This struct provides exposes a builder pattern for setting the various properties
/// individually.
@@ -66,6 +73,16 @@
@@ -66,6 +73,16 @@ impl ExtGState {
self
}
@@ -190,7 +204,7 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
/// Create a new graphics state with a mask.
#[must_use]
pub(crate) fn mask(
@@ -85,6 +102,9 @@
@@ -85,6 +102,9 @@ impl ExtGState {
&& self.0.stroking_alpha.is_none()
&& self.0.non_stroking_alpha.is_none()
&& self.0.blend_mode.is_none()
@@ -200,7 +214,7 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
}
/// Integrate another graphics state into the current one. This is done by replacing
@@ -106,6 +126,18 @@
@@ -106,6 +126,18 @@ impl ExtGState {
if let Some(mask) = other.0.mask {
Arc::make_mut(&mut self.0).mask = Some(mask);
}
@@ -219,7 +233,7 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
}
}
@@ -153,6 +185,30 @@
@@ -153,6 +185,30 @@ impl Cacheable for ExtGState {
ext_st.pair(Name(b"SMask"), mask_ref);
}
@@ -250,10 +264,11 @@ diff -ruN a/src/graphics/graphics_state.rs b/src/graphics/graphics_state.rs
ext_st.finish();
}
}
diff -ruN a/src/surface.rs b/src/surface.rs
diff --git a/src/surface.rs b/src/surface.rs
index 990527f..09b1b40 100644
--- a/src/surface.rs
+++ b/src/surface.rs
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@ use crate::geom::Path;
use crate::geom::Size;
use crate::geom::{Point, Transform};
use crate::graphic::Graphic;
@@ -262,7 +277,7 @@ diff -ruN a/src/surface.rs b/src/surface.rs
use crate::graphics::graphics_state::ExtGState;
#[cfg(feature = "raster-images")]
use crate::graphics::image::Image;
@@ -422,6 +422,16 @@
@@ -422,6 +422,16 @@ impl<'a> Surface<'a> {
self.bd.get_mut().set_blend_mode(blend_mode.to_pdf());
}
@@ -279,7 +294,7 @@ diff -ruN a/src/surface.rs b/src/surface.rs
/// Push a new clip path.
pub fn push_clip_path(&mut self, path: &Path, clip_rule: &FillRule) {
self.push_instructions.push(PushInstruction::ClipPath);
@@ -509,6 +519,7 @@
@@ -509,6 +519,7 @@ impl<'a> Surface<'a> {
}
PushInstruction::ClipPath => self.bd.get_mut().pop_clip_path(),
PushInstruction::BlendMode => self.bd.get_mut().restore_graphics_state(),
@@ -287,7 +302,7 @@ diff -ruN a/src/surface.rs b/src/surface.rs
PushInstruction::Mask(mask) => {
let stream = self.bd.sub_builders.pop().unwrap().finish(self.sc);
self.bd
@@ -670,6 +681,7 @@
@@ -670,6 +681,7 @@ pub(crate) enum PushInstruction {
Opacity(NormalizedF32),
ClipPath,
BlendMode,
+21 -17
View File
@@ -1,7 +1,8 @@
diff -ruN a/src/content.rs b/src/content.rs
diff --git a/src/content.rs b/src/content.rs
index 751cb2f..f723f20 100644
--- a/src/content.rs
+++ b/src/content.rs
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@ use crate::configure::ValidationError;
#[cfg(feature = "raster-images")]
use crate::geom::Size;
use crate::geom::{Point, Rect, Transform};
@@ -10,7 +11,7 @@ diff -ruN a/src/content.rs b/src/content.rs
use crate::graphics::color::{Color, ColorSpace};
use crate::graphics::graphics_state::{ExtGState, GraphicsStates};
#[cfg(feature = "raster-images")]
@@ -933,14 +933,15 @@
@@ -933,14 +933,15 @@ impl ContentBuilder {
self.draw_xobject(sc, chunk_container, x_object, &state);
}
@@ -28,10 +29,11 @@ diff -ruN a/src/content.rs b/src/content.rs
self.draw_xobject(sc, chunk_container, x_object, &state);
}
diff -ruN a/src/graphics/blend.rs b/src/graphics/blend.rs
diff --git a/src/graphics/blend.rs b/src/graphics/blend.rs
index 8d7c5c7..7f01733 100644
--- a/src/graphics/blend.rs
+++ b/src/graphics/blend.rs
@@ -71,3 +71,21 @@
@@ -79,3 +79,21 @@ impl Overprint {
}
}
}
@@ -53,10 +55,11 @@ diff -ruN a/src/graphics/blend.rs b/src/graphics/blend.rs
+ /// the topmost shows.
+ pub knockout: bool,
+}
diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
diff --git a/src/graphics/xobject.rs b/src/graphics/xobject.rs
index 6c56b0e..a8c9568 100644
--- a/src/graphics/xobject.rs
+++ b/src/graphics/xobject.rs
@@ -7,6 +7,7 @@
@@ -7,6 +7,7 @@ use pdf_writer::{Finish, Name, Ref};
use crate::chunk_container::ChunkContainer;
use crate::configure::ValidationError;
use crate::geom::Rect;
@@ -64,7 +67,7 @@ diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
use crate::graphics::color::{rgb, DEVICE_CMYK, DEVICE_RGB};
use crate::resource;
use crate::resource::{Resource, Resourceable};
@@ -28,6 +29,7 @@
@@ -28,6 +29,7 @@ pub(crate) enum GroupRole {
struct Repr {
stream: Stream,
isolated: bool,
@@ -72,7 +75,7 @@ diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
transparency_group_color_space: bool,
custom_bbox: Option<Rect>,
role: GroupRole,
@@ -52,9 +54,39 @@
@@ -52,9 +54,39 @@ impl XObject {
)
}
@@ -112,7 +115,7 @@ diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
mut transparency_group_color_space: bool,
custom_bbox: Option<Rect>,
role: GroupRole,
@@ -74,6 +106,7 @@
@@ -74,6 +106,7 @@ impl XObject {
Self(Arc::new(Prehashed::new(Repr {
stream,
isolated,
@@ -120,7 +123,7 @@ diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
transparency_group_color_space,
custom_bbox,
role,
@@ -102,7 +135,8 @@
@@ -102,7 +135,8 @@ impl Cacheable for XObject {
sc.register_validation_error(validation_error.clone());
}
@@ -130,7 +133,7 @@ diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
if use_transparency_group {
sc.register_validation_error(ValidationError::Transparency(sc.location));
@@ -166,6 +200,10 @@
@@ -166,6 +200,10 @@ impl Cacheable for XObject {
transparency.isolated(self.0.isolated);
}
@@ -141,10 +144,11 @@ diff -ruN a/src/graphics/xobject.rs b/src/graphics/xobject.rs
if let Some(transparency_group_cs) = transparency_group_cs {
write_group_color_space(transparency, transparency_group_cs);
}
diff -ruN a/src/surface.rs b/src/surface.rs
diff --git a/src/surface.rs b/src/surface.rs
index 09b1b40..0cbbc4b 100644
--- a/src/surface.rs
+++ b/src/surface.rs
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@ use crate::geom::Path;
use crate::geom::Size;
use crate::geom::{Point, Transform};
use crate::graphic::Graphic;
@@ -153,7 +157,7 @@ diff -ruN a/src/surface.rs b/src/surface.rs
use crate::graphics::graphics_state::ExtGState;
#[cfg(feature = "raster-images")]
use crate::graphics::image::Image;
@@ -494,9 +494,19 @@
@@ -494,9 +494,19 @@ impl<'a> Surface<'a> {
}
}
@@ -175,7 +179,7 @@ diff -ruN a/src/surface.rs b/src/surface.rs
self.bd
.sub_builders
.push(ContentBuilder::new(Transform::identity(), true, self.sc));
@@ -526,11 +536,11 @@
@@ -526,11 +536,11 @@ impl<'a> Surface<'a> {
.get_mut()
.draw_masked(self.sc, self.chunk_container, *mask, stream)
}
@@ -189,7 +193,7 @@ diff -ruN a/src/surface.rs b/src/surface.rs
}
}
}
@@ -683,5 +693,5 @@
@@ -683,5 +693,5 @@ pub(crate) enum PushInstruction {
BlendMode,
Overprint,
Mask(Box<Mask>),
+8
View File
@@ -59,6 +59,14 @@ pub struct Overprint {
/// Nonzero overprint mode (`/OPM 1`): a DeviceCMYK component of 0 leaves that
/// colorant of the backdrop unchanged. With `false` (`/OPM 0`), every DeviceCMYK
/// component paints, zero included.
///
/// ISO 32000-1 (8.6.7) applies the mode to DeviceCMYK only. With
/// [`SerializeSettings::no_device_cs`] all CMYK is ICCBased, so a PDF 1.7 reader paints
/// zero components as with `false`, and PDF/A-2 and later reject the combination
/// ([`ValidationError::OverprintModeIccCmyk`]).
///
/// [`SerializeSettings::no_device_cs`]: crate::SerializeSettings::no_device_cs
/// [`ValidationError::OverprintModeIccCmyk`]: crate::configure::ValidationError::OverprintModeIccCmyk
pub nonzero: bool,
}