Commit 9b2eb01e2505e301ce95d31f5387fea0de35eff0

Authored by Jay Berkenbilt
1 parent 6c2fb5b8

Exercise object description in tests

... ... @@ -58,9 +58,6 @@ Some of this documentation has drifted from the actual implementation.
58 58  
59 59 Make sure pages tree repair generates warnings.
60 60  
61   -* Have a test case if possible that exercises the object description
62   - which means we need some kind of semantic error that gets caught
63   - after creation.
64 61 * Document that /Length is ignored in stream dictionary replacements
65 62  
66 63 Try to never flatten pages tree. Make sure we do something reasonable
... ...
include/qpdf/QPDF.hh
... ... @@ -1041,12 +1041,15 @@ class QPDF
1041 1041  
1042 1042 void containerStart();
1043 1043 void nestedState(std::string const& key, JSON const& value, state_e);
  1044 + void setObjectDescription(QPDFObjectHandle& oh, JSON const& value);
1044 1045 QPDFObjectHandle makeObject(JSON const& value);
1045 1046 void error(size_t offset, std::string const& message);
1046 1047 QPDFObjectHandle
1047 1048 reserveObject(std::string const& obj, std::string const& gen);
1048 1049 void replaceObject(
1049   - QPDFObjectHandle to_replace, QPDFObjectHandle replacement);
  1050 + QPDFObjectHandle to_replace,
  1051 + QPDFObjectHandle replacement,
  1052 + JSON const& value);
1050 1053  
1051 1054 QPDF& pdf;
1052 1055 std::shared_ptr<InputSource> is;
... ...
libqpdf/QPDF_json.cc
... ... @@ -249,11 +249,15 @@ QPDF::JSONReactor::reserveObject(std::string const&amp; obj, std::string const&amp; gen)
249 249  
250 250 void
251 251 QPDF::JSONReactor::replaceObject(
252   - QPDFObjectHandle to_replace, QPDFObjectHandle replacement)
  252 + QPDFObjectHandle to_replace,
  253 + QPDFObjectHandle replacement,
  254 + JSON const& value)
253 255 {
254 256 auto og = to_replace.getObjGen();
255 257 this->reserved.erase(og);
256 258 this->pdf.replaceObject(og, replacement);
  259 + auto oh = pdf.getObjectByObjGen(og);
  260 + setObjectDescription(oh, value);
257 261 }
258 262  
259 263 void
... ... @@ -326,9 +330,10 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
326 330 nestedState(key, value, st_trailer);
327 331 this->cur_object = "trailer";
328 332 } else if (std::regex_match(key, m, OBJ_KEY_RE)) {
329   - object_stack.push_back(reserveObject(m[1].str(), m[2].str()));
330   - nestedState(key, value, st_object_top);
331 333 this->cur_object = key;
  334 + auto oh = reserveObject(m[1].str(), m[2].str());
  335 + object_stack.push_back(oh);
  336 + nestedState(key, value, st_object_top);
332 337 } else {
333 338 QTC::TC("qpdf", "QPDF_json bad object key");
334 339 error(
... ... @@ -348,7 +353,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
348 353 this->saw_value = true;
349 354 next_state = st_object;
350 355 replacement = makeObject(value);
351   - replaceObject(tos, replacement);
  356 + replaceObject(tos, replacement, value);
352 357 } else if (key == "stream") {
353 358 this->saw_stream = true;
354 359 nestedState(key, value, st_stream);
... ... @@ -359,7 +364,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
359 364 this->this_stream_needs_data = true;
360 365 replacement =
361 366 pdf.reserveStream(tos.getObjectID(), tos.getGeneration());
362   - replaceObject(tos, replacement);
  367 + replaceObject(tos, replacement, value);
363 368 }
364 369 } else {
365 370 // Ignore unknown keys for forward compatibility
... ... @@ -376,6 +381,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const&amp; key, JSON const&amp; value)
376 381 // The trailer must be a dictionary, so we can use nestedState.
377 382 nestedState("trailer.value", value, st_object);
378 383 this->pdf.m->trailer = makeObject(value);
  384 + setObjectDescription(this->pdf.m->trailer, value);
379 385 } else if (key == "stream") {
380 386 // Don't need to set saw_stream here since there's already
381 387 // an error.
... ... @@ -471,6 +477,17 @@ QPDF::JSONReactor::arrayItem(JSON const&amp; value)
471 477 return true;
472 478 }
473 479  
  480 +void
  481 +QPDF::JSONReactor::setObjectDescription(QPDFObjectHandle& oh, JSON const& value)
  482 +{
  483 + std::string description = this->is->getName();
  484 + if (!this->cur_object.empty()) {
  485 + description += ", " + this->cur_object;
  486 + }
  487 + description += " at offset " + QUtil::uint_to_string(value.getStart());
  488 + oh.setObjectDescription(&this->pdf, description);
  489 +}
  490 +
474 491 QPDFObjectHandle
475 492 QPDF::JSONReactor::makeObject(JSON const& value)
476 493 {
... ... @@ -515,12 +532,9 @@ QPDF::JSONReactor::makeObject(JSON const&amp; value)
515 532 "JSONReactor::makeObject didn't initialize the object");
516 533 }
517 534  
518   - std::string description = this->is->getName();
519   - if (!this->cur_object.empty()) {
520   - description += " " + this->cur_object + ",";
  535 + if (!result.hasObjectDescription()) {
  536 + setObjectDescription(result, value);
521 537 }
522   - description += " offset " + QUtil::uint_to_string(value.getStart());
523   - result.setObjectDescription(&this->pdf, description);
524 538 return result;
525 539 }
526 540  
... ...
qpdf/qtest/qpdf-json.test
... ... @@ -202,6 +202,16 @@ foreach my $f (@update_files) {
202 202 {$td->FILE => "$f-updated.pdf"});
203 203 }
204 204  
  205 +# Exercise object description
  206 +$n_tests += 2;
  207 +$td->runtest("json-input object description",
  208 + {$td->COMMAND => "test_driver 89 manual-qpdf-json.json"},
  209 + {$td->FILE => "test-89.out", $td->EXIT_STATUS => 0},
  210 + $td->NORMALIZE_NEWLINES);
  211 +$td->runtest("update-from-json object description",
  212 + {$td->COMMAND => "test_driver 90 good13.pdf various-updates.json"},
  213 + {$td->FILE => "test-90.out", $td->EXIT_STATUS => 0},
  214 + $td->NORMALIZE_NEWLINES);
205 215  
206 216 cleanup();
207 217 $td->report($n_tests);
... ...
qpdf/qtest/qpdf/qjson-object-not-dict.out
1   -WARNING: qjson-object-not-dict.json (offset 100): "obj:1 0 R" must be a dictionary
  1 +WARNING: qjson-object-not-dict.json (obj:1 0 R, offset 100): "obj:1 0 R" must be a dictionary
2 2 qpdf: qjson-object-not-dict.json: errors found in JSON
... ...
qpdf/qtest/qpdf/test-89.out 0 → 100644
  1 +WARNING: manual-qpdf-json.json, trailer at offset 1761: operation for array attempted on object of type dictionary: ignoring attempt to append item
  2 +WARNING: manual-qpdf-json.json, obj:1 0 R at offset 1079: operation for array attempted on object of type dictionary: ignoring attempt to append item
  3 +WARNING: manual-qpdf-json.json, obj:5 0 R at offset 1404: operation for dictionary attempted on object of type array: ignoring key replacement request
  4 +WARNING: manual-qpdf-json.json, obj:5 0 R at offset 1416: operation for dictionary attempted on object of type name: ignoring key replacement request
  5 +test 89 done
... ...
qpdf/qtest/qpdf/test-90.out 0 → 100644
  1 +WARNING: various-updates.json, trailer at offset 580: operation for array attempted on object of type dictionary: ignoring attempt to append item
  2 +WARNING: various-updates.json, obj:7 0 R at offset 171: operation for array attempted on object of type dictionary: ignoring attempt to append item
  3 +WARNING: various-updates.json, obj:7 0 R at offset 283: operation for integer attempted on object of type array: returning 0
  4 +WARNING: good13.pdf, object 1 0 at offset 19: operation for array attempted on object of type dictionary: ignoring attempt to append item
  5 +test 90 done
... ...
qpdf/test_driver.cc
... ... @@ -3172,6 +3172,31 @@ test_88(QPDF&amp; pdf, char const* arg2)
3172 3172 assert(arr2.eraseItemAndGet(50).isNull());
3173 3173 }
3174 3174  
  3175 +static void
  3176 +test_89(QPDF& pdf, char const* arg2)
  3177 +{
  3178 + // Generate object warning with json-input. Crafted to work with
  3179 + // manual-qpdf-json.json.
  3180 + auto null = QPDFObjectHandle::newNull();
  3181 + pdf.getTrailer().appendItem(null);
  3182 + pdf.getRoot().appendItem(null);
  3183 + pdf.getObjectByID(5, 0).replaceKey("/X", null);
  3184 + pdf.getObjectByID(5, 0).getArrayItem(0).replaceKey("/X", null);
  3185 +}
  3186 +
  3187 +static void
  3188 +test_90(QPDF& pdf, char const* arg2)
  3189 +{
  3190 + // Generate object warning with update-from-json. Crafted to work
  3191 + // with good13.pdf and various-updates.json. JSON file is arg2.
  3192 + pdf.updateFromJSON(arg2);
  3193 + pdf.getTrailer().appendItem(QPDFObjectHandle::newNull());
  3194 + pdf.getTrailer().getKey("/QTest").appendItem(QPDFObjectHandle::newNull());
  3195 + pdf.getTrailer().getKey("/QTest").getKey("/strings").getIntValue();
  3196 + // not from json
  3197 + pdf.getRoot().appendItem(QPDFObjectHandle::newNull());
  3198 +}
  3199 +
3175 3200 void
3176 3201 runtest(int n, char const* filename1, char const* arg2)
3177 3202 {
... ... @@ -3235,6 +3260,8 @@ runtest(int n, char const* filename1, char const* arg2)
3235 3260 (std::string(filename1) + ".pdf").c_str(), p, size);
3236 3261 } else if (ignore_filename.count(n)) {
3237 3262 // Ignore filename argument entirely
  3263 + } else if (n == 89) {
  3264 + pdf.createFromJSON(filename1);
3238 3265 } else if (n % 2 == 0) {
3239 3266 if (n % 4 == 0) {
3240 3267 QTC::TC("qpdf", "exercise processFile(name)");
... ... @@ -3274,7 +3301,7 @@ runtest(int n, char const* filename1, char const* arg2)
3274 3301 {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79},
3275 3302 {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
3276 3303 {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87},
3277   - {88, test_88}};
  3304 + {88, test_88}, {89, test_89}, {90, test_90}};
3278 3305  
3279 3306 auto fn = test_functions.find(n);
3280 3307 if (fn == test_functions.end()) {
... ...