mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 04:22:20 +00:00
M10: comments and fields without an appearance print as the page shows them (#6367)
This commit is contained in:
1 parent
61ce0cb010
commit
3e437b7bd4
3 files changed
+55
-1
No files matched your search
@@ -728,6 +728,14 @@ pub(crate) fn write_display_copy(
|
||||
}
|
||||
}
|
||||
|
||||
/// `doc` as the reader shows it: comments without an appearance stream drawn
|
||||
/// (`pdfcraft_annot::with_missing_appearances`) and form fields drawn from their values where the
|
||||
/// file leaves that to the viewer. `None` when `doc` needs neither. The vector print draws from
|
||||
/// this copy too, so what prints is what the page shows (#371, #3475 draw them for display only).
|
||||
pub(crate) fn as_displayed(doc: &pdfcraft_cos::Document) -> Option<pdfcraft_cos::Document> {
|
||||
with_field_appearances(pdfcraft_annot::with_missing_appearances(doc), doc)
|
||||
}
|
||||
|
||||
/// Carry the marks across a revalidation that leaves the display copy alone, when it may: only
|
||||
/// when every verdict is the one the copy was drawn with. Returns whether it did; otherwise the
|
||||
/// copy has to be drawn again.
|
||||
@@ -779,7 +787,7 @@ pub fn display_bytes_for(bytes: Arc<Vec<u8>>, password: Option<&str>) -> Arc<Vec
|
||||
let cos = pdfcraft_cos::Document::open_with_password(bytes.clone(), password).ok()?;
|
||||
let mut signatures = signatures_of(&cos, &bytes, &TrustStore::default(), &pdfcraft_sign::DigestCache::default());
|
||||
let layered = pdfcraft_sign::has_status_layers(&cos, &signatures);
|
||||
let (mut display, added) = match with_field_appearances(pdfcraft_annot::with_missing_appearances(&cos), &cos) {
|
||||
let (mut display, added) = match as_displayed(&cos) {
|
||||
Some(display) => (display, true),
|
||||
None if layered => (cos, false),
|
||||
None => return None,
|
||||
|
||||
@@ -164,6 +164,13 @@ impl Session {
|
||||
pdfcraft_cos::Document::open_with_password(doc.bytes.clone(), doc.password.as_deref()).map_err(|e| EditError::Write(e.to_string()))?
|
||||
}
|
||||
};
|
||||
// Comments and fields without an appearance print as the page shows them (and as Print
|
||||
// as Image already printed them): the vector print drew only appearance streams, so a
|
||||
// text box without one was on screen but not on paper. ISO 32000-2 §12.5.3: an
|
||||
// annotation with the Print flag is printed.
|
||||
if let Some(displayed) = guard(|| crate::document::as_displayed(&cos)).ok().flatten() {
|
||||
cos = displayed;
|
||||
}
|
||||
// #3046: the layers print as they are shown. Once the Layers panel (or a set-layer
|
||||
// action) has changed one, the print copy's default configuration says which are on, so a
|
||||
// hidden layer stays off on paper too; untouched, the document's own configuration prints.
|
||||
|
||||
@@ -1779,6 +1779,45 @@ fn low_resolution_printing_prints_only_the_chosen_content() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A comment and a field the file leaves without an appearance print as the page shows them:
|
||||
/// a text box (`/FreeText` with only `/Contents` and `/DA`) and a check box set on with
|
||||
/// `/NeedAppearances`. The vector print drew only appearance streams, so both were on screen and
|
||||
/// in Print as Image but not on paper (pdf.js `bug1865341`, `freetext_print_no_appearance`).
|
||||
/// ISO 32000-2 §12.5.3: an annotation with the Print flag is printed.
|
||||
#[test]
|
||||
fn comments_and_fields_without_an_appearance_print_as_shown() {
|
||||
let content = "";
|
||||
let pdf = format!(
|
||||
"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [6 0 R] /NeedAppearances true /DA (/Helv 0 Tf 0 g) >> >> endobj
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R /Annots [5 0 R 6 0 R] >> endobj
|
||||
4 0 obj << /Length {} >> stream
|
||||
{content}
|
||||
endstream endobj
|
||||
5 0 obj << /Type /Annot /Subtype /FreeText /F 4 /P 3 0 R /Rect [10 120 190 190] /DA (/Helv 40 Tf 0 g) /Contents (WWWW) >> endobj
|
||||
6 0 obj << /Type /Annot /Subtype /Widget /FT /Btn /T (Box) /V /Yes /AS /Yes /F 4 /P 3 0 R /Rect [10 10 110 110] /MK << /CA (8) >> >> endobj
|
||||
trailer << /Root 1 0 R >>
|
||||
%%EOF",
|
||||
content.len(),
|
||||
);
|
||||
let mut s = Session::new();
|
||||
let id = s.open("bare.pdf", None, Arc::new(pdf.into_bytes()), None).unwrap();
|
||||
let settings =
|
||||
print::Settings { pages: vec![0], paper: (200.0, 200.0), layout: print::Layout::Size(print::SizeMode::Actual), ..Default::default() };
|
||||
let (w, h, rgba) = printed_pixels(s.print_pdf(id, &settings).unwrap());
|
||||
assert_eq!((w, h), (200, 200));
|
||||
// Dark pixels in the text box (top half) and in the check box (bottom half).
|
||||
let dark = |y0: usize, y1: usize| {
|
||||
(y0..y1)
|
||||
.flat_map(|y| (0..w as usize).map(move |x| (x, y)))
|
||||
.filter(|&(x, y)| rgba.get((y * w as usize + x) * 4).is_some_and(|&r| r < 128))
|
||||
.count()
|
||||
};
|
||||
assert!(dark(10, 80) > 50, "the text box prints");
|
||||
assert!(dark(90, 190) > 50, "the check box prints");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_permissions_password_unlocks_an_open_document() {
|
||||
// Only a permissions password (#995, #1077): opens without asking. Changes are allowed,
|
||||
|
||||
Reference in new issue
Block a user