Commit d06509808957305ed01497568ddd81a2590362f8

Authored by Jay Berkenbilt
1 parent ef955b04

Test --update-from-json

@@ -58,15 +58,10 @@ Some of this documentation has drifted from the actual implementation. @@ -58,15 +58,10 @@ Some of this documentation has drifted from the actual implementation.
58 58
59 Make sure pages tree repair generates warnings. 59 Make sure pages tree repair generates warnings.
60 60
61 -* Reread from perspective of update  
62 -* Test all ignore cases with QTC  
63 -* Test case of correct file with dict before data/datafile  
64 * Have a test case if possible that exercises the object description 61 * Have a test case if possible that exercises the object description
65 which means we need some kind of semantic error that gets caught 62 which means we need some kind of semantic error that gets caught
66 after creation. 63 after creation.
67 * Test invalid data, invalid data file 64 * Test invalid data, invalid data file
68 -* Tests: round-trip through json, round-trip through qpdf --qdf  
69 -* Test to see if we get CR/NL on Windows, which is okay  
70 65
71 Try to never flatten pages tree. Make sure we do something reasonable 66 Try to never flatten pages tree. Make sure we do something reasonable
72 with pages tree repair. The problem is that if pages tree repair is 67 with pages tree repair. The problem is that if pages tree repair is
@@ -104,22 +99,20 @@ JSON to PDF: @@ -104,22 +99,20 @@ JSON to PDF:
104 99
105 Have --json-input and --update-from-json. With --json-input, the json 100 Have --json-input and --update-from-json. With --json-input, the json
106 file must be complete, meaning all stream data, the trailer, and the 101 file must be complete, meaning all stream data, the trailer, and the
107 -PDF version must be present. In --update-from-json, an object  
108 -explicitly set to null (not "value": null) is deleted. For streams  
109 -with no stream data, the dictionary is updated but the data is left  
110 -untouched. Other things that are omitted are left alone. Make sure  
111 -document that, when writing a PDF file from QPDF, there is no  
112 -expectation of object numbers being preserved. As such,  
113 ---update-from-json can only be used to update the exact file that the  
114 -json was created from. You can put multiple objects in the update  
115 -file, but you can't use a json from one file to update the output of a  
116 -previous update since the object numbers will have changed. Note that,  
117 -when creating from a JSON, object numbers are preserved in the  
118 -resulting QPDF object but still modified by QPDFWriter for the output.  
119 -This would be visible by combining --json-output and --json-input.  
120 -Also using --qdf with --create-from-json would show original object  
121 -IDs in comments. It will be important to capture this in the  
122 -documentation. 102 +PDF version must be present. For streams with no stream data, the
  103 +dictionary is updated but the data is left untouched. Other things
  104 +that are omitted are left alone. Make sure document that, when writing
  105 +a PDF file from QPDF, there is no expectation of object numbers being
  106 +preserved. As such, --update-from-json can only be used to update the
  107 +exact file that the json was created from. You can put multiple
  108 +objects in the update file, but you can't use a json from one file to
  109 +update the output of a previous update since the object numbers will
  110 +have changed. Note that, when creating from a JSON, object numbers are
  111 +preserved in the resulting QPDF object but still modified by
  112 +QPDFWriter for the output. This would be visible by combining
  113 +--json-output and --json-input. Also using --qdf with
  114 +--create-from-json would show original object IDs in comments. It will
  115 +be important to capture this in the documentation.
123 116
124 When reading a JSON string, any string that doesn't look like a name 117 When reading a JSON string, any string that doesn't look like a name
125 or indirect object or start with "b:" or "u:" should be considered an 118 or indirect object or start with "b:" or "u:" should be considered an
include/qpdf/QPDF.hh
@@ -1065,6 +1065,7 @@ class QPDF @@ -1065,6 +1065,7 @@ class QPDF
1065 bool saw_dict; 1065 bool saw_dict;
1066 bool saw_data; 1066 bool saw_data;
1067 bool saw_datafile; 1067 bool saw_datafile;
  1068 + bool this_stream_needs_data;
1068 std::vector<state_e> state_stack; 1069 std::vector<state_e> state_stack;
1069 std::vector<QPDFObjectHandle> object_stack; 1070 std::vector<QPDFObjectHandle> object_stack;
1070 std::set<QPDFObjGen> reserved; 1071 std::set<QPDFObjGen> reserved;
libqpdf/QPDF_json.cc
@@ -102,7 +102,8 @@ QPDF::JSONReactor::JSONReactor( @@ -102,7 +102,8 @@ QPDF::JSONReactor::JSONReactor(
102 saw_stream(false), 102 saw_stream(false),
103 saw_dict(false), 103 saw_dict(false),
104 saw_data(false), 104 saw_data(false),
105 - saw_datafile(false) 105 + saw_datafile(false),
  106 + this_stream_needs_data(false)
106 { 107 {
107 state_stack.push_back(st_initial); 108 state_stack.push_back(st_initial);
108 } 109 }
@@ -111,8 +112,11 @@ void @@ -111,8 +112,11 @@ void
111 QPDF::JSONReactor::error(size_t offset, std::string const& msg) 112 QPDF::JSONReactor::error(size_t offset, std::string const& msg)
112 { 113 {
113 this->errors = true; 114 this->errors = true;
114 - this->pdf.warn(  
115 - qpdf_e_json, this->cur_object, QIntC::to_offset(offset), msg); 115 + std::string object = this->cur_object;
  116 + if (is->getName() != pdf.getFilename()) {
  117 + object += " from " + is->getName();
  118 + }
  119 + this->pdf.warn(qpdf_e_json, object, QIntC::to_offset(offset), msg);
116 } 120 }
117 121
118 bool 122 bool
@@ -196,20 +200,22 @@ QPDF::JSONReactor::containerEnd(JSON const&amp; value) @@ -196,20 +200,22 @@ QPDF::JSONReactor::containerEnd(JSON const&amp; value)
196 QTC::TC("qpdf", "QPDF_json stream no dict"); 200 QTC::TC("qpdf", "QPDF_json stream no dict");
197 error(value.getStart(), "\"stream\" is missing \"dict\""); 201 error(value.getStart(), "\"stream\" is missing \"dict\"");
198 } 202 }
199 - if (must_be_complete) {  
200 - if (saw_data == saw_datafile) { 203 + if (saw_data == saw_datafile) {
  204 + if (this_stream_needs_data) {
201 QTC::TC("qpdf", "QPDF_json data datafile both or neither"); 205 QTC::TC("qpdf", "QPDF_json data datafile both or neither");
202 error( 206 error(
203 value.getStart(), 207 value.getStart(),
204 - "\"stream\" must have exactly one of \"data\" or " 208 + "new \"stream\" must have exactly one of \"data\" or "
  209 + "\"datafile\"");
  210 + } else if (saw_datafile) {
  211 + QTC::TC("qpdf", "QPDF_json data and datafile");
  212 + error(
  213 + value.getStart(),
  214 + "existing \"stream\" may at most one of \"data\" or "
205 "\"datafile\""); 215 "\"datafile\"");
  216 + } else {
  217 + QTC::TC("qpdf", "QPDF_json no stream data in update mode");
206 } 218 }
207 - } else if (saw_data && saw_datafile) {  
208 - // QXXXQ  
209 - /// QTC::TC("qpdf", "QPDF_json data and datafile");  
210 - error(  
211 - value.getStart(),  
212 - "\"stream\" may at most one of \"data\" or \"datafile\"");  
213 } 219 }
214 } 220 }
215 } else if ((state == st_stream) || (state == st_object)) { 221 } else if ((state == st_stream) || (state == st_object)) {
@@ -320,7 +326,6 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value) @@ -320,7 +326,6 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
320 nestedState(key, value, st_trailer); 326 nestedState(key, value, st_trailer);
321 this->cur_object = "trailer"; 327 this->cur_object = "trailer";
322 } else if (std::regex_match(key, m, OBJ_KEY_RE)) { 328 } else if (std::regex_match(key, m, OBJ_KEY_RE)) {
323 - // QXXXQ remember to handle null for delete  
324 object_stack.push_back(reserveObject(m[1].str(), m[2].str())); 329 object_stack.push_back(reserveObject(m[1].str(), m[2].str()));
325 nestedState(key, value, st_object_top); 330 nestedState(key, value, st_object_top);
326 this->cur_object = key; 331 this->cur_object = key;
@@ -347,9 +352,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value) @@ -347,9 +352,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
347 } else if (key == "stream") { 352 } else if (key == "stream") {
348 this->saw_stream = true; 353 this->saw_stream = true;
349 nestedState(key, value, st_stream); 354 nestedState(key, value, st_stream);
  355 + this->this_stream_needs_data = false;
350 if (tos.isStream()) { 356 if (tos.isStream()) {
351 - // QXXXQ reusing -- need QTC 357 + QTC::TC("qpdf", "QPDF_json updating existing stream");
352 } else { 358 } else {
  359 + this->this_stream_needs_data = true;
353 replacement = 360 replacement =
354 pdf.reserveStream(tos.getObjectID(), tos.getGeneration()); 361 pdf.reserveStream(tos.getObjectID(), tos.getGeneration());
355 replaceObject(tos, replacement); 362 replaceObject(tos, replacement);
@@ -386,12 +393,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value) @@ -386,12 +393,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
386 throw std::logic_error("no object on stack in st_stream"); 393 throw std::logic_error("no object on stack in st_stream");
387 } 394 }
388 auto tos = object_stack.back(); 395 auto tos = object_stack.back();
389 - auto uninitialized = QPDFObjectHandle();  
390 if (!tos.isStream()) { 396 if (!tos.isStream()) {
391 - // QXXXQ QTC in update mode  
392 - error(value.getStart(), "this object is not a stream");  
393 - parse_error = true;  
394 - } else if (key == "dict") { 397 + throw std::logic_error("top of stack is not stream in st_stream");
  398 + }
  399 + auto uninitialized = QPDFObjectHandle();
  400 + if (key == "dict") {
395 this->saw_dict = true; 401 this->saw_dict = true;
396 // Since a stream dictionary must be a dictionary, we can 402 // Since a stream dictionary must be a dictionary, we can
397 // use nestedState to transition to st_value. 403 // use nestedState to transition to st_value.
@@ -509,11 +515,12 @@ QPDF::JSONReactor::makeObject(JSON const&amp; value) @@ -509,11 +515,12 @@ QPDF::JSONReactor::makeObject(JSON const&amp; value)
509 "JSONReactor::makeObject didn't initialize the object"); 515 "JSONReactor::makeObject didn't initialize the object");
510 } 516 }
511 517
512 - // QXXXQ include object number in description  
513 - result.setObjectDescription(  
514 - &this->pdf,  
515 - this->is->getName() + " offset " +  
516 - QUtil::uint_to_string(value.getStart())); 518 + std::string description = this->is->getName();
  519 + if (!this->cur_object.empty()) {
  520 + description += " " + this->cur_object + ",";
  521 + }
  522 + description += " offset " + QUtil::uint_to_string(value.getStart());
  523 + result.setObjectDescription(&this->pdf, description);
517 return result; 524 return result;
518 } 525 }
519 526
qpdf/qpdf.testcov
@@ -676,3 +676,6 @@ QPDF_json ignore unknown key in object_top 0 @@ -676,3 +676,6 @@ QPDF_json ignore unknown key in object_top 0
676 QPDF_json ignore unknown key in trailer 0 676 QPDF_json ignore unknown key in trailer 0
677 QPDF_json ignore unknown key in stream 0 677 QPDF_json ignore unknown key in stream 0
678 QPDF_json non-trivial null reserved 0 678 QPDF_json non-trivial null reserved 0
  679 +QPDF_json data and datafile 0
  680 +QPDF_json no stream data in update mode 0
  681 +QPDF_json updating existing stream 0
qpdf/qtest/qpdf-json.test
@@ -171,5 +171,35 @@ $td-&gt;runtest(&quot;check PDF&quot;, @@ -171,5 +171,35 @@ $td-&gt;runtest(&quot;check PDF&quot;,
171 {$td->FILE => "a.pdf"}, 171 {$td->FILE => "a.pdf"},
172 {$td->FILE => "b.pdf"}); 172 {$td->FILE => "b.pdf"});
173 173
  174 +# Replace mode tests
  175 +
  176 +$n_tests += 1;
  177 +$td->runtest("create PDF for replace",
  178 + {$td->COMMAND => "qpdf good13.pdf a.pdf" .
  179 + " --update-from-json=qpdf-json-update-errors.json"},
  180 + {$td->FILE => "update-from-json-errors.out",
  181 + $td->EXIT_STATUS => 2},
  182 + $td->NORMALIZE_NEWLINES);
  183 +
  184 +my @update_files = (
  185 + "update-stream-dict-only",
  186 + "update-stream-data",
  187 + "replace-with-stream",
  188 + "various-updates",
  189 + );
  190 +$n_tests += 2 * scalar(@update_files);
  191 +
  192 +foreach my $f (@update_files) {
  193 + $td->runtest("update: $f",
  194 + {$td->COMMAND =>
  195 + "qpdf good13.pdf a.pdf --qdf --static-id" .
  196 + " --update-from-json=$f.json"},
  197 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  198 + $td->runtest("$f: check updated",
  199 + {$td->FILE => "a.pdf"},
  200 + {$td->FILE => "$f-updated.pdf"});
  201 +}
  202 +
  203 +
174 cleanup(); 204 cleanup();
175 $td->report($n_tests); 205 $td->report($n_tests);
qpdf/qtest/qpdf/qjson-obj-key-errors.out
1 WARNING: qjson-obj-key-errors.json (obj:2 0 R, offset 218): object must have exactly one of "value" or "stream" 1 WARNING: qjson-obj-key-errors.json (obj:2 0 R, offset 218): object must have exactly one of "value" or "stream"
2 WARNING: qjson-obj-key-errors.json (obj:3 0 R, offset 516): object must have exactly one of "value" or "stream" 2 WARNING: qjson-obj-key-errors.json (obj:3 0 R, offset 516): object must have exactly one of "value" or "stream"
3 WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): "stream" is missing "dict" 3 WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): "stream" is missing "dict"
4 -WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): "stream" must have exactly one of "data" or "datafile"  
5 -WARNING: qjson-obj-key-errors.json (obj:5 0 R, offset 774): "stream" must have exactly one of "data" or "datafile" 4 +WARNING: qjson-obj-key-errors.json (obj:4 0 R, offset 684): new "stream" must have exactly one of "data" or "datafile"
  5 +WARNING: qjson-obj-key-errors.json (obj:5 0 R, offset 774): new "stream" must have exactly one of "data" or "datafile"
6 WARNING: qjson-obj-key-errors.json (trailer, offset 1152): "trailer" is missing "value" 6 WARNING: qjson-obj-key-errors.json (trailer, offset 1152): "trailer" is missing "value"
7 qpdf: qjson-obj-key-errors.json: errors found in JSON 7 qpdf: qjson-obj-key-errors.json: errors found in JSON
qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out
1 WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): "stream.dict" must be a dictionary 1 WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): "stream.dict" must be a dictionary
2 WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): unrecognized string value 2 WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): unrecognized string value
3 -WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 98): "stream" must have exactly one of "data" or "datafile" 3 +WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 98): new "stream" must have exactly one of "data" or "datafile"
4 WARNING: qjson-stream-dict-not-dict.json: "qpdf-v2.objects.trailer" was not seen 4 WARNING: qjson-stream-dict-not-dict.json: "qpdf-v2.objects.trailer" was not seen
5 qpdf: qjson-stream-dict-not-dict.json: errors found in JSON 5 qpdf: qjson-stream-dict-not-dict.json: errors found in JSON
qpdf/qtest/qpdf/qpdf-json-update-errors.json 0 → 100644
  1 +{
  2 + "qpdf-v2": {
  3 + "objects": {
  4 + "obj:4 0 R": {
  5 + "stream": {
  6 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoUG90YXRvKSBUagpFVAo=",
  7 + "datafile": "file-too",
  8 + "dict": {}
  9 + }
  10 + },
  11 + "obj:20 0 R": {
  12 + "stream": {
  13 + "dict": {
  14 + "/Bad": "string-value"
  15 + }
  16 + }
  17 + }
  18 + }
  19 + }
  20 +}
qpdf/qtest/qpdf/replace-with-stream-updated.pdf 0 → 100644
  1 +%PDF-2.0
  2 +%¿÷¢þ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 3 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 7 0
  14 +2 0 obj
  15 +<<
  16 + /dangling-ref-for-json-test [
  17 + 4 0 R
  18 + ]
  19 + /hex#20strings [
  20 + (Potato)
  21 + <01020300040560>
  22 + (AB)
  23 + ]
  24 + /indirect 5 0 R
  25 + /names [
  26 + /nesting
  27 + /hex#20strings
  28 + /text#2fplain
  29 + ]
  30 + /nesting <<
  31 + /a [
  32 + 1
  33 + 2
  34 + <<
  35 + /x (y)
  36 + >>
  37 + [
  38 + (z)
  39 + ]
  40 + ]
  41 + /b <<
  42 + / (legal)
  43 + /a [
  44 + 1
  45 + 2
  46 + ]
  47 + >>
  48 + >>
  49 + /strings [
  50 + (one)
  51 + <24a2>
  52 + ()
  53 + (\(\))
  54 + (\()
  55 + (\))
  56 + (a\f\b\t\r\nb)
  57 + (")
  58 + ("")
  59 + ("\("\)")
  60 + <410042>
  61 + (a\nb)
  62 + (a b)
  63 + <efbbbfcf80>
  64 + <efbbbff09fa594>
  65 + ]
  66 +>>
  67 +endobj
  68 +
  69 +%% Original object ID: 2 0
  70 +3 0 obj
  71 +<<
  72 + /Count 1
  73 + /Kids [
  74 + 7 0 R
  75 + ]
  76 + /Type /Pages
  77 +>>
  78 +endobj
  79 +
  80 +%% Original object ID: 9 0
  81 +4 0 obj
  82 +null
  83 +endobj
  84 +
  85 +%% Original object ID: 8 0
  86 +5 0 obj
  87 +<<
  88 + /K /V
  89 + /Length 6 0 R
  90 +>>
  91 +stream
  92 +new-stream-here
  93 +endstream
  94 +endobj
  95 +
  96 +6 0 obj
  97 +16
  98 +endobj
  99 +
  100 +%% Page 1
  101 +%% Original object ID: 3 0
  102 +7 0 obj
  103 +<<
  104 + /Contents 8 0 R
  105 + /MediaBox [
  106 + 0
  107 + 0
  108 + 612
  109 + 792
  110 + ]
  111 + /Parent 3 0 R
  112 + /Resources <<
  113 + /Font <<
  114 + /F1 10 0 R
  115 + >>
  116 + /ProcSet 11 0 R
  117 + >>
  118 + /Type /Page
  119 +>>
  120 +endobj
  121 +
  122 +%% Contents for page 1
  123 +%% Original object ID: 4 0
  124 +8 0 obj
  125 +<<
  126 + /Length 9 0 R
  127 +>>
  128 +stream
  129 +BT
  130 + /F1 24 Tf
  131 + 72 720 Td
  132 + (Potato) Tj
  133 +ET
  134 +endstream
  135 +endobj
  136 +
  137 +9 0 obj
  138 +44
  139 +endobj
  140 +
  141 +%% Original object ID: 6 0
  142 +10 0 obj
  143 +<<
  144 + /BaseFont /Helvetica
  145 + /Encoding /WinAnsiEncoding
  146 + /Name /F1
  147 + /Subtype /Type1
  148 + /Type /Font
  149 +>>
  150 +endobj
  151 +
  152 +%% Original object ID: 5 0
  153 +11 0 obj
  154 +[
  155 + /PDF
  156 + /Text
  157 +]
  158 +endobj
  159 +
  160 +xref
  161 +0 12
  162 +0000000000 65535 f
  163 +0000000052 00000 n
  164 +0000000133 00000 n
  165 +0000000756 00000 n
  166 +0000000855 00000 n
  167 +0000000903 00000 n
  168 +0000000982 00000 n
  169 +0000001038 00000 n
  170 +0000001282 00000 n
  171 +0000001381 00000 n
  172 +0000001427 00000 n
  173 +0000001573 00000 n
  174 +trailer <<
  175 + /QTest 2 0 R
  176 + /Root 1 0 R
  177 + /Size 12
  178 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
  179 +>>
  180 +startxref
  181 +1609
  182 +%%EOF
qpdf/qtest/qpdf/replace-with-stream.json 0 → 100644
  1 +{
  2 + "qpdf-v2": {
  3 + "pdfversion": "2.0",
  4 + "maxobjectid": 9,
  5 + "objects": {
  6 + "obj:8 0 R": {
  7 + "stream": {
  8 + "data": "bmV3LXN0cmVhbS1oZXJlCg==",
  9 + "dict": {
  10 + "/K": "/V"
  11 + }
  12 + }
  13 + }
  14 + }
  15 + }
  16 +}
qpdf/qtest/qpdf/update-from-json-errors.out 0 → 100644
  1 +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"
  2 +WARNING: good13.pdf (obj:20 0 R from qpdf-json-update-errors.json, offset 313): unrecognized string value
  3 +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"
  4 +qpdf: qpdf-json-update-errors.json: errors found in JSON
qpdf/qtest/qpdf/update-stream-data-updated.pdf 0 → 100644
  1 +%PDF-2.0
  2 +%¿÷¢þ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 3 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 7 0
  14 +2 0 obj
  15 +<<
  16 + /dangling-ref-for-json-test [
  17 + 4 0 R
  18 + ]
  19 + /hex#20strings [
  20 + (Potato)
  21 + <01020300040560>
  22 + (AB)
  23 + ]
  24 + /indirect 5 0 R
  25 + /names [
  26 + /nesting
  27 + /hex#20strings
  28 + /text#2fplain
  29 + ]
  30 + /nesting <<
  31 + /a [
  32 + 1
  33 + 2
  34 + <<
  35 + /x (y)
  36 + >>
  37 + [
  38 + (z)
  39 + ]
  40 + ]
  41 + /b <<
  42 + / (legal)
  43 + /a [
  44 + 1
  45 + 2
  46 + ]
  47 + >>
  48 + >>
  49 + /strings [
  50 + (one)
  51 + <24a2>
  52 + ()
  53 + (\(\))
  54 + (\()
  55 + (\))
  56 + (a\f\b\t\r\nb)
  57 + (")
  58 + ("")
  59 + ("\("\)")
  60 + <410042>
  61 + (a\nb)
  62 + (a b)
  63 + <efbbbfcf80>
  64 + <efbbbff09fa594>
  65 + ]
  66 +>>
  67 +endobj
  68 +
  69 +%% Original object ID: 2 0
  70 +3 0 obj
  71 +<<
  72 + /Count 1
  73 + /Kids [
  74 + 6 0 R
  75 + ]
  76 + /Type /Pages
  77 +>>
  78 +endobj
  79 +
  80 +%% Original object ID: 9 0
  81 +4 0 obj
  82 +null
  83 +endobj
  84 +
  85 +%% Original object ID: 8 0
  86 +5 0 obj
  87 +(hello)
  88 +endobj
  89 +
  90 +%% Page 1
  91 +%% Original object ID: 3 0
  92 +6 0 obj
  93 +<<
  94 + /Contents 7 0 R
  95 + /MediaBox [
  96 + 0
  97 + 0
  98 + 612
  99 + 792
  100 + ]
  101 + /Parent 3 0 R
  102 + /Resources <<
  103 + /Font <<
  104 + /F1 9 0 R
  105 + >>
  106 + /ProcSet 10 0 R
  107 + >>
  108 + /Type /Page
  109 +>>
  110 +endobj
  111 +
  112 +%% Contents for page 1
  113 +%% Original object ID: 4 0
  114 +7 0 obj
  115 +<<
  116 + /Length 8 0 R
  117 +>>
  118 +stream
  119 +BT
  120 + /F1 24 Tf
  121 + 72 720 Td
  122 + (Salad) Tj
  123 +ET
  124 +endstream
  125 +endobj
  126 +
  127 +8 0 obj
  128 +43
  129 +endobj
  130 +
  131 +%% Original object ID: 6 0
  132 +9 0 obj
  133 +<<
  134 + /BaseFont /Helvetica
  135 + /Encoding /WinAnsiEncoding
  136 + /Name /F1
  137 + /Subtype /Type1
  138 + /Type /Font
  139 +>>
  140 +endobj
  141 +
  142 +%% Original object ID: 5 0
  143 +10 0 obj
  144 +[
  145 + /PDF
  146 + /Text
  147 +]
  148 +endobj
  149 +
  150 +xref
  151 +0 11
  152 +0000000000 65535 f
  153 +0000000052 00000 n
  154 +0000000133 00000 n
  155 +0000000756 00000 n
  156 +0000000855 00000 n
  157 +0000000903 00000 n
  158 +0000000964 00000 n
  159 +0000001207 00000 n
  160 +0000001305 00000 n
  161 +0000001351 00000 n
  162 +0000001496 00000 n
  163 +trailer <<
  164 + /QTest 2 0 R
  165 + /Root 1 0 R
  166 + /Size 11
  167 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
  168 +>>
  169 +startxref
  170 +1532
  171 +%%EOF
qpdf/qtest/qpdf/update-stream-data.json 0 → 100644
  1 +{
  2 + "qpdf-v2": {
  3 + "pdfversion": "2.0",
  4 + "maxobjectid": 9,
  5 + "objects": {
  6 + "obj:1 0 R": {
  7 + "value": {
  8 + "/Pages": "2 0 R",
  9 + "/Type": "/Catalog"
  10 + }
  11 + },
  12 + "obj:4 0 R": {
  13 + "stream": {
  14 + "data": "QlQKICAvRjEgMjQgVGYKICA3MiA3MjAgVGQKICAoU2FsYWQpIFRqCkVUCg==",
  15 + "dict": {}
  16 + }
  17 + }
  18 + }
  19 + }
  20 +}
qpdf/qtest/qpdf/update-stream-dict-only-updated.pdf 0 → 100644
  1 +%PDF-2.0
  2 +%¿÷¢þ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 3 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 7 0
  14 +2 0 obj
  15 +<<
  16 + /dangling-ref-for-json-test [
  17 + 4 0 R
  18 + ]
  19 + /hex#20strings [
  20 + (Potato)
  21 + <01020300040560>
  22 + (AB)
  23 + ]
  24 + /indirect 5 0 R
  25 + /names [
  26 + /nesting
  27 + /hex#20strings
  28 + /text#2fplain
  29 + ]
  30 + /nesting <<
  31 + /a [
  32 + 1
  33 + 2
  34 + <<
  35 + /x (y)
  36 + >>
  37 + [
  38 + (z)
  39 + ]
  40 + ]
  41 + /b <<
  42 + / (legal)
  43 + /a [
  44 + 1
  45 + 2
  46 + ]
  47 + >>
  48 + >>
  49 + /strings [
  50 + (one)
  51 + <24a2>
  52 + ()
  53 + (\(\))
  54 + (\()
  55 + (\))
  56 + (a\f\b\t\r\nb)
  57 + (")
  58 + ("")
  59 + ("\("\)")
  60 + <410042>
  61 + (a\nb)
  62 + (a b)
  63 + <efbbbfcf80>
  64 + <efbbbff09fa594>
  65 + ]
  66 +>>
  67 +endobj
  68 +
  69 +%% Original object ID: 2 0
  70 +3 0 obj
  71 +<<
  72 + /Count 1
  73 + /Kids [
  74 + 6 0 R
  75 + ]
  76 + /Type /Pages
  77 +>>
  78 +endobj
  79 +
  80 +%% Original object ID: 9 0
  81 +4 0 obj
  82 +null
  83 +endobj
  84 +
  85 +%% Original object ID: 8 0
  86 +5 0 obj
  87 +(hello)
  88 +endobj
  89 +
  90 +%% Page 1
  91 +%% Original object ID: 3 0
  92 +6 0 obj
  93 +<<
  94 + /Contents 7 0 R
  95 + /MediaBox [
  96 + 0
  97 + 0
  98 + 612
  99 + 792
  100 + ]
  101 + /Parent 3 0 R
  102 + /Resources <<
  103 + /Font <<
  104 + /F1 9 0 R
  105 + >>
  106 + /ProcSet 10 0 R
  107 + >>
  108 + /Type /Page
  109 +>>
  110 +endobj
  111 +
  112 +%% Contents for page 1
  113 +%% Original object ID: 4 0
  114 +7 0 obj
  115 +<<
  116 + /Potato (salad)
  117 + /Length 8 0 R
  118 +>>
  119 +stream
  120 +BT
  121 + /F1 24 Tf
  122 + 72 720 Td
  123 + (Potato) Tj
  124 +ET
  125 +endstream
  126 +endobj
  127 +
  128 +8 0 obj
  129 +44
  130 +endobj
  131 +
  132 +%% Original object ID: 6 0
  133 +9 0 obj
  134 +<<
  135 + /BaseFont /Helvetica
  136 + /Encoding /WinAnsiEncoding
  137 + /Name /F1
  138 + /Subtype /Type1
  139 + /Type /Font
  140 +>>
  141 +endobj
  142 +
  143 +%% Original object ID: 5 0
  144 +10 0 obj
  145 +[
  146 + /PDF
  147 + /Text
  148 +]
  149 +endobj
  150 +
  151 +xref
  152 +0 11
  153 +0000000000 65535 f
  154 +0000000052 00000 n
  155 +0000000133 00000 n
  156 +0000000756 00000 n
  157 +0000000855 00000 n
  158 +0000000903 00000 n
  159 +0000000964 00000 n
  160 +0000001207 00000 n
  161 +0000001324 00000 n
  162 +0000001370 00000 n
  163 +0000001515 00000 n
  164 +trailer <<
  165 + /QTest 2 0 R
  166 + /Root 1 0 R
  167 + /Size 11
  168 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
  169 +>>
  170 +startxref
  171 +1551
  172 +%%EOF
qpdf/qtest/qpdf/update-stream-dict-only.json 0 → 100644
  1 +{
  2 + "qpdf-v2": {
  3 + "pdfversion": "2.0",
  4 + "maxobjectid": 9,
  5 + "objects": {
  6 + "obj:4 0 R": {
  7 + "stream": {
  8 + "dict": {
  9 + "/Potato": "u:salad"
  10 + }
  11 + }
  12 + }
  13 + }
  14 + }
  15 +}
qpdf/qtest/qpdf/various-updates-updated.pdf 0 → 100644
  1 +%PDF-2.0
  2 +%¿÷¢þ
  3 +%QDF-1.0
  4 +
  5 +%% Original object ID: 1 0
  6 +1 0 obj
  7 +<<
  8 + /Pages 3 0 R
  9 + /Type /Catalog
  10 +>>
  11 +endobj
  12 +
  13 +%% Original object ID: 7 0
  14 +2 0 obj
  15 +<<
  16 + /indirect 4 0 R
  17 + /now-#cf#80 [
  18 + 5 0 R
  19 + ]
  20 + /strings [
  21 + (one)
  22 + <feff03c0>
  23 + ]
  24 +>>
  25 +endobj
  26 +
  27 +%% Original object ID: 2 0
  28 +3 0 obj
  29 +<<
  30 + /Count 1
  31 + /Kids [
  32 + 6 0 R
  33 + ]
  34 + /Type /Pages
  35 +>>
  36 +endobj
  37 +
  38 +%% Original object ID: 8 0
  39 +4 0 obj
  40 +[
  41 + (hello)
  42 + 7 0 R
  43 +]
  44 +endobj
  45 +
  46 +%% Original object ID: 9 0
  47 +5 0 obj
  48 +3.14159
  49 +endobj
  50 +
  51 +%% Page 1
  52 +%% Original object ID: 3 0
  53 +6 0 obj
  54 +<<
  55 + /Contents 8 0 R
  56 + /MediaBox [
  57 + 0
  58 + 0
  59 + 612
  60 + 792
  61 + ]
  62 + /Parent 3 0 R
  63 + /Resources <<
  64 + /Font <<
  65 + /F1 10 0 R
  66 + >>
  67 + >>
  68 + /Type /Page
  69 +>>
  70 +endobj
  71 +
  72 +%% Original object ID: 10 0
  73 +7 0 obj
  74 +(this is new)
  75 +endobj
  76 +
  77 +%% Contents for page 1
  78 +%% Original object ID: 4 0
  79 +8 0 obj
  80 +<<
  81 + /Length 9 0 R
  82 +>>
  83 +stream
  84 +BT
  85 + /F1 24 Tf
  86 + 72 720 Td
  87 + (Potato) Tj
  88 +ET
  89 +endstream
  90 +endobj
  91 +
  92 +9 0 obj
  93 +44
  94 +endobj
  95 +
  96 +%% Original object ID: 6 0
  97 +10 0 obj
  98 +<<
  99 + /BaseFont /Helvetica
  100 + /Encoding /WinAnsiEncoding
  101 + /Name /F1
  102 + /Subtype /Type1
  103 + /Type /Font
  104 +>>
  105 +endobj
  106 +
  107 +xref
  108 +0 11
  109 +0000000000 65535 f
  110 +0000000052 00000 n
  111 +0000000133 00000 n
  112 +0000000272 00000 n
  113 +0000000371 00000 n
  114 +0000000436 00000 n
  115 +0000000497 00000 n
  116 +0000000699 00000 n
  117 +0000000779 00000 n
  118 +0000000878 00000 n
  119 +0000000924 00000 n
  120 +trailer <<
  121 + /QTest 2 0 R
  122 + /Root 1 0 R
  123 + /Size 11
  124 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
  125 +>>
  126 +startxref
  127 +1043
  128 +%%EOF
qpdf/qtest/qpdf/various-updates.json 0 → 100644
  1 +{
  2 + "qpdf-v2": {
  3 + "pdfversion": "2.0",
  4 + "maxobjectid": 9,
  5 + "objects": {
  6 + "obj:5 0 R": {
  7 + "value": null
  8 + },
  9 + "obj:7 0 R": {
  10 + "value": {
  11 + "/now-π": [
  12 + "9 0 R"
  13 + ],
  14 + "/indirect": "8 0 R",
  15 + "/strings": [
  16 + "u:one",
  17 + "u:π"
  18 + ]
  19 + }
  20 + },
  21 + "obj:8 0 R": {
  22 + "value": ["u:hello", "10 0 R"]
  23 + },
  24 + "obj:9 0 R": {
  25 + "value": 3.14159
  26 + },
  27 + "obj:10 0 R": {
  28 + "value": "u:this is new"
  29 + },
  30 + "trailer": {
  31 + "value": {
  32 + "/QTest": "7 0 R",
  33 + "/Root": "1 0 R",
  34 + "/Size": 9
  35 + }
  36 + }
  37 + }
  38 + }
  39 +}