mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 04:22:20 +00:00
Private data takes OPI links and alternate images (#6457)
This commit is contained in:
1 parent
d0083c09a4
commit
34f9bea0ad
2 files changed
+39
-5
No files matched your search
@@ -28,7 +28,8 @@ pub enum Hidden {
|
||||
Bookmarks,
|
||||
/// Links, actions (open action, additional actions, what bookmarks run) and document JavaScript.
|
||||
LinksActionsScripts,
|
||||
/// Private data of other applications (`/PieceInfo`).
|
||||
/// Private data of other applications (`/PieceInfo`), and prepress data on images and Forms
|
||||
/// (`/OPI`, `/Alternates`).
|
||||
PrivateData,
|
||||
}
|
||||
|
||||
@@ -253,9 +254,16 @@ fn outline_action_count(doc: &Document, cat: &Dict) -> usize {
|
||||
|
||||
/// Entries that hold a category's data wherever they appear, not only on the catalog and pages:
|
||||
/// XMP on images, fonts and Forms, private data on Forms, associated files on pages and
|
||||
/// annotations, additional actions on fields.
|
||||
const OWNED: [(Hidden, &[u8]); 4] =
|
||||
[(Hidden::Metadata, b"Metadata"), (Hidden::PrivateData, b"PieceInfo"), (Hidden::Attachments, b"AF"), (Hidden::LinksActionsScripts, b"AA")];
|
||||
/// annotations, additional actions on fields. Images and Forms also carry prepress data: an OPI
|
||||
/// proxy's link to the original file, and alternate images, which can be the unedited original.
|
||||
const OWNED: [(Hidden, &[u8]); 6] = [
|
||||
(Hidden::Metadata, b"Metadata"),
|
||||
(Hidden::PrivateData, b"PieceInfo"),
|
||||
(Hidden::PrivateData, b"OPI"),
|
||||
(Hidden::PrivateData, b"Alternates"),
|
||||
(Hidden::Attachments, b"AF"),
|
||||
(Hidden::LinksActionsScripts, b"AA"),
|
||||
];
|
||||
|
||||
/// Keys whose dictionary values map names a producer chooses to objects (resources, glyph
|
||||
/// procedures, named destinations, role and class maps, private data by application). A name
|
||||
@@ -542,7 +550,7 @@ pub fn scan(doc: &Document) -> Vec<(Hidden, usize)> {
|
||||
// A graph nested too deeply to walk counts as holding everything, so the removal runs and
|
||||
// refuses it.
|
||||
let held = owners(doc, &owned_keys).map_or_else(|_| vec![1; OWNED.len()], |o| o.0);
|
||||
let owned = |h: Hidden| OWNED.iter().position(|o| o.0 == h).and_then(|i| held.get(i)).copied().unwrap_or(0);
|
||||
let owned = |h: Hidden| OWNED.iter().zip(&held).filter(|(o, _)| o.0 == h).map(|(_, n)| n).sum::<usize>();
|
||||
let mut doc2 = doc.clone();
|
||||
HIDDEN
|
||||
.into_iter()
|
||||
|
||||
@@ -4580,3 +4580,29 @@ fn a_redacted_form_xobject_drops_the_source_text_of_added_content() {
|
||||
let bytes = write_full(&doc, &SaveOptions { object_streams: false, ..SaveOptions::default() }).unwrap();
|
||||
assert!(!bytes.windows(4).any(|w| w == b"1234"), "redacted text left in the saved file");
|
||||
}
|
||||
|
||||
/// Private data takes an image's prepress data too: its OPI link to the original file and its
|
||||
/// alternate images, which can be the unedited original.
|
||||
#[test]
|
||||
fn private_data_takes_opi_links_and_alternate_images() {
|
||||
use crate::sanitize::{Hidden, remove_hidden, scan};
|
||||
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 << /XObject << /Im 5 0 R /Fm 6 0 R >> >> >>".to_vec(),
|
||||
stream("", b"q 50 0 0 50 20 20 cm /Im Do Q /Fm Do"),
|
||||
stream(
|
||||
"/Type /XObject /Subtype /Image /Width 11 /Height 1 /ColorSpace /DeviceGray /BitsPerComponent 8 /Alternates [<< /Image 7 0 R /DefaultForPrinting true >>]",
|
||||
b"SHOWNIMAGE!",
|
||||
),
|
||||
stream("/Type /XObject /Subtype /Form /BBox [0 0 300 300] /OPI << /1.3 << /Type /OPI /Version 1.3 /F (OPISERVERPATH.tif) >> >>", b""),
|
||||
stream("/Type /XObject /Subtype /Image /Width 17 /Height 1 /ColorSpace /DeviceGray /BitsPerComponent 8", b"ALTERNATEORIGINAL"),
|
||||
]);
|
||||
assert_eq!(scan(&doc).into_iter().find(|c| c.0 == Hidden::PrivateData), Some((Hidden::PrivateData, 2)));
|
||||
assert_eq!(remove_hidden(&mut doc, &[Hidden::PrivateData]).unwrap(), [(Hidden::PrivateData, 2)]);
|
||||
let bytes = write_full(&doc, &SaveOptions { object_streams: false, ..SaveOptions::default() }).unwrap();
|
||||
let has = |needle: &[u8]| bytes.windows(needle.len()).any(|w| w == needle);
|
||||
assert!(has(b"SHOWNIMAGE!"), "the image the page shows stays");
|
||||
assert!(!has(b"ALTERNATEORIGINAL"), "the alternate image survived the save");
|
||||
assert!(!has(b"OPISERVERPATH"), "the OPI link survived the save");
|
||||
}
|
||||
Reference in new issue
Block a user