From 5e121c96906d4d81b882f84407459f5e1312af34 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Tue, 18 Jun 2024 08:47:14 -0400 Subject: [PATCH] Handle null form field from annotation (fixes #1189) A file that has Widget annotations that can't be mapped back to form fields would crash qpdf json. --- libqpdf/QPDFJob.cc | 3 + qpdf/qtest/copy-annotations.test | 9 +- qpdf/qtest/qpdf/annotations-no-acroform.json | 765 +++++++++++++++++++ 3 files changed, 776 insertions(+), 1 deletion(-) create mode 100644 qpdf/qtest/qpdf/annotations-no-acroform.json diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index d76098f6..55902865 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -1168,6 +1168,9 @@ QPDFJob::doJSONAcroform(Pipeline* p, bool& first, QPDF& pdf) ++pagepos1; for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) { QPDFFormFieldObjectHelper ffh = afdh.getFieldForAnnotation(aoh); + if (!ffh.getObjectHandle().isDictionary()) { + continue; + } JSON j_field = j_fields.addArrayElement(JSON::makeDictionary()); j_field.addDictionaryMember("object", ffh.getObjectHandle().getJSON(m->json_version)); j_field.addDictionaryMember( diff --git a/qpdf/qtest/copy-annotations.test b/qpdf/qtest/copy-annotations.test index 8a6aa2d6..44aaf74a 100644 --- a/qpdf/qtest/copy-annotations.test +++ b/qpdf/qtest/copy-annotations.test @@ -14,7 +14,7 @@ cleanup(); my $td = new TestDriver('copy-annotations'); -my $n_tests = 45; +my $n_tests = 46; $td->runtest("complex copy annotations", {$td->COMMAND => @@ -50,6 +50,13 @@ $td->runtest("copy annotations no acroform from foreign file", {$td->STRING => "", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("get json no acroform", + {$td->COMMAND => + "qpdf --json annotations-no-acroform.pdf"}, + {$td->FILE => "annotations-no-acroform.json", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + $td->runtest("check output", {$td->FILE => "a.pdf"}, {$td->FILE => "annotations-same-file.out.pdf"}); diff --git a/qpdf/qtest/qpdf/annotations-no-acroform.json b/qpdf/qtest/qpdf/annotations-no-acroform.json new file mode 100644 index 00000000..a099d788 --- /dev/null +++ b/qpdf/qtest/qpdf/annotations-no-acroform.json @@ -0,0 +1,765 @@ +{ + "version": 2, + "parameters": { + "decodelevel": "generalized" + }, + "pages": [ + { + "contents": [ + "39 0 R" + ], + "images": [], + "label": null, + "object": "17 0 R", + "outlines": [], + "pageposfrom1": 1 + } + ], + "pagelabels": [], + "acroform": { + "fields": [], + "hasacroform": false, + "needappearances": false + }, + "attachments": { + "attachment1.txt": { + "description": null, + "filespec": "16 0 R", + "names": { + "/F": "attachment1.txt", + "/UF": "attachment1.txt" + }, + "preferredcontents": "31 0 R", + "preferredname": "attachment1.txt", + "streams": { + "/F": { + "checksum": "80a33fc110b5a7b8b4d58b8d57e814bc", + "creationdate": null, + "mimetype": null, + "modificationdate": null + }, + "/UF": { + "checksum": "80a33fc110b5a7b8b4d58b8d57e814bc", + "creationdate": null, + "mimetype": null, + "modificationdate": null + } + } + } + }, + "encrypt": { + "capabilities": { + "accessibility": true, + "extract": true, + "modify": true, + "modifyannotations": true, + "modifyassembly": true, + "modifyforms": true, + "modifyother": true, + "printhigh": true, + "printlow": true + }, + "encrypted": false, + "ownerpasswordmatched": false, + "parameters": { + "P": 0, + "R": 0, + "V": 0, + "bits": 0, + "filemethod": "none", + "key": null, + "method": "none", + "streammethod": "none", + "stringmethod": "none" + }, + "recovereduserpassword": null, + "userpasswordmatched": false + }, + "outlines": [], + "qpdf": [ + { + "jsonversion": 2, + "pdfversion": "1.6", + "pushedinheritedpageresources": false, + "calledgetallpages": true, + "maxobjectid": 52 + }, + { + "obj:1 0 R": { + "value": { + "/Names": { + "/EmbeddedFiles": "6 0 R" + }, + "/Pages": "7 0 R", + "/Type": "/Catalog" + } + }, + "obj:2 0 R": { + "value": { + "/Font": { + "/F1": "8 0 R" + } + } + }, + "obj:3 0 R": { + "value": { + "/AP": { + "/N": "9 0 R" + }, + "/DA": "u:0 0.4 0 rg /F1 18 Tf", + "/DR": "2 0 R", + "/DV": "u:", + "/FT": "/Tx", + "/Ff": 0, + "/Rect": [ + 72, + 470.774, + 190.8, + 484.922 + ], + "/Subtype": "/Widget", + "/T": "u:Text Box 1", + "/Type": "/Annot", + "/V": "u:Formy field" + } + }, + "obj:4 0 R": { + "value": { + "/AP": { + "/N": "11 0 R" + }, + "/DA": "u:0 0.4 0 rg /F1 18 Tf", + "/DR": "2 0 R", + "/DV": "u:", + "/FT": "/Tx", + "/Ff": 0, + "/Rect": [ + 372, + 330.774, + 386.148, + 470.374 + ], + "/Subtype": "/Widget", + "/T": "u:Text Box 2", + "/Type": "/Annot", + "/V": "u:Rot-ccw field" + } + }, + "obj:5 0 R": { + "value": { + "/DV": "/1", + "/FT": "/Btn", + "/Ff": 49152, + "/Kids": [ + "13 0 R", + "14 0 R", + "15 0 R" + ], + "/T": "u:r1", + "/V": "/2" + } + }, + "obj:6 0 R": { + "value": { + "/Names": [ + "u:attachment1.txt", + "16 0 R" + ] + } + }, + "obj:7 0 R": { + "value": { + "/Count": 1, + "/Kids": [ + "17 0 R" + ], + "/Type": "/Pages" + } + }, + "obj:8 0 R": { + "value": { + "/BaseFont": "/Courier", + "/Encoding": "/WinAnsiEncoding", + "/Subtype": "/Type1", + "/Type": "/Font" + } + }, + "obj:9 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + -2.826, + 118.8, + 11.322 + ], + "/Length": "10 0 R", + "/Resources": "2 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:10 0 R": { + "value": 53 + }, + "obj:11 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + -2.826, + 140.4, + 11.322 + ], + "/Length": "12 0 R", + "/Matrix": [ + 0, + 1, + -1, + 0, + 0, + 0 + ], + "/Resources": "2 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:12 0 R": { + "value": 55 + }, + "obj:13 0 R": { + "value": { + "/AP": { + "/N": { + "/1": "18 0 R", + "/Off": "20 0 R" + } + }, + "/AS": "/1", + "/DA": "u:0.18039 0.20392 0.21176 rg /ZaDi 0 Tf", + "/DR": { + "/Font": { + "/ZaDi": "22 0 R" + } + }, + "/F": 4, + "/FT": "/Btn", + "/MK": { + "/CA": "u:l" + }, + "/Parent": "5 0 R", + "/Rect": [ + 152.749, + 648.501, + 164.801, + 660.549 + ], + "/Subtype": "/Widget", + "/Type": "/Annot" + } + }, + "obj:14 0 R": { + "value": { + "/AP": { + "/N": { + "/2": "23 0 R", + "/Off": "25 0 R" + } + }, + "/AS": "/2", + "/DA": "u:0.18039 0.20392 0.21176 rg /ZaDi 0 Tf", + "/DR": { + "/Font": { + "/ZaDi": "22 0 R" + } + }, + "/F": 4, + "/FT": "/Btn", + "/MK": { + "/CA": "u:l" + }, + "/Parent": "5 0 R", + "/Rect": [ + 152.749, + 627.301, + 164.801, + 639.349 + ], + "/Subtype": "/Widget", + "/Type": "/Annot" + } + }, + "obj:15 0 R": { + "value": { + "/AP": { + "/N": { + "/3": "27 0 R", + "/Off": "29 0 R" + } + }, + "/AS": "/3", + "/DA": "u:0.18039 0.20392 0.21176 rg /ZaDi 0 Tf", + "/DR": { + "/Font": { + "/ZaDi": "22 0 R" + } + }, + "/F": 4, + "/FT": "/Btn", + "/MK": { + "/CA": "u:l" + }, + "/Parent": "5 0 R", + "/Rect": [ + 151.399, + 606.501, + 163.451, + 618.549 + ], + "/Subtype": "/Widget", + "/Type": "/Annot" + } + }, + "obj:16 0 R": { + "value": { + "/EF": { + "/F": "31 0 R", + "/UF": "31 0 R" + }, + "/F": "u:attachment1.txt", + "/Type": "/Filespec", + "/UF": "u:attachment1.txt" + } + }, + "obj:17 0 R": { + "value": { + "/Annots": [ + "33 0 R", + "3 0 R", + "34 0 R", + "4 0 R", + "35 0 R", + "36 0 R", + "37 0 R", + "38 0 R", + "13 0 R", + "14 0 R", + "15 0 R" + ], + "/Contents": "39 0 R", + "/MediaBox": [ + 0, + 0, + 612, + 792 + ], + "/Parent": "7 0 R", + "/Resources": "2 0 R", + "/Type": "/Page" + } + }, + "obj:18 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 12.05, + 12.05 + ], + "/Length": "19 0 R", + "/Resources": "41 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:19 0 R": { + "value": 202 + }, + "obj:20 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 12.05, + 12.05 + ], + "/Length": "21 0 R", + "/Resources": "41 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:21 0 R": { + "value": 12 + }, + "obj:22 0 R": { + "value": { + "/BaseFont": "/ZapfDingbats", + "/Subtype": "/Type1", + "/Type": "/Font" + } + }, + "obj:23 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 12.05, + 12.05 + ], + "/Length": "24 0 R", + "/Resources": "41 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:24 0 R": { + "value": 202 + }, + "obj:25 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 12.05, + 12.05 + ], + "/Length": "26 0 R", + "/Resources": "41 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:26 0 R": { + "value": 12 + }, + "obj:27 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 12.05, + 12.05 + ], + "/Length": "28 0 R", + "/Resources": "41 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:28 0 R": { + "value": 202 + }, + "obj:29 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 12.05, + 12.05 + ], + "/Length": "30 0 R", + "/Resources": "41 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:30 0 R": { + "value": 12 + }, + "obj:31 0 R": { + "stream": { + "dict": { + "/Length": "32 0 R", + "/Params": { + "/CheckSum": "b:80a33fc110b5a7b8b4d58b8d57e814bc", + "/Size": 22, + "/Subtype": "/text/plain" + }, + "/Type": "/EmbeddedFile" + } + } + }, + "obj:32 0 R": { + "value": 22 + }, + "obj:33 0 R": { + "value": { + "/A": { + "/S": "/URI", + "/URI": "u:https://www.qbilt.org/" + }, + "/Border": [ + 0, + 0, + 0.4 + ], + "/C": [ + 0.8, + 0.6, + 0.6 + ], + "/H": "/I", + "/Rect": [ + 72, + 501.832, + 374.4, + 520.696 + ], + "/Subtype": "/Link", + "/Type": "/Annot" + } + }, + "obj:34 0 R": { + "value": { + "/AP": { + "/N": "42 0 R" + }, + "/Contents": "u:attachment1.txt", + "/FS": "16 0 R", + "/NM": "u:attachment1.txt", + "/Rect": [ + 72, + 400, + 92, + 420 + ], + "/Subtype": "/FileAttachment", + "/Type": "/Annot" + } + }, + "obj:35 0 R": { + "value": { + "/AP": { + "/N": "44 0 R" + }, + "/DA": "u:", + "/Rect": [ + 72, + 350, + 92, + 360 + ], + "/Subtype": "/FreeText", + "/Type": "/Annot" + } + }, + "obj:36 0 R": { + "value": { + "/AP": { + "/N": "46 0 R" + }, + "/DA": "u:", + "/Rect": [ + 102, + 350, + 112, + 370 + ], + "/Subtype": "/FreeText", + "/Type": "/Annot" + } + }, + "obj:37 0 R": { + "value": { + "/AP": { + "/N": "48 0 R" + }, + "/DA": "u:", + "/Rect": [ + 122, + 350, + 142, + 360 + ], + "/Subtype": "/FreeText", + "/Type": "/Annot" + } + }, + "obj:38 0 R": { + "value": { + "/AP": { + "/N": "50 0 R" + }, + "/DA": "u:", + "/Rect": [ + 152, + 350, + 162, + 370 + ], + "/Subtype": "/FreeText", + "/Type": "/Annot" + } + }, + "obj:39 0 R": { + "stream": { + "dict": { + "/Length": "40 0 R" + } + } + }, + "obj:40 0 R": { + "value": 874 + }, + "obj:41 0 R": { + "value": { + "/Font": "52 0 R", + "/ProcSet": [ + "/PDF", + "/Text" + ] + } + }, + "obj:42 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 20, + 20 + ], + "/Length": "43 0 R", + "/Resources": {}, + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:43 0 R": { + "value": 52 + }, + "obj:44 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 20, + 10 + ], + "/Length": "45 0 R", + "/Resources": "2 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:45 0 R": { + "value": 36 + }, + "obj:46 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 20, + 10 + ], + "/Length": "47 0 R", + "/Matrix": [ + 0, + 1, + -1, + 0, + 0, + 0 + ], + "/Resources": "2 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:47 0 R": { + "value": 36 + }, + "obj:48 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 20, + 10 + ], + "/Length": "49 0 R", + "/Matrix": [ + -1, + 0, + 0, + -1, + 0, + 0 + ], + "/Resources": "2 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:49 0 R": { + "value": 36 + }, + "obj:50 0 R": { + "stream": { + "dict": { + "/BBox": [ + 0, + 0, + 20, + 10 + ], + "/Length": "51 0 R", + "/Matrix": [ + 0, + -1, + 1, + 0, + 0, + 0 + ], + "/Resources": "2 0 R", + "/Subtype": "/Form", + "/Type": "/XObject" + } + } + }, + "obj:51 0 R": { + "value": 38 + }, + "obj:52 0 R": { + "value": { + "/ZaDi": "22 0 R" + } + }, + "trailer": { + "value": { + "/ID": [ + "b:a2f146daeb6d814a742556489dab9882", + "b:7b639c67bfc16b5e891fa5468aac3a14" + ], + "/Root": "1 0 R", + "/Size": 53 + } + } + } + ] +}