aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--include/qpdf/QPDF.hh5
-rw-r--r--libqpdf/QPDF_json.cc34
-rw-r--r--qpdf/qtest/qpdf-json.test10
-rw-r--r--qpdf/qtest/qpdf/qjson-object-not-dict.out2
-rw-r--r--qpdf/qtest/qpdf/test-89.out5
-rw-r--r--qpdf/qtest/qpdf/test-90.out5
-rw-r--r--qpdf/test_driver.cc29
8 files changed, 77 insertions, 16 deletions
diff --git a/TODO b/TODO
index eed33e6d..353be485 100644
--- a/TODO
+++ b/TODO
@@ -58,9 +58,6 @@ Some of this documentation has drifted from the actual implementation.
Make sure pages tree repair generates warnings.
-* 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.
* Document that /Length is ignored in stream dictionary replacements
Try to never flatten pages tree. Make sure we do something reasonable
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index 7d7af225..b4f03599 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -1041,12 +1041,15 @@ class QPDF
void containerStart();
void nestedState(std::string const& key, JSON const& value, state_e);
+ void setObjectDescription(QPDFObjectHandle& oh, JSON const& value);
QPDFObjectHandle makeObject(JSON const& value);
void error(size_t offset, std::string const& message);
QPDFObjectHandle
reserveObject(std::string const& obj, std::string const& gen);
void replaceObject(
- QPDFObjectHandle to_replace, QPDFObjectHandle replacement);
+ QPDFObjectHandle to_replace,
+ QPDFObjectHandle replacement,
+ JSON const& value);
QPDF& pdf;
std::shared_ptr<InputSource> is;
diff --git a/libqpdf/QPDF_json.cc b/libqpdf/QPDF_json.cc
index af12459a..9ae5b288 100644
--- a/libqpdf/QPDF_json.cc
+++ b/libqpdf/QPDF_json.cc
@@ -249,11 +249,15 @@ QPDF::JSONReactor::reserveObject(std::string const& obj, std::string const& gen)
void
QPDF::JSONReactor::replaceObject(
- QPDFObjectHandle to_replace, QPDFObjectHandle replacement)
+ QPDFObjectHandle to_replace,
+ QPDFObjectHandle replacement,
+ JSON const& value)
{
auto og = to_replace.getObjGen();
this->reserved.erase(og);
this->pdf.replaceObject(og, replacement);
+ auto oh = pdf.getObjectByObjGen(og);
+ setObjectDescription(oh, value);
}
void
@@ -326,9 +330,10 @@ 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)) {
- object_stack.push_back(reserveObject(m[1].str(), m[2].str()));
- nestedState(key, value, st_object_top);
this->cur_object = key;
+ auto oh = reserveObject(m[1].str(), m[2].str());
+ object_stack.push_back(oh);
+ nestedState(key, value, st_object_top);
} else {
QTC::TC("qpdf", "QPDF_json bad object key");
error(
@@ -348,7 +353,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value)
this->saw_value = true;
next_state = st_object;
replacement = makeObject(value);
- replaceObject(tos, replacement);
+ replaceObject(tos, replacement, value);
} else if (key == "stream") {
this->saw_stream = true;
nestedState(key, value, st_stream);
@@ -359,7 +364,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value)
this->this_stream_needs_data = true;
replacement =
pdf.reserveStream(tos.getObjectID(), tos.getGeneration());
- replaceObject(tos, replacement);
+ replaceObject(tos, replacement, value);
}
} else {
// Ignore unknown keys for forward compatibility
@@ -376,6 +381,7 @@ QPDF::JSONReactor::dictionaryItem(std::string const& key, JSON const& value)
// The trailer must be a dictionary, so we can use nestedState.
nestedState("trailer.value", value, st_object);
this->pdf.m->trailer = makeObject(value);
+ setObjectDescription(this->pdf.m->trailer, value);
} else if (key == "stream") {
// Don't need to set saw_stream here since there's already
// an error.
@@ -471,6 +477,17 @@ QPDF::JSONReactor::arrayItem(JSON const& value)
return true;
}
+void
+QPDF::JSONReactor::setObjectDescription(QPDFObjectHandle& oh, JSON const& value)
+{
+ std::string description = this->is->getName();
+ if (!this->cur_object.empty()) {
+ description += ", " + this->cur_object;
+ }
+ description += " at offset " + QUtil::uint_to_string(value.getStart());
+ oh.setObjectDescription(&this->pdf, description);
+}
+
QPDFObjectHandle
QPDF::JSONReactor::makeObject(JSON const& value)
{
@@ -515,12 +532,9 @@ QPDF::JSONReactor::makeObject(JSON const& value)
"JSONReactor::makeObject didn't initialize the object");
}
- std::string description = this->is->getName();
- if (!this->cur_object.empty()) {
- description += " " + this->cur_object + ",";
+ if (!result.hasObjectDescription()) {
+ setObjectDescription(result, value);
}
- description += " offset " + QUtil::uint_to_string(value.getStart());
- result.setObjectDescription(&this->pdf, description);
return result;
}
diff --git a/qpdf/qtest/qpdf-json.test b/qpdf/qtest/qpdf-json.test
index 22b714c5..c627da89 100644
--- a/qpdf/qtest/qpdf-json.test
+++ b/qpdf/qtest/qpdf-json.test
@@ -202,6 +202,16 @@ foreach my $f (@update_files) {
{$td->FILE => "$f-updated.pdf"});
}
+# Exercise object description
+$n_tests += 2;
+$td->runtest("json-input object description",
+ {$td->COMMAND => "test_driver 89 manual-qpdf-json.json"},
+ {$td->FILE => "test-89.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("update-from-json object description",
+ {$td->COMMAND => "test_driver 90 good13.pdf various-updates.json"},
+ {$td->FILE => "test-90.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
cleanup();
$td->report($n_tests);
diff --git a/qpdf/qtest/qpdf/qjson-object-not-dict.out b/qpdf/qtest/qpdf/qjson-object-not-dict.out
index cfb44457..1268c83d 100644
--- a/qpdf/qtest/qpdf/qjson-object-not-dict.out
+++ b/qpdf/qtest/qpdf/qjson-object-not-dict.out
@@ -1,2 +1,2 @@
-WARNING: qjson-object-not-dict.json (offset 100): "obj:1 0 R" must be a dictionary
+WARNING: qjson-object-not-dict.json (obj:1 0 R, offset 100): "obj:1 0 R" must be a dictionary
qpdf: qjson-object-not-dict.json: errors found in JSON
diff --git a/qpdf/qtest/qpdf/test-89.out b/qpdf/qtest/qpdf/test-89.out
new file mode 100644
index 00000000..97d71e1b
--- /dev/null
+++ b/qpdf/qtest/qpdf/test-89.out
@@ -0,0 +1,5 @@
+WARNING: manual-qpdf-json.json, trailer at offset 1761: operation for array attempted on object of type dictionary: ignoring attempt to append item
+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
+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
+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
+test 89 done
diff --git a/qpdf/qtest/qpdf/test-90.out b/qpdf/qtest/qpdf/test-90.out
new file mode 100644
index 00000000..2c535a09
--- /dev/null
+++ b/qpdf/qtest/qpdf/test-90.out
@@ -0,0 +1,5 @@
+WARNING: various-updates.json, trailer at offset 580: operation for array attempted on object of type dictionary: ignoring attempt to append item
+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
+WARNING: various-updates.json, obj:7 0 R at offset 283: operation for integer attempted on object of type array: returning 0
+WARNING: good13.pdf, object 1 0 at offset 19: operation for array attempted on object of type dictionary: ignoring attempt to append item
+test 90 done
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index f355aa54..8f09785a 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -3172,6 +3172,31 @@ test_88(QPDF& pdf, char const* arg2)
assert(arr2.eraseItemAndGet(50).isNull());
}
+static void
+test_89(QPDF& pdf, char const* arg2)
+{
+ // Generate object warning with json-input. Crafted to work with
+ // manual-qpdf-json.json.
+ auto null = QPDFObjectHandle::newNull();
+ pdf.getTrailer().appendItem(null);
+ pdf.getRoot().appendItem(null);
+ pdf.getObjectByID(5, 0).replaceKey("/X", null);
+ pdf.getObjectByID(5, 0).getArrayItem(0).replaceKey("/X", null);
+}
+
+static void
+test_90(QPDF& pdf, char const* arg2)
+{
+ // Generate object warning with update-from-json. Crafted to work
+ // with good13.pdf and various-updates.json. JSON file is arg2.
+ pdf.updateFromJSON(arg2);
+ pdf.getTrailer().appendItem(QPDFObjectHandle::newNull());
+ pdf.getTrailer().getKey("/QTest").appendItem(QPDFObjectHandle::newNull());
+ pdf.getTrailer().getKey("/QTest").getKey("/strings").getIntValue();
+ // not from json
+ pdf.getRoot().appendItem(QPDFObjectHandle::newNull());
+}
+
void
runtest(int n, char const* filename1, char const* arg2)
{
@@ -3235,6 +3260,8 @@ runtest(int n, char const* filename1, char const* arg2)
(std::string(filename1) + ".pdf").c_str(), p, size);
} else if (ignore_filename.count(n)) {
// Ignore filename argument entirely
+ } else if (n == 89) {
+ pdf.createFromJSON(filename1);
} else if (n % 2 == 0) {
if (n % 4 == 0) {
QTC::TC("qpdf", "exercise processFile(name)");
@@ -3274,7 +3301,7 @@ runtest(int n, char const* filename1, char const* arg2)
{76, test_76}, {77, test_77}, {78, test_78}, {79, test_79},
{80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
{84, test_84}, {85, test_85}, {86, test_86}, {87, test_87},
- {88, test_88}};
+ {88, test_88}, {89, test_89}, {90, test_90}};
auto fn = test_functions.find(n);
if (fn == test_functions.end()) {