Printing: a layer that is off in the document stays off on the sheets (#1135)

The print-ready PDF that `pdfcraft_print::impose` builds starts from a new
catalog with only /Type and /Pages, so the document's /OCProperties was not
carried over. The pages' marked content still refers to its optional-content
groups, but nothing said a group was off, so a layer that is switched off in
the document (and not drawn on screen) was printed anyway.

Keep /OCProperties in the new catalog, so the sheets hide the same layers the
document hides. Test: a one-page document whose red square is in a layer; with
the layer off by default it is not drawn on screen and, now, not on the
printed sheet; with the layer on it is drawn in both.

Printing still follows the layers' default state only; applying the Print
event of /AS (a layer shown only on screen or only in print) is not done here.
This commit is contained in:
Alexis
2026-10-15 02:16:23 -04:00
committed by GitHub
parent dbef872ed7
commit e75082c9d6
2 changed files with 42 additions and 0 deletions
+36
View File
@@ -1827,6 +1827,42 @@ fn printing_lays_out_sheets_that_render() {
assert_eq!(s4.print_pdf(id4, &print::Settings { pages: vec![0], ..Default::default() }), Err(EditError::NotPermitted("printing")));
}
/// A one-page document whose red square is in a layer; `off` switches the layer off by default.
fn red_layer_pdf(off: bool) -> Vec<u8> {
let content = "/OC /L1 BDC 1 0 0 rg 0 0 100 100 re f EMC";
let off = if off { "/OFF [5 0 R] " } else { "" };
format!(
"%PDF-1.7\n\
1 0 obj << /Type /Catalog /Pages 2 0 R /OCProperties << /OCGs [5 0 R] /D << {off}/Order [5 0 R] >> >> >> endobj\n\
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj\n\
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents 4 0 R /Resources << /Properties << /L1 5 0 R >> >> >> endobj\n\
4 0 obj << /Length {} >> stream\n{content}\nendstream endobj\n\
5 0 obj << /Type /OCG /Name (Red) >> endobj\n\
trailer << /Root 1 0 R >>\n%%EOF",
content.len()
)
.into_bytes()
}
/// Does the first page of `pdf` show any red when drawn?
fn first_page_has_red(pdf: Vec<u8>) -> bool {
let mut renderer = pdfcraft_render::PageRenderer::new(Arc::new(pdf), pdfcraft_render::RenderConfig::default());
let page = renderer.render(pdfcraft_render::RenderRequest { page: 0, scale: 0.5, ..Default::default() });
page.rgba.as_chunks::<4>().0.iter().any(|p| p[0] > 200 && p[1] < 60 && p[2] < 60)
}
#[test]
fn a_layer_that_is_off_does_not_print_and_one_that_is_on_does() {
for (off, prints) in [(true, false), (false, true)] {
let source = red_layer_pdf(off);
assert_eq!(first_page_has_red(source.clone()), prints, "on screen (off: {off})");
let mut s = Session::new();
let id = s.open("layers.pdf", None, Arc::new(source), None).unwrap();
let sheet = s.print_pdf(id, &print::Settings { pages: vec![0], ..Default::default() }).unwrap();
assert_eq!(first_page_has_red(sheet), prints, "printed (layer off by default: {off})");
}
}
#[test]
fn crop_and_duplicate_pages_show_in_the_viewer() {
let (mut s, id) = session_with(2);
+6
View File
@@ -723,6 +723,12 @@ pub fn impose(src: &Document, settings: &Settings) -> Result<Vec<u8>, PrintError
let mut cat = Dict::new();
cat.set(b"Type".to_vec(), Object::name("Catalog"));
cat.set(b"Pages".to_vec(), Object::Ref(pages_root));
// Keep the layers' configuration, or a layer that is off in the document would print: the
// pages' marked content still refers to its groups, and without this nothing says they are off.
let layers = src.trailer().get(b"Root").and_then(|r| src.resolve(r).as_dict().and_then(|root| root.get(b"OCProperties").cloned()));
if let Some(layers) = layers {
cat.set(b"OCProperties".to_vec(), layers);
}
let root = doc.add(Object::Dict(cat));
doc.trailer_mut().set(b"Root".to_vec(), Object::Ref(root));
doc.trailer_mut().remove(b"Info");