From 8d485c38cd1656ba63fc378f6007c4aafc9d28f4 Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Tue, 5 Sep 2023 17:47:14 -0400 Subject: [PATCH] test: trivial synthetic pdf for completing structure coverage --- tests/pdfs/hello_structure.pdf | 145 +++++++++++++++++++++++++++++++++ tests/pdfs/make_xref.py | 27 ++++++ tests/test_structure.py | 27 ++++++ 3 files changed, 199 insertions(+) create mode 100644 tests/pdfs/hello_structure.pdf create mode 100644 tests/pdfs/make_xref.py diff --git a/tests/pdfs/hello_structure.pdf b/tests/pdfs/hello_structure.pdf new file mode 100644 index 0000000..a0558ca --- /dev/null +++ b/tests/pdfs/hello_structure.pdf @@ -0,0 +1,145 @@ +%PDF-1.4 +1 0 obj + << /Type /Catalog + /Outlines 2 0 R + /Pages 3 0 R + /StructTreeRoot 7 0 R + >> +endobj + +2 0 obj + << /Type /Outlines + /Count 0 + >> +endobj + +3 0 obj + << /Type /Pages + /MediaBox [0 0 612 792] + /Kids [4 0 R 11 0 R] + /Count 2 + >> +endobj + +4 0 obj + << /Type /Page + /Parent 3 0 R + /Contents 6 0 R + /StructParents 0 + /Resources << /ProcSet [/PDF /Text] + /Font << /F1 5 0 R >> + >> + >> +endobj + +5 0 obj + << /Type /Font + /Subtype /Type1 + /Name /F1 + /BaseFont /Helvetica + >> +endobj + +6 0 obj + << /Length 87 >> +stream + /P <> BDC + BT + /F1 24 Tf + 100 700 Td + (Hello World) Tj + ET + EMC +endstream +endobj + +7 0 obj + << /Type /StructTreeRoot + /ParentTree 8 0 R + /RoleMap << /Title /P >> + /ClassMap << /C1 << /O /Foo /A1 1 >> >> + /K [9 0 R] + >> +endobj + +8 0 obj + << /Nums [ + 0 [10 0 R] + 1 [13 0 R] + ] + >> +endobj + +9 0 obj + << /Type /StructElem + /S /Document + /P 7 0 R + /K [10 0 R 13 0 R] + >> +endobj + +10 0 obj + << /Type /StructElem + /S /Title + /P 9 0 R + /Pg 4 0 R + /C /C1 + /K [1] + >> +endobj + +11 0 obj + << /Type /Page + /Parent 3 0 R + /Contents 12 0 R + /StructParents 1 + /Resources << /ProcSet [/PDF /Text] + /Font << /F1 5 0 R >> + >> + >> +endobj + +12 0 obj + << /Length 101 >> +stream + /P <> BDC + BT + /F1 24 Tf + 100 700 Td + (Goodbye Cruel World) Tj + ET + endstream +EMC +endobj + +13 0 obj + << /Type /StructElem + /S /Title + /P 9 0 R + /Pg 11 0 R + /K [1] + /C /C1 + /A << /A1 2 /A2 2 >> + >> +endobj + +xref +0 14 +0000000000 65535 f +0000000009 00000 n +0000000116 00000 n +0000000172 00000 n +0000000280 00000 n +0000000481 00000 n +0000000581 00000 n +0000000720 00000 n +0000000882 00000 n +0000000975 00000 n +0000001075 00000 n +0000001195 00000 n +0000001398 00000 n +0000001547 00000 n +trailer << /Size 14 /Root 1 0 R >> +startxref +1695 +%%EOF diff --git a/tests/pdfs/make_xref.py b/tests/pdfs/make_xref.py new file mode 100644 index 0000000..96c6c8b --- /dev/null +++ b/tests/pdfs/make_xref.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +"""Create an xref section for a simple handmade PDF. + +Not a general purpose tool!!!""" + +import re +import sys + +with open(sys.argv[1], "rb") as infh: + pos = 0 + xref = [(0, 65535, "f")] + for spam in infh: + text = spam.decode("ascii") + if re.match(r"\s*(\d+)\s+(\d+)\s+obj", text): + xref.append((pos, 0, "n")) + elif text.strip() == "xref": + startxref = pos + pos = infh.tell() + print("xref") + print("0", len(xref)) + for x in xref: + print("%010d %05d %s " % x) + print("trailer << /Size %d /Root 1 0 R >>" % len(xref)) + print("startxref") + print(startxref) + print("%%EOF") diff --git a/tests/test_structure.py b/tests/test_structure.py index 7f4297a..7470fd5 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -801,6 +801,27 @@ SCOTUS = [ ] +HELLO = [ + { + "type": "Document", + "children": [ + { + "type": "P", + "page_number": 1, + "attributes": {"O": "Foo", "A1": 1}, + "mcids": [1], + }, + { + "type": "P", + "page_number": 2, + "attributes": {"O": "Foo", "A1": 2, "A2": 2}, + "mcids": [1], + }, + ], + } +] + + class TestClass(unittest.TestCase): """Test the underlying Structure tree class""" @@ -904,3 +925,9 @@ class TestMany(unittest.TestCase): if "children" in el1: assert len(el1["children"]) == len(el2["children"]) d.extend(zip(el1["children"], el2["children"])) + + def test_hello_structure(self): + # Synthetic PDF to test some corner cases + path = os.path.join(HERE, "pdfs/hello_structure.pdf") + pdf = pdfplumber.open(path) + assert pdf.structure_tree == HELLO