mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 04:22:20 +00:00
M2.6: marking content as an artifact refuses what may be past the damage
A tag's content not found before the damage in a damaged stream may be past it. Marking a figure decorative took it for absent: it succeeded, took the figure out of the structure tree and left its content tagged. Marking a tag as an artifact said its content wasn't on the page. Both now refuse with `Damaged`, as when the content is before the damage, and change nothing. Clearing the parent tree read the page's `/StructParents` only when direct, so with it given by reference (ISO 32000-2 lets it be) the tree went on pointing the content at the retired tag. It is read through the reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
This commit is contained in:
1 parent
69e26167e4
commit
3bf6c6f027
2 files changed
+44
-3
No files matched your search
@@ -417,7 +417,7 @@ enum Marked {
|
||||
/// None of the content is on the page, or with `require_all` not all of it: nothing changed.
|
||||
NotFound,
|
||||
/// Some of it is in a stream damaged partway, which can't be rewritten without losing what
|
||||
/// follows the damage: nothing changed.
|
||||
/// follows the damage, or may be past the damage: nothing changed.
|
||||
Damaged,
|
||||
}
|
||||
|
||||
@@ -442,6 +442,11 @@ fn artifact_marked_content(
|
||||
edits.push((op.span, b"/Artifact BMC".to_vec()));
|
||||
}
|
||||
}
|
||||
// Content not found may be past the damage in a damaged stream: whether it is on the page
|
||||
// isn't known.
|
||||
if found != *ids && streams.iter().any(|(_, s)| s.as_ref().is_some_and(|(_, _, damaged)| *damaged)) {
|
||||
return Ok(Marked::Damaged);
|
||||
}
|
||||
if if require_all { found != *ids } else { found.is_empty() } {
|
||||
return Ok(Marked::NotFound);
|
||||
}
|
||||
@@ -494,7 +499,7 @@ pub fn mark_decorative(doc: &mut Document, obj: ObjRef) -> Result<(), AltError>
|
||||
match artifact_marked_content(doc, page, streams, &ids, false)? {
|
||||
// The parent tree no longer points those ids at the figure.
|
||||
Marked::Done => {
|
||||
if let Some(key) = page.dict.get(b"StructParents").and_then(Object::as_int) {
|
||||
if let Some(key) = page.dict.get(b"StructParents").and_then(|o| doc.resolve(o).as_int()) {
|
||||
clear_parent_tree(doc, key, obj)?;
|
||||
}
|
||||
}
|
||||
@@ -545,7 +550,7 @@ pub fn mark_tag_artifact(doc: &mut Document, obj: ObjRef) -> Result<(), Artifact
|
||||
Marked::NotFound => return Err(ArtifactError::ContentNotFound),
|
||||
Marked::Damaged => return Err(ArtifactError::Damaged),
|
||||
}
|
||||
if let Some(key) = page.dict.get(b"StructParents").and_then(Object::as_int) {
|
||||
if let Some(key) = page.dict.get(b"StructParents").and_then(|o| doc.resolve(o).as_int()) {
|
||||
clear_parent_tree(doc, key, obj).map_err(|e| ArtifactError::ParentTree(e.to_string()))?;
|
||||
}
|
||||
doc.update_dict(parent, |p| p.set(b"K".to_vec(), replacement))?;
|
||||
|
||||
@@ -1129,6 +1129,42 @@ fn a_tags_content_in_a_stream_damaged_partway_is_not_rewritten() {
|
||||
assert_ne!(list.get(1), Some(&Object::Ref(ObjRef::new(4, 0))), "the tag's stream is rewritten");
|
||||
}
|
||||
|
||||
/// A tag's content not found before the damage in a damaged stream may be past it: it isn't known
|
||||
/// to be off the page, so neither marking changes anything.
|
||||
#[test]
|
||||
fn a_tags_content_past_the_damage_is_not_taken_for_absent() {
|
||||
use pdfcraft_cos::ObjRef;
|
||||
let damaged =
|
||||
damaged_flate(b"/Photo << /MCID 1 >> BDC 100 100 50 30 re f EMC\n", b"/Figure << /MCID 0 >> BDC q 40 0 0 20 10 150 cm /Im Do Q EMC");
|
||||
let mut doc = figures_doc();
|
||||
doc.set(ObjRef::new(4, 0), damaged);
|
||||
let contents = |doc: &Document| doc.get(ObjRef::new(3, 0)).as_dict().unwrap().get(b"Contents").cloned();
|
||||
let before = (contents(&doc), doc.get(ObjRef::new(9, 0)), doc.get(ObjRef::new(6, 0)));
|
||||
assert_eq!(mark_decorative(&mut doc, ObjRef::new(7, 0)), Err(AltError::Damaged));
|
||||
assert_eq!(mark_tag_artifact(&mut doc, ObjRef::new(7, 0)), Err(ArtifactError::Damaged));
|
||||
assert_eq!((contents(&doc), doc.get(ObjRef::new(9, 0)), doc.get(ObjRef::new(6, 0))), before);
|
||||
}
|
||||
|
||||
/// A page's `/StructParents` given by reference is read through it: the parent tree no longer
|
||||
/// points the marked content at the tag.
|
||||
#[test]
|
||||
fn struct_parents_given_by_reference_are_read() {
|
||||
use pdfcraft_cos::{ObjRef, Object};
|
||||
let by_reference = || {
|
||||
let mut doc = figures_doc();
|
||||
let key = doc.add(Object::Int(0));
|
||||
doc.update_dict(ObjRef::new(3, 0), |p| p.set(b"StructParents".to_vec(), Object::Ref(key))).unwrap();
|
||||
doc
|
||||
};
|
||||
let nums = |doc: &Document| doc.get(ObjRef::new(9, 0)).as_dict().and_then(|d| d.get(b"Nums").cloned());
|
||||
let mut doc = by_reference();
|
||||
mark_decorative(&mut doc, ObjRef::new(7, 0)).unwrap();
|
||||
assert_eq!(nums(&doc), Some(Object::Array(vec![Object::Int(0), Object::Array(vec![Object::Null, Object::Ref(ObjRef::new(8, 0))])])));
|
||||
let mut doc = by_reference();
|
||||
mark_tag_artifact(&mut doc, ObjRef::new(8, 0)).unwrap();
|
||||
assert_eq!(nums(&doc), Some(Object::Array(vec![Object::Int(0), Object::Array(vec![Object::Ref(ObjRef::new(7, 0)), Object::Null])])));
|
||||
}
|
||||
|
||||
/// A content stream that doesn't decode at all is kept as stored, and a tag in the page's other
|
||||
/// streams is still marked: only running past the decode budget refuses.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user