From f049a77c5962a0e41723bc83900656ece821d916 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 30 May 2022 09:23:48 -0400 Subject: [PATCH] Add additional information when listing attachments --- ChangeLog | 3 + TODO | 3 - job.sums | 2 +- libqpdf/QPDFJob.cc | 65 +++- manual/cli.rst | 5 +- manual/release-notes.rst | 10 + qpdf/qtest/attachments.test | 3 +- qpdf/qtest/json.test | 1 + qpdf/qtest/qpdf/attachment-fields.pdf | 190 +++++++++++ qpdf/qtest/qpdf/filter-attachment-date.pl | 8 + qpdf/qtest/qpdf/json-attachment-fields-v1.out | 305 ++++++++++++++++++ qpdf/qtest/qpdf/json-attachment-fields-v2.out | 241 ++++++++++++++ qpdf/qtest/qpdf/list-attachments-1.out | 24 ++ qpdf/qtest/qpdf/list-attachments-2.out | 48 +++ qpdf/qtest/qpdf/list-attachments-3.out | 24 ++ qpdf/qtest/qpdf/list-attachments-4.out | 8 + qpdf/qtest/qpdf/test76-json.out | 63 +++- qpdf/qtest/qpdf/test76-list-verbose.out | 24 ++ qpdf/qtest/qpdf/utf16le-attachments.out | 8 + qtest/bin/qtest-driver | 16 +- 20 files changed, 1034 insertions(+), 17 deletions(-) create mode 100644 qpdf/qtest/qpdf/attachment-fields.pdf create mode 100644 qpdf/qtest/qpdf/filter-attachment-date.pl create mode 100644 qpdf/qtest/qpdf/json-attachment-fields-v1.out create mode 100644 qpdf/qtest/qpdf/json-attachment-fields-v2.out diff --git a/ChangeLog b/ChangeLog index ea445e64..0d60467d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-05-30 Jay Berkenbilt + * Include additional information in --list-attachments --verbose + and in --json --json-key=attachments. + * Add QUtil::qpdf_time_to_iso8601 and QUtil::pdf_time_to_iso8601 for converting PDF/qpdf timestamps to ISO-8601 date format. diff --git a/TODO b/TODO index e66fb49b..663dbb40 100644 --- a/TODO +++ b/TODO @@ -72,9 +72,6 @@ Remaining work: * --encryption: show recovered user password when available - * --list-attachments: add information from --verbose. Add to a - "details" subkey. - * Consider having --check, --show-encryption, etc., just select the right keys when in json mode. I don't think I want check on by default, so that might be different. diff --git a/job.sums b/job.sums index befae830..119c9bdf 100644 --- a/job.sums +++ b/job.sums @@ -14,4 +14,4 @@ libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a947431 libqpdf/qpdf/auto_job_json_init.hh 5f6b53e3c81d4b54ce5c4cf9c3f52d0c02f987c53bf8841c0280367bad23e335 libqpdf/qpdf/auto_job_schema.hh 9d543cd4a43eafffc2c4b8a6fee29e399c271c52cb6f7d417ae5497b3c1127dc manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 -manual/cli.rst e7c35f8183d015d7fe074e38baed4c89bad827fd9c23b4cafd73d562df82ab1b +manual/cli.rst 82ead389c03bbf5e0498bd0571a11dc06544d591f4e4454c00322e3473fc556d diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index 7c9e9bed..d7cc66cf 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -993,8 +993,16 @@ QPDFJob::doListAttachments(QPDF& pdf) } cout << " all data streams:" << std::endl; for (auto i2: efoh->getEmbeddedFileStreams().ditems()) { + auto efs = QPDFEFStreamObjectHelper(i2.second); cout << " " << i2.first << " -> " - << i2.second.getObjGen() << std::endl; + << efs.getObjectHandle().getObjGen() << std::endl; + cout << " creation date: " << efs.getCreationDate() + << std::endl + << " modification date: " << efs.getModDate() + << std::endl + << " mime type: " << efs.getSubtype() << std::endl + << " checksum: " + << QUtil::hex_encode(efs.getChecksum()) << std::endl; } }); } @@ -1445,6 +1453,22 @@ QPDFJob::doJSONEncrypt(Pipeline* p, bool& first, QPDF& pdf) void QPDFJob::doJSONAttachments(Pipeline* p, bool& first, QPDF& pdf) { + auto to_iso8601 = [](std::string const& d) { + // Convert PDF date to iso8601 if not empty; if empty, return + // empty. + std::string iso8601; + QUtil::pdf_time_to_iso8601(d, iso8601); + return iso8601; + }; + + auto null_or_string = [](std::string const& s) { + if (s.empty()) { + return JSON::makeNull(); + } else { + return JSON::makeString(s); + } + }; + JSON j_attachments = JSON::makeDictionary(); QPDFEmbeddedFileDocumentHelper efdh(pdf); for (auto const& iter: efdh.getEmbeddedFiles()) { @@ -1459,6 +1483,31 @@ QPDFJob::doJSONAttachments(Pipeline* p, bool& first, QPDF& pdf) j_details.addDictionaryMember( "preferredcontents", JSON::makeString(fsoh->getEmbeddedFileStream().unparse())); + j_details.addDictionaryMember( + "description", null_or_string(fsoh->getDescription())); + auto j_names = + j_details.addDictionaryMember("names", JSON::makeDictionary()); + for (auto const& i2: fsoh->getFilenames()) { + j_names.addDictionaryMember(i2.first, JSON::makeString(i2.second)); + } + auto j_streams = + j_details.addDictionaryMember("streams", JSON::makeDictionary()); + for (auto i2: fsoh->getEmbeddedFileStreams().ditems()) { + auto efs = QPDFEFStreamObjectHelper(i2.second); + auto j_stream = + j_streams.addDictionaryMember(i2.first, JSON::makeDictionary()); + j_stream.addDictionaryMember( + "creationdate", + null_or_string(to_iso8601(efs.getCreationDate()))); + j_stream.addDictionaryMember( + "modificationdate", + null_or_string(to_iso8601(efs.getCreationDate()))); + j_stream.addDictionaryMember( + "mimetype", null_or_string(efs.getSubtype())); + j_stream.addDictionaryMember( + "checksum", + null_or_string(QUtil::hex_encode(efs.getChecksum()))); + } } JSON::writeDictionaryItem(p, first, "attachments", j_attachments, 0); } @@ -1640,7 +1689,19 @@ QPDFJob::json_schema(int json_version, std::set* keys) "": { "filespec": "object containing the file spec", "preferredcontents": "most preferred embedded file stream", - "preferredname": "most preferred file name" + "preferredname": "most preferred file name", + "description": "description of attachment", + "names": { + "": "file name for key" + }, + "streams": { + "": { + "creationdate": "ISO-8601 creation date or null", + "modificationdate": "ISO-8601 modification date or null", + "mimetype": "mime type or null", + "checksum": "MD5 checksum or null" + } + } } })")); } diff --git a/manual/cli.rst b/manual/cli.rst index c6e408c6..194bfacd 100644 --- a/manual/cli.rst +++ b/manual/cli.rst @@ -3124,7 +3124,10 @@ Related Options :qpdf:ref:`--verbose`, additional information, including preferred file name, description, dates, and more are also displayed. The key is usually but not always equal to the file name and is needed by - some of the other options. See also :ref:`attachments`. + some of the other options. See also :ref:`attachments`. Note that + this option displays dates in PDF timestamp syntax. When attachment + information is included in json output (see :ref:`--json`), dates + are shown in ISO-8601 format. .. qpdf:option:: --show-attachment=key diff --git a/manual/release-notes.rst b/manual/release-notes.rst index 31ddabfd..d106ec11 100644 --- a/manual/release-notes.rst +++ b/manual/release-notes.rst @@ -96,6 +96,13 @@ For a detailed list of changes, please see the file - See :ref:`breaking-crypto-api` for specific details, and see :ref:`weak-crypto` for a general discussion. + - CLI Enhancements + + - ``qpdf --list-attachments --verbose`` include some additional + information about attachments. Additional information about + attachments is also included in the ``attachments`` json key + with ``--json``. + - Library Enhancements - New methods ``insertItemAndGet``, ``appendItemAndGet``, @@ -120,6 +127,9 @@ For a detailed list of changes, please see the file - Add new ``Pipeline`` type ``Pl_String`` to append to a ``std::string``. + - Add methods to QUtil for converting PDF timestamps and QPDFTime + objects to ISO-8601 timestamps. + - Enhance JSON class to better support incrementally reading and writing large amounts of data without having to keep everything in memory. diff --git a/qpdf/qtest/attachments.test b/qpdf/qtest/attachments.test index dbd47161..0d32ea0b 100644 --- a/qpdf/qtest/attachments.test +++ b/qpdf/qtest/attachments.test @@ -189,7 +189,8 @@ $td->runtest("add attachments: current date", $td->NORMALIZE_NEWLINES); $td->runtest("list attachments", {$td->COMMAND => - "qpdf --password=u --list-attachments a.pdf --verbose"}, + "qpdf --password=u --list-attachments a.pdf --verbose", + $td->FILTER => "perl filter-attachment-date.pl"}, {$td->FILE => "list-attachments-4.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); # The object to show here is the one in list-attachments-4.out diff --git a/qpdf/qtest/json.test b/qpdf/qtest/json.test index d010496b..1a2e1fe8 100644 --- a/qpdf/qtest/json.test +++ b/qpdf/qtest/json.test @@ -26,6 +26,7 @@ my @json_files = ( ['field-types', ['--show-encryption-key']], ['image-streams', ['--decode-level=all']], ['image-streams', ['--decode-level=specialized']], + ['attachment-fields', []], ['page-labels-and-outlines', ['--json-key=objects']], ['page-labels-and-outlines', ['--json-key=pages']], ['page-labels-and-outlines', ['--json-key=pagelabels']], diff --git a/qpdf/qtest/qpdf/attachment-fields.pdf b/qpdf/qtest/qpdf/attachment-fields.pdf new file mode 100644 index 00000000..f48c8f76 --- /dev/null +++ b/qpdf/qtest/qpdf/attachment-fields.pdf @@ -0,0 +1,190 @@ +%PDF-1.3 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Names << + /EmbeddedFiles 2 0 R + >> + /PageMode /UseAttachments + /Pages 3 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Names [ + (a.txt) + 4 0 R + + 5 0 R + ] +>> +endobj + +%% Original object ID: 3 0 +3 0 obj +<< + /Count 1 + /Kids [ + 6 0 R + ] + /Type /Pages +>> +endobj + +%% Original object ID: 4 0 +4 0 obj +<< + /EF << + /F 7 0 R + /UF 7 0 R + >> + /F (a.txt) + /Type /Filespec + /UF (a.txt) +>> +endobj + +%% Original object ID: 5 0 +5 0 obj +<< + /Desc (Two filenames) + /EF << + /F 9 0 R + /UF 9 0 R + >> + /F (pi.txt) + /Type /Filespec + /UF +>> +endobj + +%% Page 1 +%% Original object ID: 6 0 +6 0 obj +<< + /Contents 11 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 3 0 R + /Resources << + /Font << + /F1 13 0 R + >> + /ProcSet 14 0 R + >> + /Type /Page +>> +endobj + +%% Original object ID: 7 0 +7 0 obj +<< + /Params << + /Size 7 + >> + /Type /EmbeddedFile + /Length 8 0 R +>> +stream +potato +endstream +endobj + +8 0 obj +7 +endobj + +%% Original object ID: 8 0 +9 0 obj +<< + /Params << + /CheckSum + /CreationDate (D:20220530094116-05'00') + /ModDate (D:20220530094116-05'00') + /Size 7 + >> + /Subtype /text#2fplain + /Type /EmbeddedFile + /Length 10 0 R +>> +stream +potato +endstream +endobj + +10 0 obj +7 +endobj + +%% Contents for page 1 +%% Original object ID: 9 0 +11 0 obj +<< + /Length 12 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +12 0 obj +44 +endobj + +%% Original object ID: 10 0 +13 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 11 0 +14 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 15 +0000000000 65535 f +0000000052 00000 n +0000000203 00000 n +0000000330 00000 n +0000000429 00000 n +0000000564 00000 n +0000000753 00000 n +0000000975 00000 n +0000001089 00000 n +0000001134 00000 n +0000001406 00000 n +0000001475 00000 n +0000001576 00000 n +0000001624 00000 n +0000001759 00000 n +trailer << + /Root 1 0 R + /Size 15 + /ID [<3d1a89e08f9d6b3e1f787d04fceed440><4e13de38230bb10a03e49285a742c392>] +>> +startxref +1795 +%%EOF diff --git a/qpdf/qtest/qpdf/filter-attachment-date.pl b/qpdf/qtest/qpdf/filter-attachment-date.pl new file mode 100644 index 00000000..6def1b99 --- /dev/null +++ b/qpdf/qtest/qpdf/filter-attachment-date.pl @@ -0,0 +1,8 @@ +use warnings; +use strict; + +while (<>) +{ + s/D:\d{14}\S+//; + print; +} diff --git a/qpdf/qtest/qpdf/json-attachment-fields-v1.out b/qpdf/qtest/qpdf/json-attachment-fields-v1.out new file mode 100644 index 00000000..21fe88be --- /dev/null +++ b/qpdf/qtest/qpdf/json-attachment-fields-v1.out @@ -0,0 +1,305 @@ +{ + "version": 1, + "parameters": { + "decodelevel": "generalized" + }, + "pages": [ + { + "contents": [ + "11 0 R" + ], + "images": [], + "label": null, + "object": "6 0 R", + "outlines": [], + "pageposfrom1": 1 + } + ], + "pagelabels": [], + "acroform": { + "fields": [], + "hasacroform": false, + "needappearances": false + }, + "attachments": { + "a.txt": { + "description": null, + "filespec": "4 0 R", + "names": { + "/F": "a.txt", + "/UF": "a.txt" + }, + "preferredcontents": "7 0 R", + "preferredname": "a.txt", + "streams": { + "/F": { + "checksum": null, + "creationdate": null, + "mimetype": null, + "modificationdate": null + }, + "/UF": { + "checksum": null, + "creationdate": null, + "mimetype": null, + "modificationdate": null + } + } + }, + "Ï€.txt": { + "description": "Two filenames", + "filespec": "5 0 R", + "names": { + "/F": "pi.txt", + "/UF": "Ï€.txt" + }, + "preferredcontents": "9 0 R", + "preferredname": "Ï€.txt", + "streams": { + "/F": { + "checksum": "e561f9248d7563d15dd93457b02ebbb6", + "creationdate": "2022-05-30T09:41:16-05:00", + "mimetype": "text/plain", + "modificationdate": "2022-05-30T09:41:16-05:00" + }, + "/UF": { + "checksum": "e561f9248d7563d15dd93457b02ebbb6", + "creationdate": "2022-05-30T09:41:16-05:00", + "mimetype": "text/plain", + "modificationdate": "2022-05-30T09:41:16-05:00" + } + } + } + }, + "encrypt": { + "capabilities": { + "accessibility": true, + "extract": true, + "moddifyannotations": true, + "modify": 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" + }, + "userpasswordmatched": false + }, + "outlines": [], + "objects": { + "1 0 R": { + "/Names": { + "/EmbeddedFiles": "2 0 R" + }, + "/PageMode": "/UseAttachments", + "/Pages": "3 0 R", + "/Type": "/Catalog" + }, + "2 0 R": { + "/Names": [ + "a.txt", + "4 0 R", + "Ï€.txt", + "5 0 R" + ] + }, + "3 0 R": { + "/Count": 1, + "/Kids": [ + "6 0 R" + ], + "/Type": "/Pages" + }, + "4 0 R": { + "/EF": { + "/F": "7 0 R", + "/UF": "7 0 R" + }, + "/F": "a.txt", + "/Type": "/Filespec", + "/UF": "a.txt" + }, + "5 0 R": { + "/Desc": "Two filenames", + "/EF": { + "/F": "9 0 R", + "/UF": "9 0 R" + }, + "/F": "pi.txt", + "/Type": "/Filespec", + "/UF": "Ï€.txt" + }, + "6 0 R": { + "/Contents": "11 0 R", + "/MediaBox": [ + 0, + 0, + 612, + 792 + ], + "/Parent": "3 0 R", + "/Resources": { + "/Font": { + "/F1": "13 0 R" + }, + "/ProcSet": "14 0 R" + }, + "/Type": "/Page" + }, + "7 0 R": { + "/Length": "8 0 R", + "/Params": { + "/Size": 7 + }, + "/Type": "/EmbeddedFile" + }, + "8 0 R": 7, + "9 0 R": { + "/Length": "10 0 R", + "/Params": { + "/CheckSum": "Ã¥aù$“ucÑ]Ù4W°.Ȧ", + "/CreationDate": "D:20220530094116-05'00'", + "/ModDate": "D:20220530094116-05'00'", + "/Size": 7 + }, + "/Subtype": "/text#2fplain", + "/Type": "/EmbeddedFile" + }, + "10 0 R": 7, + "11 0 R": { + "/Length": "12 0 R" + }, + "12 0 R": 44, + "13 0 R": { + "/BaseFont": "/Helvetica", + "/Encoding": "/WinAnsiEncoding", + "/Subtype": "/Type1", + "/Type": "/Font" + }, + "14 0 R": [ + "/PDF", + "/Text" + ], + "trailer": { + "/ID": [ + "=ˆ›à‘šk>Ëœx}\u0004üîÔ@", + "N\u0013Þ8#\u000b±\n\u0003ä™–§BÙ" + ], + "/Root": "1 0 R", + "/Size": 15 + } + }, + "objectinfo": { + "1 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "2 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "3 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "4 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "5 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "6 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "7 0 R": { + "stream": { + "filter": null, + "is": true, + "length": 7 + } + }, + "8 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "9 0 R": { + "stream": { + "filter": null, + "is": true, + "length": 7 + } + }, + "10 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "11 0 R": { + "stream": { + "filter": null, + "is": true, + "length": 44 + } + }, + "12 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "13 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + }, + "14 0 R": { + "stream": { + "filter": null, + "is": false, + "length": null + } + } + } +} diff --git a/qpdf/qtest/qpdf/json-attachment-fields-v2.out b/qpdf/qtest/qpdf/json-attachment-fields-v2.out new file mode 100644 index 00000000..e94e05dc --- /dev/null +++ b/qpdf/qtest/qpdf/json-attachment-fields-v2.out @@ -0,0 +1,241 @@ +{ + "version": 2, + "parameters": { + "decodelevel": "generalized" + }, + "pages": [ + { + "contents": [ + "11 0 R" + ], + "images": [], + "label": null, + "object": "6 0 R", + "outlines": [], + "pageposfrom1": 1 + } + ], + "pagelabels": [], + "acroform": { + "fields": [], + "hasacroform": false, + "needappearances": false + }, + "attachments": { + "a.txt": { + "description": null, + "filespec": "4 0 R", + "names": { + "/F": "a.txt", + "/UF": "a.txt" + }, + "preferredcontents": "7 0 R", + "preferredname": "a.txt", + "streams": { + "/F": { + "checksum": null, + "creationdate": null, + "mimetype": null, + "modificationdate": null + }, + "/UF": { + "checksum": null, + "creationdate": null, + "mimetype": null, + "modificationdate": null + } + } + }, + "Ï€.txt": { + "description": "Two filenames", + "filespec": "5 0 R", + "names": { + "/F": "pi.txt", + "/UF": "Ï€.txt" + }, + "preferredcontents": "9 0 R", + "preferredname": "Ï€.txt", + "streams": { + "/F": { + "checksum": "e561f9248d7563d15dd93457b02ebbb6", + "creationdate": "2022-05-30T09:41:16-05:00", + "mimetype": "text/plain", + "modificationdate": "2022-05-30T09:41:16-05:00" + }, + "/UF": { + "checksum": "e561f9248d7563d15dd93457b02ebbb6", + "creationdate": "2022-05-30T09:41:16-05:00", + "mimetype": "text/plain", + "modificationdate": "2022-05-30T09:41:16-05:00" + } + } + } + }, + "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" + }, + "userpasswordmatched": false + }, + "outlines": [], + "objects": { + "obj:1 0 R": { + "value": { + "/Names": { + "/EmbeddedFiles": "2 0 R" + }, + "/PageMode": "/UseAttachments", + "/Pages": "3 0 R", + "/Type": "/Catalog" + } + }, + "obj:2 0 R": { + "value": { + "/Names": [ + "u:a.txt", + "4 0 R", + "u:Ï€.txt", + "5 0 R" + ] + } + }, + "obj:3 0 R": { + "value": { + "/Count": 1, + "/Kids": [ + "6 0 R" + ], + "/Type": "/Pages" + } + }, + "obj:4 0 R": { + "value": { + "/EF": { + "/F": "7 0 R", + "/UF": "7 0 R" + }, + "/F": "u:a.txt", + "/Type": "/Filespec", + "/UF": "u:a.txt" + } + }, + "obj:5 0 R": { + "value": { + "/Desc": "u:Two filenames", + "/EF": { + "/F": "9 0 R", + "/UF": "9 0 R" + }, + "/F": "u:pi.txt", + "/Type": "/Filespec", + "/UF": "u:Ï€.txt" + } + }, + "obj:6 0 R": { + "value": { + "/Contents": "11 0 R", + "/MediaBox": [ + 0, + 0, + 612, + 792 + ], + "/Parent": "3 0 R", + "/Resources": { + "/Font": { + "/F1": "13 0 R" + }, + "/ProcSet": "14 0 R" + }, + "/Type": "/Page" + } + }, + "obj:7 0 R": { + "stream": { + "dict": { + "/Length": "8 0 R", + "/Params": { + "/Size": 7 + }, + "/Type": "/EmbeddedFile" + } + } + }, + "obj:8 0 R": { + "value": 7 + }, + "obj:9 0 R": { + "stream": { + "dict": { + "/Length": "10 0 R", + "/Params": { + "/CheckSum": "b:e561f9248d7563d15dd93457b02ebbb6", + "/CreationDate": "u:D:20220530094116-05'00'", + "/ModDate": "u:D:20220530094116-05'00'", + "/Size": 7 + }, + "/Subtype": "/text/plain", + "/Type": "/EmbeddedFile" + } + } + }, + "obj:10 0 R": { + "value": 7 + }, + "obj:11 0 R": { + "stream": { + "dict": { + "/Length": "12 0 R" + } + } + }, + "obj:12 0 R": { + "value": 44 + }, + "obj:13 0 R": { + "value": { + "/BaseFont": "/Helvetica", + "/Encoding": "/WinAnsiEncoding", + "/Subtype": "/Type1", + "/Type": "/Font" + } + }, + "obj:14 0 R": { + "value": [ + "/PDF", + "/Text" + ] + }, + "trailer": { + "value": { + "/ID": [ + "b:3d1a89e08f9d6b3e1f787d04fceed440", + "b:4e13de38230bb10a03e49285a742c392" + ], + "/Root": "1 0 R", + "/Size": 15 + } + } + } +} diff --git a/qpdf/qtest/qpdf/list-attachments-1.out b/qpdf/qtest/qpdf/list-attachments-1.out index 6e94cc64..5fac5ea2 100644 --- a/qpdf/qtest/qpdf/list-attachments-1.out +++ b/qpdf/qtest/qpdf/list-attachments-1.out @@ -5,7 +5,15 @@ auto-1 -> 8,0 /UF -> auto-1 all data streams: /F -> 8,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: text/plain + checksum: a857d18d3fc23ad412122ef040733331 /UF -> 8,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: text/plain + checksum: a857d18d3fc23ad412122ef040733331 auto-3 -> 10,0 description: two words preferred name: auto-Three.txt @@ -14,7 +22,15 @@ auto-3 -> 10,0 /UF -> auto-Three.txt all data streams: /F -> 10,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab /UF -> 10,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab auto-Two -> 12,0 preferred name: auto-2 all names: @@ -22,4 +38,12 @@ auto-Two -> 12,0 /UF -> auto-2 all data streams: /F -> 12,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 /UF -> 12,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 diff --git a/qpdf/qtest/qpdf/list-attachments-2.out b/qpdf/qtest/qpdf/list-attachments-2.out index d5488020..1683aef2 100644 --- a/qpdf/qtest/qpdf/list-attachments-2.out +++ b/qpdf/qtest/qpdf/list-attachments-2.out @@ -5,7 +5,15 @@ /UF -> auto-1 all data streams: /F -> 11,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: text/plain + checksum: a857d18d3fc23ad412122ef040733331 /UF -> 11,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: text/plain + checksum: a857d18d3fc23ad412122ef040733331 1-auto-3 -> 13,0 description: two words preferred name: auto-Three.txt @@ -14,7 +22,15 @@ /UF -> auto-Three.txt all data streams: /F -> 13,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab /UF -> 13,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab 1-auto-Two -> 15,0 preferred name: auto-2 all names: @@ -22,7 +38,15 @@ /UF -> auto-2 all data streams: /F -> 15,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 /UF -> 15,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 auto-1 -> 17,0 preferred name: auto-1 all names: @@ -30,7 +54,15 @@ auto-1 -> 17,0 /UF -> auto-1 all data streams: /F -> 17,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: text/plain + checksum: a857d18d3fc23ad412122ef040733331 /UF -> 17,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: text/plain + checksum: a857d18d3fc23ad412122ef040733331 auto-3 -> 19,0 description: two words preferred name: auto-Three.txt @@ -39,7 +71,15 @@ auto-3 -> 19,0 /UF -> auto-Three.txt all data streams: /F -> 19,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab /UF -> 19,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab auto-Two -> 21,0 preferred name: auto-2 all names: @@ -47,4 +87,12 @@ auto-Two -> 21,0 /UF -> auto-2 all data streams: /F -> 21,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 /UF -> 21,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 diff --git a/qpdf/qtest/qpdf/list-attachments-3.out b/qpdf/qtest/qpdf/list-attachments-3.out index 0467b59d..e48bb4c0 100644 --- a/qpdf/qtest/qpdf/list-attachments-3.out +++ b/qpdf/qtest/qpdf/list-attachments-3.out @@ -5,7 +5,15 @@ auto-1 -> 8,0 /UF -> auto-2 all data streams: /F -> 8,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 /UF -> 8,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 auto-3 -> 10,0 description: two words preferred name: auto-Three.txt @@ -14,7 +22,15 @@ auto-3 -> 10,0 /UF -> auto-Three.txt all data streams: /F -> 10,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab /UF -> 10,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: d6c7ac7cf295ae133fea186cfd068dab auto-Two -> 12,0 preferred name: auto-2 all names: @@ -22,4 +38,12 @@ auto-Two -> 12,0 /UF -> auto-2 all data streams: /F -> 12,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 /UF -> 12,0 + creation date: D:20210210091359-05'00' + modification date: D:20210210141359Z + mime type: + checksum: 9f991a5669c47a94f9350f53e3953e57 diff --git a/qpdf/qtest/qpdf/list-attachments-4.out b/qpdf/qtest/qpdf/list-attachments-4.out index b2d59e08..304b1e3d 100644 --- a/qpdf/qtest/qpdf/list-attachments-4.out +++ b/qpdf/qtest/qpdf/list-attachments-4.out @@ -5,4 +5,12 @@ auto-1 -> 6,0 /UF -> auto-1 all data streams: /F -> 6,0 + creation date: + modification date: + mime type: + checksum: a857d18d3fc23ad412122ef040733331 /UF -> 6,0 + creation date: + modification date: + mime type: + checksum: a857d18d3fc23ad412122ef040733331 diff --git a/qpdf/qtest/qpdf/test76-json.out b/qpdf/qtest/qpdf/test76-json.out index 02f0317c..df326e83 100644 --- a/qpdf/qtest/qpdf/test76-json.out +++ b/qpdf/qtest/qpdf/test76-json.out @@ -5,19 +5,76 @@ }, "attachments": { "att1": { + "description": "some text", "filespec": "4 0 R", + "names": { + "/F": "att1.txt", + "/UF": "att1.txt" + }, "preferredcontents": "8 0 R", - "preferredname": "att1.txt" + "preferredname": "att1.txt", + "streams": { + "/F": { + "checksum": "2e10f186a4cdf5be438747f4bdc2d4d4", + "creationdate": "2021-02-07T19:11:21-05:00", + "mimetype": "text/plain", + "modificationdate": "2021-02-07T19:11:21-05:00" + }, + "/UF": { + "checksum": "2e10f186a4cdf5be438747f4bdc2d4d4", + "creationdate": "2021-02-07T19:11:21-05:00", + "mimetype": "text/plain", + "modificationdate": "2021-02-07T19:11:21-05:00" + } + } }, "att2": { + "description": null, "filespec": "5 0 R", + "names": { + "/F": "att2.txt", + "/UF": "att2.txt" + }, "preferredcontents": "10 0 R", - "preferredname": "att2.txt" + "preferredname": "att2.txt", + "streams": { + "/F": { + "checksum": "2fce9c8228e360ba9b04a1bd1bf63d6b", + "creationdate": null, + "mimetype": "text/plain", + "modificationdate": null + }, + "/UF": { + "checksum": "2fce9c8228e360ba9b04a1bd1bf63d6b", + "creationdate": null, + "mimetype": "text/plain", + "modificationdate": null + } + } }, "att3": { + "description": null, "filespec": "6 0 R", + "names": { + "/F": "att3.txt", + "/UF": "Ï€.txt" + }, "preferredcontents": "12 0 R", - "preferredname": "Ï€.txt" + "preferredname": "Ï€.txt", + "streams": { + "/F": { + "checksum": "2236c155b1d62b7f00285bba081d4336", + "creationdate": null, + "mimetype": "text/plain", + "modificationdate": null + }, + "/UF": { + "checksum": "2236c155b1d62b7f00285bba081d4336", + "creationdate": null, + "mimetype": "text/plain", + "modificationdate": null + } + } } } } diff --git a/qpdf/qtest/qpdf/test76-list-verbose.out b/qpdf/qtest/qpdf/test76-list-verbose.out index 5e6df1a2..c64e1f02 100644 --- a/qpdf/qtest/qpdf/test76-list-verbose.out +++ b/qpdf/qtest/qpdf/test76-list-verbose.out @@ -6,7 +6,15 @@ att1 -> 8,0 /UF -> att1.txt all data streams: /F -> 8,0 + creation date: D:20210207191121-05'00' + modification date: D:20210208001122Z + mime type: text/plain + checksum: 2e10f186a4cdf5be438747f4bdc2d4d4 /UF -> 8,0 + creation date: D:20210207191121-05'00' + modification date: D:20210208001122Z + mime type: text/plain + checksum: 2e10f186a4cdf5be438747f4bdc2d4d4 att2 -> 10,0 preferred name: att2.txt all names: @@ -14,7 +22,15 @@ att2 -> 10,0 /UF -> att2.txt all data streams: /F -> 10,0 + creation date: + modification date: + mime type: text/plain + checksum: 2fce9c8228e360ba9b04a1bd1bf63d6b /UF -> 10,0 + creation date: + modification date: + mime type: text/plain + checksum: 2fce9c8228e360ba9b04a1bd1bf63d6b att3 -> 12,0 preferred name: Ï€.txt all names: @@ -22,4 +38,12 @@ att3 -> 12,0 /UF -> Ï€.txt all data streams: /F -> 12,0 + creation date: + modification date: + mime type: text/plain + checksum: 2236c155b1d62b7f00285bba081d4336 /UF -> 12,0 + creation date: + modification date: + mime type: text/plain + checksum: 2236c155b1d62b7f00285bba081d4336 diff --git a/qpdf/qtest/qpdf/utf16le-attachments.out b/qpdf/qtest/qpdf/utf16le-attachments.out index 74abc20b..f4a409e1 100644 --- a/qpdf/qtest/qpdf/utf16le-attachments.out +++ b/qpdf/qtest/qpdf/utf16le-attachments.out @@ -5,4 +5,12 @@ potato.png -> 6,0 /UF -> Ï€.png all data streams: /F -> 6,0 + creation date: D:20220215153939-05'00' + modification date: D:20220215153939-05'00' + mime type: + checksum: c55e70c0c72d7eaf01230124fe5ff2d9 /UF -> 6,0 + creation date: D:20220215153939-05'00' + modification date: D:20220215153939-05'00' + mime type: + checksum: c55e70c0c72d7eaf01230124fe5ff2d9 diff --git a/qtest/bin/qtest-driver b/qtest/bin/qtest-driver index ce08f29f..c37c4d1d 100755 --- a/qtest/bin/qtest-driver +++ b/qtest/bin/qtest-driver @@ -218,7 +218,7 @@ if (defined $tc_log) print_xml(">\n"); print_junit("\n" . "\n"); -my @invalid_test_suites = (); +my @failed_test_suites = (); foreach my $test (@tests) { print_and_log("\nRunning $test\n"); @@ -228,7 +228,7 @@ foreach my $test (@tests) if (scalar(@results) != 5) { print_and_log("test driver $test returned invalid results\n"); - push(@invalid_test_suites, $test); + push(@failed_test_suites, $test); } else { @@ -263,6 +263,10 @@ foreach my $test (@tests) my $passed = (($extra == 0) && ($missing == 0) && ($fails == 0) && ($xpasses == 0)); + if (! $passed) + { + push(@failed_test_suites, $test); + } print_xml("