PDF export: a CMYK page without blending through is an isolated group (#2156)

This commit is contained in:
Justin Willhite
2026-10-16 02:56:34 +07:00
committed by bflatastic
parent 18f8a1bdcf
commit 8da47f11f8
2 changed files with 22 additions and 3 deletions
+4 -3
View File
@@ -241,12 +241,13 @@ impl Writer {
}
// Page Isolated Blending / Page Knockout Group: the page content is one group (the PDF
// writer has no page group attributes; a flat PDF 1.3 file has none). In CMYK, transparency
// at the top of a page is put in a non-isolated group of its own, whose blending space
// `cmyk_blending` rewrites.
// at the top of a page is put in a group of its own, whose blending space `cmyk_blending`
// rewrites: non-isolated only when blending on the page reaches the paper, since some
// readers (CoreGraphics) drop most of a page in a non-isolated group (#2156).
let doc = ex.doc;
let page_group = fits && !self.pdf13 && (doc.page_isolate || doc.page_knockout);
let cmyk_page_group = fits && self.cmyk && !page_group && ex.shows_transparency();
if page_group {
if page_group || (cmyk_page_group && !Node::children_blend(&doc.layers, &mut |c| c.blends_through())) {
s.push_isolated();
} else if cmyk_page_group {
let all = constant_mask(&mut s, r, 1.0);
+18
View File
@@ -85,3 +85,21 @@ fn constant_alpha_masks_are_white() {
assert!(t.contains("/S/Alpha") && t.contains("111rg") && !t.contains("000rg"), "grouped {grouped}: {t}");
}
}
#[test]
fn a_cmyk_page_without_blending_through_is_an_isolated_group() {
// #2156: CoreGraphics drops most of a page put in a non-isolated group. Without a blend mode
// reaching the paper, isolating the page's group changes nothing it draws.
let mut d = Document::new_with_mode(100.0, 100.0, ColorMode::Cmyk);
add(&mut d, rect(Rect::new(0.0, 0.0, 100.0, 100.0), Color::cmyk(0.0, 1.0, 0.0, 0.0)));
let mut top = rect(Rect::new(20.0, 20.0, 80.0, 80.0), Color::cmyk(1.0, 0.0, 0.0, 0.0));
top.opacity = 0.6;
add(&mut d, top);
let t = text(&d);
assert!(t.contains("/Itrue") && t.contains("/CS/DeviceCMYK") && !t.contains("/S/Alpha"), "{t}");
// Blending that reaches the paper keeps the page's group non-isolated.
for grouped in [true, false] {
let t = text(&doc(ColorMode::Cmyk, grouped));
assert!(!t.contains("/Itrue") && t.contains("/S/Alpha"), "grouped {grouped}: {t}");
}
}