diff --git a/crates/pdf/src/export.rs b/crates/pdf/src/export.rs index a6c9f70e3..84c731cc5 100644 --- a/crates/pdf/src/export.rs +++ b/crates/pdf/src/export.rs @@ -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); diff --git a/crates/pdf/src/tests_cmykblend.rs b/crates/pdf/src/tests_cmykblend.rs index 03fcad84f..5655dd0e6 100644 --- a/crates/pdf/src/tests_cmykblend.rs +++ b/crates/pdf/src/tests_cmykblend.rs @@ -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}"); + } +}