Summary
Stream objects and plain dictionaries are syntactically distinct in PDF, but object-kind mismatches in page structures are downgraded to warnings and hidden by recovery. A dictionary used as page Contents becomes empty content, while a stream used as a page dictionary becomes a synthesized empty page.
Versions tested
Test files
Reproduction
import playa
for path in (
"Dialect-StreamIsDict.pdf",
"Dialect-DictIsStream.pdf",
):
with playa.open(path) as document:
page = document.pages[0]
print(path, page.mediabox, list(page.contents), repr(page.extract_text()))
Actual behavior
For Dialect-StreamIsDict.pdf, Playa logs Found non-stream in contents and returns an empty contents sequence without raising an error.
For Dialect-DictIsStream.pdf, Playa logs Missing or invalid page object 4, substitutes an empty 612 by 792 page, and returns empty contents and text without raising an error.
Expected behavior
The missing stream/endstream delimiters in the first file and the unexpected stream wrappers in the second file are PDF syntax errors. The object-kind mismatch should remain observable to callers as a type or syntax error. If recovery is retained, it should not silently replace the malformed objects without an error channel.
Relevant implementation
ContentParser.__init__() catches TypeError from stream_value() and initializes an empty parser buffer; Page.streams catches the same error and skips the object. In the inverse case, Pages._get_pages() catches TypeError from dict_value() and creates a synthetic empty page.
Disclosure
This issue was prepared by an AI coding agent on behalf of @lin-string. The reproduction and results were verified against the current default branch.
Summary
Stream objects and plain dictionaries are syntactically distinct in PDF, but object-kind mismatches in page structures are downgraded to warnings and hidden by recovery. A dictionary used as page Contents becomes empty content, while a stream used as a page dictionary becomes a synthesized empty page.
Versions tested
playa-pdf1.1.0 (v1.1.0, commit85a9c1e22e327bf873e4282d66ed1dba89fa7575)mainat commit9496dfea5150343c9dc05544d9c004bbcccf17e6, checked on 2026-09-06Test files
Reproduction
Actual behavior
For
Dialect-StreamIsDict.pdf, Playa logsFound non-stream in contentsand returns an empty contents sequence without raising an error.For
Dialect-DictIsStream.pdf, Playa logsMissing or invalid page object 4, substitutes an empty 612 by 792 page, and returns empty contents and text without raising an error.Expected behavior
The missing
stream/endstreamdelimiters in the first file and the unexpected stream wrappers in the second file are PDF syntax errors. The object-kind mismatch should remain observable to callers as a type or syntax error. If recovery is retained, it should not silently replace the malformed objects without an error channel.Relevant implementation
ContentParser.__init__()catchesTypeErrorfromstream_value()and initializes an empty parser buffer;Page.streamscatches the same error and skips the object. In the inverse case,Pages._get_pages()catchesTypeErrorfromdict_value()and creates a synthetic empty page.Disclosure
This issue was prepared by an AI coding agent on behalf of @lin-string. The reproduction and results were verified against the current default branch.