Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Tests/test_pdfparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,42 @@ def test_linearize_page_tree() -> None:
assert pdf.linearize_page_tree() == page_ids


def test_linearize_page_tree_duplicate_reference() -> None:
b = BytesIO()
with PdfParser(f=b, mode="wb") as pdf:
pdf.start_writing()
pdf.write_header()

pages_ids = [pdf.next_object_id(0) for _ in range(2)]
pdf.write_obj(
pages_ids[0], Type=PdfName(b"Pages"), Count=1, Kids=[pages_ids[1]]
)
pdf.write_obj(
pages_ids[1], Type=PdfName(b"Pages"), Count=1, Kids=[pages_ids[0]]
)

pdf.write_catalog()
pdf.write_xref_and_trailer()

with PdfParser(f=b) as pdf:
with pytest.raises(PdfFormatError, match="cyclic or duplicate reference"):
pdf.linearize_page_tree(
PdfDict({b"Type": b"Pages", b"Kids": [pages_ids[0]]})
)

with PdfParser(f=b, mode="wb") as pdf:
pdf.start_writing()
pdf.write_header()

pdf.write_catalog()
pdf.write_xref_and_trailer()

with PdfParser(f=b) as pdf:
pdf.page_tree_root[b"Kids"] = [pdf.pages_ref]
with pytest.raises(PdfFormatError, match="cyclic or duplicate reference"):
pdf.linearize_page_tree()


def test_duplicate_xref_entry() -> None:
pdf = PdfParser("Tests/images/duplicate_xref_entry.pdf")
assert pdf.xref_table.existing_entries[6][0] == 1197
Expand Down
20 changes: 16 additions & 4 deletions src/PIL/PdfParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ def read_pdf_info(self) -> None:
self.pages_ref = self.root[b"Pages"]
assert self.pages_ref is not None
self.page_tree_root = self.read_indirect(self.pages_ref)
self.pages = self.linearize_page_tree(self.page_tree_root)
self.pages = self.linearize_page_tree()
# save the original list of page references
# in case the user modifies, adds or deletes some pages
# and we need to rewrite the pages and their list
Expand Down Expand Up @@ -1083,17 +1083,29 @@ def read_indirect(self, ref: IndirectReference, max_nesting: int = -1) -> Any:
return value

def linearize_page_tree(
self, node: PdfDict | None = None
self, node: PdfDict | None = None, processed_ids: set[int] | None = None
) -> list[IndirectReference]:
page_node = node if node is not None else self.page_tree_root
if processed_ids is None:
processed_ids = set()
if node is not None:
page_node = node
else:
page_node = self.page_tree_root
if self.pages_ref is not None:
processed_ids.add(self.pages_ref.object_id)
check_format_condition(
page_node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages"
)
pages = []
for kid in page_node[b"Kids"]:
check_format_condition(
kid.object_id not in processed_ids,
f"page tree contains a cyclic or duplicate reference to {kid}",
)
processed_ids.add(kid.object_id)
kid_object = self.read_indirect(kid)
if kid_object[b"Type"] == b"Page":
pages.append(kid)
else:
pages.extend(self.linearize_page_tree(node=kid_object))
pages.extend(self.linearize_page_tree(kid_object, processed_ids))
return pages
Loading