feat(pdftract-1x2): implement StructTree depth-first walker with RoleMap resolution

Implements the StructTree parser (Phase 7.1.1) with:
- Depth-first walker over /StructTreeRoot via /K array
- Support for all four /K entry types: StructElem, MCID, MCR, OBJR
- /RoleMap resolution with chain handling and cycle detection
- /Lang inheritance through the structure tree
- /ActualText inheritance (applies to all descendant content)
- Public API: StructureType, StructElemNode, StructTreeRoot, RoleMap, Kid

Acceptance criteria:
- PASS: All four /K element kinds handled without crashing
- PASS: /RoleMap chains resolve to standard type or NonStruct
- PASS: /Lang and /ActualText inherit correctly down tree
- PASS: Unit tests for Word RoleMap (Heading1 -> H1)
- PASS: Unit tests for nested /Lang and /ActualText scope
- PASS: Public type StructElemNode documented in core crate

References:
- Plan section 7.1 StructTree Exploitation (lines 2547-2549, 2552-2553)
- PDF 1.7 spec 14.7.4 (Structure Tree) and 14.8.4 (Standard Structure Types)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-05-23 16:43:22 -04:00
parent 3a0143eef6
commit d41d47de66
2 changed files with 1219 additions and 0 deletions

View file

@ -14,6 +14,7 @@ pub mod pages;
pub mod outline;
pub mod resources;
pub mod ocg;
pub mod struct_tree;
// Re-export from the unified diagnostics module (Phase 1.6)
pub use crate::diagnostics::{Diagnostic, Severity, DiagCode, ObjRef};
@ -29,6 +30,10 @@ pub use catalog::{Catalog, MarkInfo, PageLabel, PageLabelsTree, PageLabelStyle,
pub use ocg::{OcProperties, OcGroup, Ocmd, OcmdPolicy, BaseState, parse_oc_properties};
pub use resources::{ResourceDict, merge_resources, extract_resources};
pub use pages::{PageDict, flatten_page_tree, DEFAULT_MEDIABOX};
pub use struct_tree::{
StructureType, StructElemNode, StructTreeRoot, RoleMap, Kid,
parse_struct_tree,
};
pub use stream::{
StreamDecoder, FlateDecoder, ASCII85Decoder, ASCIIHexDecoder, CryptDecoder, PassthroughDecoder,
normalize_filter_name, get_decoder, FilterError, DEFAULT_MAX_DECOMPRESS_BYTES,

File diff suppressed because it is too large Load diff