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 58  
59 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 61 * Have a test case if possible that exercises the object description
65 62 which means we need some kind of semantic error that gets caught
66 63 after creation.
67 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 66 Try to never flatten pages tree. Make sure we do something reasonable
72 67 with pages tree repair. The problem is that if pages tree repair is
... ... @@ -104,22 +99,20 @@ JSON to PDF:
104 99  
105 100 Have --json-input and --update-from-json. With --json-input, the json
106 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 117 When reading a JSON string, any string that doesn't look like a name
125 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 1065 bool saw_dict;
1066 1066 bool saw_data;
1067 1067 bool saw_datafile;
  1068 + bool this_stream_needs_data;
1068 1069 std::vector<state_e> state_stack;
1069 1070 std::vector<QPDFObjectHandle> object_stack;
1070 1071 std::set<QPDFObjGen> reserved;
... ...
libqpdf/QPDF_json.cc
... ... @@ -102,7 +102,8 @@ QPDF::JSONReactor::JSONReactor(
102 102 saw_stream(false),
103 103 saw_dict(false),
104 104 saw_data(false),
105   - saw_datafile(false)
  105 + saw_datafile(false),
  106 + this_stream_needs_data(false)
106 107 {
107 108 state_stack.push_back(st_initial);
108 109 }
... ... @@ -111,8 +112,11 @@ void
111 112 QPDF::JSONReactor::error(size_t offset, std::string const& msg)
112 113 {
113 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 122 bool
... ... @@ -196,20 +200,22 @@ QPDF::JSONReactor::containerEnd(JSON const&amp; value)
196 200 QTC::TC("qpdf", "QPDF_json stream no dict");
197 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 205 QTC::TC("qpdf", "QPDF_json data datafile both or neither");
202 206 error(
203 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 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 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 326 nestedState(key, value, st_trailer);
321 327 this->cur_object = "trailer";
322 328 } else if (std::regex_match(key, m, OBJ_KEY_RE)) {
323   - // QXXXQ remember to handle null for delete
324 329 object_stack.push_back(reserveObject(m[1].str(), m[2].str()));
325 330 nestedState(key, value, st_object_top);
326 331 this->cur_object = key;
... ... @@ -347,9 +352,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
347 352 } else if (key == "stream") {
348 353 this->saw_stream = true;
349 354 nestedState(key, value, st_stream);
  355 + this->this_stream_needs_data = false;
350 356 if (tos.isStream()) {
351   - // QXXXQ reusing -- need QTC
  357 + QTC::TC("qpdf", "QPDF_json updating existing stream");
352 358 } else {
  359 + this->this_stream_needs_data = true;
353 360 replacement =
354 361 pdf.reserveStream(tos.getObjectID(), tos.getGeneration());
355 362 replaceObject(tos, replacement);
... ... @@ -386,12 +393,11 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
386 393 throw std::logic_error("no object on stack in st_stream");
387 394 }
388 395 auto tos = object_stack.back();
389   - auto uninitialized = QPDFObjectHandle();
390 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 401 this->saw_dict = true;
396 402 // Since a stream dictionary must be a dictionary, we can
397 403 // use nestedState to transition to st_value.
... ... @@ -509,11 +515,12 @@ QPDF::JSONReactor::makeObject(JSON const&amp; value)
509 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 524 return result;
518 525 }
519 526  
... ...
qpdf/qpdf.testcov
... ... @@ -676,3 +676,6 @@ QPDF_json ignore unknown key in object_top 0
676 676 QPDF_json ignore unknown key in trailer 0
677 677 QPDF_json ignore unknown key in stream 0
678 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 171 {$td->FILE => "a.pdf"},
172 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 204 cleanup();
175 205 $td->report($n_tests);
... ...
qpdf/qtest/qpdf/qjson-obj-key-errors.out
1 1 WARNING: qjson-obj-key-errors.json (obj:2 0 R, offset 218): object must have exactly one of "value" or "stream"
2 2 WARNING: qjson-obj-key-errors.json (obj:3 0 R, offset 516): object must have exactly one of "value" or "stream"
3 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 6 WARNING: qjson-obj-key-errors.json (trailer, offset 1152): "trailer" is missing "value"
7 7 qpdf: qjson-obj-key-errors.json: errors found in JSON
... ...
qpdf/qtest/qpdf/qjson-stream-dict-not-dict.out
1 1 WARNING: qjson-stream-dict-not-dict.json (obj:1 0 R, offset 118): "stream.dict" must be a dictionary
2 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 4 WARNING: qjson-stream-dict-not-dict.json: "qpdf-v2.objects.trailer" was not seen
5 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 +}
... ...