Tolerate non-dictionary /Resources and /XObject in image scanner

A malformed PDF may store a non-dictionary object (an array, name, or
other type) at /Resources or /Resources /XObject. The pdfinfo image
scanner iterated these with .items()/.as_dict() and probed them with the
`in` operator, which raise TypeError/ValueError on non-dictionary pikepdf
objects and crashed PdfInfo on otherwise-processable files. OCRmyPDF's
domain is messy, machine-generated PDFs, so scanning must tolerate this.

Guard _image_xobjects and _find_form_xobject_images with
isinstance(x, Dictionary) before iterating, treating a non-dictionary
/Resources or /XObject as "no image XObjects". This is the same
robustness class as the pdfa.py find_nonembedded_cid_fonts fix, applied
to the pdfinfo image scanner.
This commit is contained in:
James R. Barlow
2026-07-17 00:05:50 -07:00
parent 9cda02317b
commit b60df59c62
2 changed files with 64 additions and 4 deletions
+15 -4
View File
@@ -287,9 +287,15 @@ def _image_xobjects(container) -> Iterator[tuple[Object, str]]:
if Name.Resources not in container:
return
resources = container[Name.Resources]
if Name.XObject not in resources:
# A malformed PDF may store a non-dictionary at /Resources or
# /Resources /XObject; treat that as "no image XObjects" instead of
# crashing when we try to iterate it.
if not isinstance(resources, Dictionary):
return
for key, candidate in resources[Name.XObject].items():
xobjects = resources.get(Name.XObject)
if not isinstance(xobjects, Dictionary):
return
for key, candidate in xobjects.items():
if candidate is None or Name.Subtype not in candidate:
continue
if candidate[Name.Subtype] == Name.Image:
@@ -336,9 +342,14 @@ def _find_form_xobject_images(pdf: Pdf, container: Object, contentsinfo: Content
if Name.Resources not in container:
return
resources = container[Name.Resources]
if Name.XObject not in resources:
# As in _image_xobjects, tolerate a non-dictionary /Resources or
# /Resources /XObject in a malformed PDF rather than crashing.
if not isinstance(resources, Dictionary):
return
xobjs = resources[Name.XObject].as_dict()
xobject = resources.get(Name.XObject)
if not isinstance(xobject, Dictionary):
return
xobjs = xobject.as_dict()
for xobj in xobjs:
candidate = xobjs[xobj]
if candidate is None or candidate.get(Name.Subtype) != Name.Form: