diff --git a/crates/redact/src/lib.rs b/crates/redact/src/lib.rs index f6adf1a1..3835e07d 100644 --- a/crates/redact/src/lib.rs +++ b/crates/redact/src/lib.rs @@ -296,6 +296,10 @@ pub fn apply(doc: &mut Document, pages: Option<&[usize]>) -> Result Dict::new(), }; dict.remove(b"Length"); + // Edit ▸ Add content keeps an added item's source text in `/PCAdded`; once its glyphs + // are redacted, keeping it would leave the redacted text in the file. The item stays + // as plain drawn content but is no longer editable. + dict.remove(b"PCAdded"); // A new object: the original may be shared with other pages. new_list[i] = Object::Ref(doc.add(Object::Stream(Stream::flate(dict, &bytes)))); changed = true; diff --git a/crates/redact/src/tests.rs b/crates/redact/src/tests.rs index 21c87f1a..0ca0b794 100644 --- a/crates/redact/src/tests.rs +++ b/crates/redact/src/tests.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use printcraft_annot::{Meta, NewAnnotation, Shape, Style, add_annotation, rect_quad}; -use printcraft_cos::{Document, SaveOptions, write_incremental}; +use printcraft_cos::{Document, SaveOptions, write_full, write_incremental}; use super::*; @@ -99,6 +99,24 @@ fn glyphs_under_a_mark_go_and_the_rest_stays_put() { assert!(!content(&doc, 0).contains("1234")); } +#[test] +fn added_text_parameters_do_not_keep_redacted_text() { + // An Edit ▸ Add content item: its stream dictionary keeps the source text under /PCAdded. + let mut doc = pdf(vec![ + b"<< /Type /Catalog /Pages 2 0 R >>".to_vec(), + b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>".to_vec(), + b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 300 300] /Contents [4 0 R] /Resources << /Font << /F1 5 0 R >> >> >>".to_vec(), + stream("/PCMark /Added /PCAdded << /Kind /Text /Text (AB1234CD) >>", b"BT /F1 10 Tf 10 100 Td (AB1234CD) Tj ET"), + FONT.replace("95 0 R", "6 0 R").into_bytes(), + widths(), + ]); + mark(&mut doc, 0, &[[20.0, 95.0, 40.0, 110.0]], ""); + apply(&mut doc, None).unwrap(); + assert!(!content(&doc, 0).contains("1234")); + let bytes = write_full(&doc, &SaveOptions::default()).unwrap(); + assert!(!bytes.windows(4).any(|w| w == b"1234"), "redacted text left in the saved file"); +} + #[test] fn kerning_spacing_scaling_and_line_operators_are_honoured() { // TJ kerning, character and word spacing, 50% horizontal scaling, ' and ".