From d06509808957305ed01497568ddd81a2590362f8 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 20 May 2022 11:10:12 -0400 Subject: Test --update-from-json --- TODO | 35 ++-- include/qpdf/QPDF.hh | 1 + libqpdf/QPDF_json.cc | 55 ++++--- qpdf/qpdf.testcov | 3 + qpdf/qtest/qpdf-json.test | 30 ++++ qpdf/qtest/qpdf/qjson-obj-key-errors.out | 4 +- qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out | 2 +- qpdf/qtest/qpdf/qpdf-json-update-errors.json | 20 +++ qpdf/qtest/qpdf/replace-with-stream-updated.pdf | 182 +++++++++++++++++++++ qpdf/qtest/qpdf/replace-with-stream.json | 16 ++ qpdf/qtest/qpdf/update-from-json-errors.out | 4 + qpdf/qtest/qpdf/update-stream-data-updated.pdf | 171 +++++++++++++++++++ qpdf/qtest/qpdf/update-stream-data.json | 20 +++ .../qtest/qpdf/update-stream-dict-only-updated.pdf | 172 +++++++++++++++++++ qpdf/qtest/qpdf/update-stream-dict-only.json | 15 ++ qpdf/qtest/qpdf/various-updates-updated.pdf | 128 +++++++++++++++ qpdf/qtest/qpdf/various-updates.json | 39 +++++ 17 files changed, 849 insertions(+), 48 deletions(-) create mode 100644 qpdf/qtest/qpdf/qpdf-json-update-errors.json create mode 100644 qpdf/qtest/qpdf/replace-with-stream-updated.pdf create mode 100644 qpdf/qtest/qpdf/replace-with-stream.json create mode 100644 qpdf/qtest/qpdf/update-from-json-errors.out create mode 100644 qpdf/qtest/qpdf/update-stream-data-updated.pdf create mode 100644 qpdf/qtest/qpdf/update-stream-data.json create mode 100644 qpdf/qtest/qpdf/update-stream-dict-only-updated.pdf create mode 100644 qpdf/qtest/qpdf/update-stream-dict-only.json create mode 100644 qpdf/qtest/qpdf/various-updates-updated.pdf create mode 100644 qpdf/qtest/qpdf/various-updates.json diff --git a/TODO b/TODO index 8d96d31e..ea6cf42f 100644 --- a/TODO +++ b/TODO @@ -58,15 +58,10 @@ Some of this documentation has drifted from the actual implementation. Make sure pages tree repair generates warnings. -* Reread from perspective of update -* Test all ignore cases with QTC -* Test case of correct file with dict before data/datafile * Have a test case if possible that exercises the object description which means we need some kind of semantic error that gets caught after creation. * Test invalid data, invalid data file -* Tests: round-trip through json, round-trip through qpdf --qdf -* Test to see if we get CR/NL on Windows, which is okay Try to never flatten pages tree. Make sure we do something reasonable with pages tree repair. The problem is that if pages tree repair is @@ -104,22 +99,20 @@ JSON to PDF: Have --json-input and --update-from-json. With --json-input, the json file must be complete, meaning all stream data, the trailer, and the -PDF version must be present. In --update-from-json, an object -explicitly set to null (not "value": null) is deleted. For streams -with no stream data, the dictionary is updated but the data is left -untouched. Other things that are omitted are left alone. Make sure -document that, when writing a PDF file from QPDF, there is no -expectation of object numbers being preserved. As such, ---update-from-json can only be used to update the exact file that the -json was created from. You can put multiple objects in the update -file, but you can't use a json from one file to update the output of a -previous update since the object numbers will have changed. Note that, -when creating from a JSON, object numbers are preserved in the -resulting QPDF object but still modified by QPDFWriter for the output. -This would be visible by combining --json-output and --json-input. -Also using --qdf with --create-from-json would show original object -IDs in comments. It will be important to capture this in the -documentation. +PDF version must be present. For streams with no stream data, the +dictionary is updated but the data is left untouched. Other things +that are omitted are left alone. Make sure document that, when writing +a PDF file from QPDF, there is no expectation of object numbers being +preserved. As such, --update-from-json can only be used to update the +exact file that the json was created from. You can put multiple +objects in the update file, but you can't use a json from one file to +update the output of a previous update since the object numbers will +have changed. Note that, when creating from a JSON, object numbers are +preserved in the resulting QPDF object but still modified by +QPDFWriter for the output. This would be visible by combining +--json-output and --json-input. Also using --qdf with +--create-from-json would show original object IDs in comments. It will +be important to capture this in the documentation. When reading a JSON string, any string that doesn't look like a name or indirect object or start with "b:" or "u:" should be considered an diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index fc515898..7d7af225 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1065,6 +1065,7 @@ class QPDF bool saw_dict; bool saw_data; bool saw_datafile; + bool this_stream_needs_data; std::vector state_stack; std::vector object_stack; std::set reserved; diff --git a/libqpdf/QPDF_json.cc b/libqpdf/QPDF_json.cc index 18631114..af12459a 100644 --- a/libqpdf/QPDF_json.cc +++ b/libqpdf/QPDF_json.cc @@ -102,7 +102,8 @@ QPDF::JSONReactor::JSONReactor( saw_stream(false), saw_dict(false), saw_data(false), - saw_datafile(false) + saw_datafile(false), + this_stream_needs_data(false) { state_stack.push_back(st_initial); } @@ -111,8 +112,11 @@ void QPDF::JSONReactor::error(size_t offset, std::string const& msg) { this->errors = true; - this->pdf.warn( - qpdf_e_json, this->cur_object, QIntC::to_offset(offset), msg); + std::string object = this->cur_object; + if (is->getName() != pdf.getFilename()) { + object += " from " + is->getName(); + } + this->pdf.warn(qpdf_e_json, object, QIntC::to_offset(offset), msg); } bool @@ -196,20 +200,22 @@ QPDF::JSONReactor::containerEnd(JSON const& value) QTC::TC("qpdf", "QPDF_json stream no dict"); error(value.getStart(), "\"stream\" is missing \"dict\""); } - if (must_be_complete) { - if (saw_data == saw_datafile) { + if (saw_data == saw_datafile) { + if (this_stream_needs_data) { QTC::TC("qpdf", "QPDF_json data datafile both or neither"); error( value.getStart(), - "\"stream\" must have exactly one of \"data\" or " + "new \"stream\" must have exactly one of \"data\" or " + "\"datafile\""); + } else if (saw_datafile) { + QTC::TC("qpdf", "QPDF_json data and datafile"); + error( + value.getStart(), + "existing \"stream\" may at most one of \"data\" or " "\"datafile\""); + } else { + QTC::TC("qpdf", "QPDF_json no stream data in update mode"); } - } else if (saw_data && saw_datafile) { - // QXXXQ - /// QTC::TC("qpdf", "QPDF_json data and datafile"); - error( - value.getStart(), - "\"stream\" may at most one of \"data\" or \"datafile\""); } } } else if ((state == st_stream) || (state == st_object)) { @@ -320,7 +326,6 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) nestedState(key, value, st_trailer); this->cur_object = "trailer"; } else if (std::regex_match(key, m, OBJ_KEY_RE)) { - // QXXXQ remember to handle null for delete object_stack.push_back(reserveObject(m[1].str(), m[2].str())); nestedState(key, value, st_object_top); this->cur_object = key; @@ -347,9 +352,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) } else if (key == "stream") { this->saw_stream = true; nestedState(key, value, st_stream); + this->this_stream_needs_data = false; if (tos.isStream()) { - // QXXXQ reusing -- need QTC + QTC::TC("qpdf", "QPDF_json updating existing stream"); } else { + this->this_stream_needs_data = true; replacement = pdf.reserveStream(tos.getObjectID(), tos.getGeneration()); replaceObject(tos, replacement); @@ -386,12 +393,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value) throw std::logic_error("no object on stack in st_stream"); } auto tos = object_stack.back(); - auto uninitialized = QPDFObjectHandle(); if (!tos.isStream()) { - // QXXXQ QTC in update mode - error(value.getStart(), "this object is not a stream"); - parse_error = true; - } else if (key == "dict") { + throw std::logic_error("top of stack is not stream in st_stream"); + } + auto uninitialized = QPDFObjectHandle(); + if (key == "dict") { this->saw_dict = true; // Since a stream dictionary must be a dictionary, we can // use nestedState to transition to st_value. @@ -509,11 +515,12 @@ QPDF::JSONReactor::makeObject(JSON const& value) "JSONReactor::makeObject didn't initialize the object"); } - // QXXXQ include object number in description - result.setObjectDescription( - &this->pdf, - this->is->getName() + " offset " + - QUtil::uint_to_string(value.getStart())); + std::string description = this->is->getName(); + if (!this->cur_object.empty()) { + description += " " + this->cur_object + ","; + } + description += " offset " + QUtil::uint_to_string(value.getStart()); + result.setObjectDescription(&this->pdf, description); return result; } diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 65aa421a..93c658bb 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -676,3 +676,6 @@ QPDF_json ignore unknown key in object_top 0 QPDF_json ignore unknown key in trailer 0 QPDF_json ignore unknown key in stream 0 QPDF_json non-trivial null reserved 0 +QPDF_json data and datafile 0 +QPDF_json no stream data in update mode 0 +QPDF_json updating existing stream 0 diff --git a/qpdf/qtest/qpdf-json.test b/qpdf/qtest/qpdf-json.test index 9e92e27d..dda056a1 100644 --- a/qpdf/qtest/qpdf-json.test +++ b/qpdf/qtest/qpdf-json.test @@ -171,5 +171,35 @@ $td->runtest("check PDF", {$td->FILE => "a.pdf"}, {$td->FILE => "b.pdf"}); +# Replace mode tests + +$n_tests += 1; +$td->runtest("create PDF for replace", + {$td->COMMAND => "qpdf good13.pdf a.pdf" . + " --update-from-json=qpdf-json-update-errors.json"}, + {$td->FILE => "update-from-json-errors.out", + $td->EXIT_STATUS => 2}, + $td->NORMALIZE_NEWLINES); + +my @update_files = ( + "update-stream-dict-only", + "update-stream-data", + "replace-with-stream", + "various-updates", + ); +$n_tests += 2 * scalar(@update_files); + +foreach my $f (@update_files) { + $td->runtest("update: $f", + {$td->COMMAND => + "qpdf good13.pdf a.pdf --qdf --static-id" . + " --update-from-json=$f.json"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + $td->runtest("$f: check updated", + {$td->FILE => "a.pdf"}, + {$td->FILE => "$f-updated.pdf"}); +} + + cleanup(); $td->report($n_tests); diff --git a/qpdf/qtest/qpdf/qjson-obj-key-errors.out b/qpdf/qtest/qpdf/qjson-obj-key-errors.out index 752c5321..e739a2f8 100644 --- a/qpdf/qtest/qpdf/qjson-obj-key-errors.out +++ b/qpdf/qtest/qpdf/qjson-obj-key-errors.out @@ -1,7 +1,7 @@ WARNING: qjson-obj-key-errors.json (obj:2 0 R, offset 218): object must have exactly one of "value" or "stream" WARNING: qjson-obj-key-errors.json (obj:3 0 R, offset 516): object must have exactly one of "value" or "stream" WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): "stream" is missing "dict" -WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): "stream" must have exactly one of "data" or "datafile" -WARNING: qjson-obj-key-errors.json (obj:5 0 R, offset 774): "stream" must have exactly one of "data" or "datafile" +WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): new "stream" must have exactly one of "data" or "datafile" +WARNING: qjson-obj-key-errors.json (obj:5 0 R, offset 774): new "stream" must have exactly one of "data" or "datafile" WARNING: qjson-obj-key-errors.json (trailer, offset 1152): "trailer" is missing "value" qpdf: qjson-obj-key-errors.json: errors found in JSON diff --git a/qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out b/qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out index 2a2c8de9..39b5207d 100644 --- a/qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out +++ b/qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out @@ -1,5 +1,5 @@ WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): "stream.dict" must be a dictionary WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): unrecognized string value -WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 98): "stream" must have exactly one of "data" or "datafile" +WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 98): new "stream" must have exactly one of "data" or "datafile" WARNING: qjson-stream-dict-not-dict.json: "qpdf-v2.objects.trailer" was not seen qpdf: qjson-stream-dict-not-dict.json: errors found in JSON diff --git a/qpdf/qtest/qpdf/qpdf-json-update-errors.json b/qpdf/qtest/qpdf/qpdf-json-update-errors.json new file mode 100644 index 00000000..4f699342 --- /dev/null +++ b/qpdf/qtest/qpdf/qpdf-json-update-errors.json @@ -0,0 +1,20 @@ +{ + "qpdf-v2": { + "objects": { + "obj:4 0 R": { + "stream": { + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=", + "datafile": "file-too", + "dict": {} + } + }, + "obj:20 0 R": { + "stream": { + "dict": { + "/Bad": "string-value" + } + } + } + } + } +} diff --git a/qpdf/qtest/qpdf/replace-with-stream-updated.pdf b/qpdf/qtest/qpdf/replace-with-stream-updated.pdf new file mode 100644 index 00000000..d8dc76a3 --- /dev/null +++ b/qpdf/qtest/qpdf/replace-with-stream-updated.pdf @@ -0,0 +1,182 @@ +%PDF-2.0 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 3 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 7 0 +2 0 obj +<< + /dangling-ref-for-json-test [ + 4 0 R + ] + /hex#20strings [ + (Potato) + <01020300040560> + (AB) + ] + /indirect 5 0 R + /names [ + /nesting + /hex#20strings + /text#2fplain + ] + /nesting << + /a [ + 1 + 2 + << + /x (y) + >> + [ + (z) + ] + ] + /b << + / (legal) + /a [ + 1 + 2 + ] + >> + >> + /strings [ + (one) + <24a2> + () + (\(\)) + (\() + (\)) + (a\f\b\t\r\nb) + (") + ("") + ("\("\)") + <410042> + (a\nb) + (a b) + + + ] +>> +endobj + +%% Original object ID: 2 0 +3 0 obj +<< + /Count 1 + /Kids [ + 7 0 R + ] + /Type /Pages +>> +endobj + +%% Original object ID: 9 0 +4 0 obj +null +endobj + +%% Original object ID: 8 0 +5 0 obj +<< + /K /V + /Length 6 0 R +>> +stream +new-stream-here +endstream +endobj + +6 0 obj +16 +endobj + +%% Page 1 +%% Original object ID: 3 0 +7 0 obj +<< + /Contents 8 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 3 0 R + /Resources << + /Font << + /F1 10 0 R + >> + /ProcSet 11 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +8 0 obj +<< + /Length 9 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +9 0 obj +44 +endobj + +%% Original object ID: 6 0 +10 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 5 0 +11 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 12 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000756 00000 n +0000000855 00000 n +0000000903 00000 n +0000000982 00000 n +0000001038 00000 n +0000001282 00000 n +0000001381 00000 n +0000001427 00000 n +0000001573 00000 n +trailer << + /QTest 2 0 R + /Root 1 0 R + /Size 12 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +1609 +%%EOF diff --git a/qpdf/qtest/qpdf/replace-with-stream.json b/qpdf/qtest/qpdf/replace-with-stream.json new file mode 100644 index 00000000..7004c2a1 --- /dev/null +++ b/qpdf/qtest/qpdf/replace-with-stream.json @@ -0,0 +1,16 @@ +{ + "qpdf-v2": { + "pdfversion": "2.0", + "maxobjectid": 9, + "objects": { + "obj:8 0 R": { + "stream": { + "data": "bmV3LXN0cmVhbS1oZXJlCg==", + "dict": { + "/K": "/V" + } + } + } + } + } +} diff --git a/qpdf/qtest/qpdf/update-from-json-errors.out b/qpdf/qtest/qpdf/update-from-json-errors.out new file mode 100644 index 00000000..763579c3 --- /dev/null +++ b/qpdf/qtest/qpdf/update-from-json-errors.out @@ -0,0 +1,4 @@ +WARNING: good13.pdf (obj:4 0 R from qpdf-json-update-errors.json, offset 73): existing "stream" may at most one of "data" or "datafile" +WARNING: good13.pdf (obj:20 0 R from qpdf-json-update-errors.json, offset 313): unrecognized string value +WARNING: good13.pdf (obj:20 0 R from qpdf-json-update-errors.json, offset 271): new "stream" must have exactly one of "data" or "datafile" +qpdf: qpdf-json-update-errors.json: errors found in JSON diff --git a/qpdf/qtest/qpdf/update-stream-data-updated.pdf b/qpdf/qtest/qpdf/update-stream-data-updated.pdf new file mode 100644 index 00000000..3f9ae74d --- /dev/null +++ b/qpdf/qtest/qpdf/update-stream-data-updated.pdf @@ -0,0 +1,171 @@ +%PDF-2.0 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 3 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 7 0 +2 0 obj +<< + /dangling-ref-for-json-test [ + 4 0 R + ] + /hex#20strings [ + (Potato) + <01020300040560> + (AB) + ] + /indirect 5 0 R + /names [ + /nesting + /hex#20strings + /text#2fplain + ] + /nesting << + /a [ + 1 + 2 + << + /x (y) + >> + [ + (z) + ] + ] + /b << + / (legal) + /a [ + 1 + 2 + ] + >> + >> + /strings [ + (one) + <24a2> + () + (\(\)) + (\() + (\)) + (a\f\b\t\r\nb) + (") + ("") + ("\("\)") + <410042> + (a\nb) + (a b) + + + ] +>> +endobj + +%% Original object ID: 2 0 +3 0 obj +<< + /Count 1 + /Kids [ + 6 0 R + ] + /Type /Pages +>> +endobj + +%% Original object ID: 9 0 +4 0 obj +null +endobj + +%% Original object ID: 8 0 +5 0 obj +(hello) +endobj + +%% Page 1 +%% Original object ID: 3 0 +6 0 obj +<< + /Contents 7 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 3 0 R + /Resources << + /Font << + /F1 9 0 R + >> + /ProcSet 10 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +7 0 obj +<< + /Length 8 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Salad) Tj +ET +endstream +endobj + +8 0 obj +43 +endobj + +%% Original object ID: 6 0 +9 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 5 0 +10 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 11 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000756 00000 n +0000000855 00000 n +0000000903 00000 n +0000000964 00000 n +0000001207 00000 n +0000001305 00000 n +0000001351 00000 n +0000001496 00000 n +trailer << + /QTest 2 0 R + /Root 1 0 R + /Size 11 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +1532 +%%EOF diff --git a/qpdf/qtest/qpdf/update-stream-data.json b/qpdf/qtest/qpdf/update-stream-data.json new file mode 100644 index 00000000..a650e93f --- /dev/null +++ b/qpdf/qtest/qpdf/update-stream-data.json @@ -0,0 +1,20 @@ +{ + "qpdf-v2": { + "pdfversion": "2.0", + "maxobjectid": 9, + "objects": { + "obj:1 0 R": { + "value": { + "/Pages": "2 0 R", + "/Type": "/Catalog" + } + }, + "obj:4 0 R": { + "stream": { + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoU2FsYWQpIFRqCkVUCg==", + "dict": {} + } + } + } + } +} diff --git a/qpdf/qtest/qpdf/update-stream-dict-only-updated.pdf b/qpdf/qtest/qpdf/update-stream-dict-only-updated.pdf new file mode 100644 index 00000000..f2bd079f --- /dev/null +++ b/qpdf/qtest/qpdf/update-stream-dict-only-updated.pdf @@ -0,0 +1,172 @@ +%PDF-2.0 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 3 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 7 0 +2 0 obj +<< + /dangling-ref-for-json-test [ + 4 0 R + ] + /hex#20strings [ + (Potato) + <01020300040560> + (AB) + ] + /indirect 5 0 R + /names [ + /nesting + /hex#20strings + /text#2fplain + ] + /nesting << + /a [ + 1 + 2 + << + /x (y) + >> + [ + (z) + ] + ] + /b << + / (legal) + /a [ + 1 + 2 + ] + >> + >> + /strings [ + (one) + <24a2> + () + (\(\)) + (\() + (\)) + (a\f\b\t\r\nb) + (") + ("") + ("\("\)") + <410042> + (a\nb) + (a b) + + + ] +>> +endobj + +%% Original object ID: 2 0 +3 0 obj +<< + /Count 1 + /Kids [ + 6 0 R + ] + /Type /Pages +>> +endobj + +%% Original object ID: 9 0 +4 0 obj +null +endobj + +%% Original object ID: 8 0 +5 0 obj +(hello) +endobj + +%% Page 1 +%% Original object ID: 3 0 +6 0 obj +<< + /Contents 7 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 3 0 R + /Resources << + /Font << + /F1 9 0 R + >> + /ProcSet 10 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +7 0 obj +<< + /Potato (salad) + /Length 8 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +8 0 obj +44 +endobj + +%% Original object ID: 6 0 +9 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 5 0 +10 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 11 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000756 00000 n +0000000855 00000 n +0000000903 00000 n +0000000964 00000 n +0000001207 00000 n +0000001324 00000 n +0000001370 00000 n +0000001515 00000 n +trailer << + /QTest 2 0 R + /Root 1 0 R + /Size 11 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +1551 +%%EOF diff --git a/qpdf/qtest/qpdf/update-stream-dict-only.json b/qpdf/qtest/qpdf/update-stream-dict-only.json new file mode 100644 index 00000000..bc0a17ce --- /dev/null +++ b/qpdf/qtest/qpdf/update-stream-dict-only.json @@ -0,0 +1,15 @@ +{ + "qpdf-v2": { + "pdfversion": "2.0", + "maxobjectid": 9, + "objects": { + "obj:4 0 R": { + "stream": { + "dict": { + "/Potato": "u:salad" + } + } + } + } + } +} diff --git a/qpdf/qtest/qpdf/various-updates-updated.pdf b/qpdf/qtest/qpdf/various-updates-updated.pdf new file mode 100644 index 00000000..93ecb403 --- /dev/null +++ b/qpdf/qtest/qpdf/various-updates-updated.pdf @@ -0,0 +1,128 @@ +%PDF-2.0 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Pages 3 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 7 0 +2 0 obj +<< + /indirect 4 0 R + /now-#cf#80 [ + 5 0 R + ] + /strings [ + (one) + + ] +>> +endobj + +%% Original object ID: 2 0 +3 0 obj +<< + /Count 1 + /Kids [ + 6 0 R + ] + /Type /Pages +>> +endobj + +%% Original object ID: 8 0 +4 0 obj +[ + (hello) + 7 0 R +] +endobj + +%% Original object ID: 9 0 +5 0 obj +3.14159 +endobj + +%% Page 1 +%% Original object ID: 3 0 +6 0 obj +<< + /Contents 8 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 3 0 R + /Resources << + /Font << + /F1 10 0 R + >> + >> + /Type /Page +>> +endobj + +%% Original object ID: 10 0 +7 0 obj +(this is new) +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +8 0 obj +<< + /Length 9 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +9 0 obj +44 +endobj + +%% Original object ID: 6 0 +10 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +xref +0 11 +0000000000 65535 f +0000000052 00000 n +0000000133 00000 n +0000000272 00000 n +0000000371 00000 n +0000000436 00000 n +0000000497 00000 n +0000000699 00000 n +0000000779 00000 n +0000000878 00000 n +0000000924 00000 n +trailer << + /QTest 2 0 R + /Root 1 0 R + /Size 11 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +1043 +%%EOF diff --git a/qpdf/qtest/qpdf/various-updates.json b/qpdf/qtest/qpdf/various-updates.json new file mode 100644 index 00000000..80e36465 --- /dev/null +++ b/qpdf/qtest/qpdf/various-updates.json @@ -0,0 +1,39 @@ +{ + "qpdf-v2": { + "pdfversion": "2.0", + "maxobjectid": 9, + "objects": { + "obj:5 0 R": { + "value": null + }, + "obj:7 0 R": { + "value": { + "/now-Ï€": [ + "9 0 R" + ], + "/indirect": "8 0 R", + "/strings": [ + "u:one", + "u:Ï€" + ] + } + }, + "obj:8 0 R": { + "value": ["u:hello", "10 0 R"] + }, + "obj:9 0 R": { + "value": 3.14159 + }, + "obj:10 0 R": { + "value": "u:this is new" + }, + "trailer": { + "value": { + "/QTest": "7 0 R", + "/Root": "1 0 R", + "/Size": 9 + } + } + } + } +} -- cgit v1.2.3-70-g09d2