Handle inline (non-reference) structure elements#1379
Open
gaoflow wants to merge 1 commit into
Open
Conversation
A structure element may be embedded directly as a dictionary in another element's /K, rather than via an indirect reference (PDF 1.7 section 14.7.2, which _make_element and the struct-tree root already accommodate). The per-child traversal only enqueued PDFObjRef children, so an inline child was never registered, and structure_tree raised a KeyError in prune(). Enqueue and resolve inline structure-element dicts too.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
PDF.structure_tree/Page.structure_treeraiseKeyErrorwhen a structure element is embedded inline — as a direct dictionary in another element's/K— rather than as an indirect reference:Per PDF 1.7 §14.7.2, the
/Kvalue "may be ... a structure element dictionary ... or an array of such"._make_elementalready has an explicit branch for an inline-dict child (# a single object.. ugh...), and_parse_struct_tree/_resolve_childrenalready handle an inline dict at the root — but the per-child traversal does not, so a nested inline element crashes.Root cause
In
_parse_struct_tree's main loop, onlyPDFObjRefchildren are pushed onto the work queue (and thus registered in theslookup). An inline structure-element dict is silently skipped, so it never gets ansentry — andprune()then hitss[repr(ref)]→KeyError._resolve_childrenfollows the samePDFObjRef-only rule, so even past the crash an inline child would be dropped from the output.The fix
Two symmetric one-line changes: in both the traversal loop and
_resolve_children, treat a child that is a structure-element dictionary the same as an indirect reference (isinstance(child, (PDFObjRef, dict))). MCID dicts were already handled/continued earlier and are not in the lookup, so they're unaffected;prune()needs no change once inline elements are registered.Tests
test_inline_structure_elementbuilds a minimal tagged PDF in memory whoseDocumentelement holds an inlinePelement in its/Karray, and asserts the parsed tree on both the document-level (pdf.structure_tree) and page-level (page.structure_tree) code paths. It fails ondevelopwith the exactKeyErrorand passes with the fix;tests/test_structure.py+tests/test_mcids.py+tests/test_basics.pystay green (39 passed);black/isort/flake8clean. CHANGELOG updated.(None of the 65 shipped test PDFs exercise inline structure children, so this path was previously untested.)