Commit e362bce8e86f4912eaa008bac06f9e2c19b72d3f
Merge branch 'jw' from #1146 into work
Showing
49 changed files
with
2555 additions
and
327 deletions
include/qpdf/JSON.hh
| @@ -290,8 +290,11 @@ class JSON | @@ -290,8 +290,11 @@ class JSON | ||
| 290 | QPDF_DLL | 290 | QPDF_DLL |
| 291 | qpdf_offset_t getEnd() const; | 291 | qpdf_offset_t getEnd() const; |
| 292 | 292 | ||
| 293 | + // The following class does not form part of the public API and is for internal use only. | ||
| 294 | + | ||
| 295 | + class Writer; | ||
| 296 | + | ||
| 293 | private: | 297 | private: |
| 294 | - static std::string encode_string(std::string const& utf8); | ||
| 295 | static void writeClose(Pipeline* p, bool first, size_t depth, char const* delimeter); | 298 | static void writeClose(Pipeline* p, bool first, size_t depth, char const* delimeter); |
| 296 | 299 | ||
| 297 | enum value_type_e { | 300 | enum value_type_e { |
include/qpdf/QPDF.hh
| @@ -1411,19 +1411,6 @@ class QPDF | @@ -1411,19 +1411,6 @@ class QPDF | ||
| 1411 | // JSON import | 1411 | // JSON import |
| 1412 | void importJSON(std::shared_ptr<InputSource>, bool must_be_complete); | 1412 | void importJSON(std::shared_ptr<InputSource>, bool must_be_complete); |
| 1413 | 1413 | ||
| 1414 | - // JSON write | ||
| 1415 | - void writeJSONStream( | ||
| 1416 | - int version, | ||
| 1417 | - Pipeline* p, | ||
| 1418 | - bool& first, | ||
| 1419 | - std::string const& key, | ||
| 1420 | - QPDFObjectHandle&, | ||
| 1421 | - qpdf_stream_decode_level_e, | ||
| 1422 | - qpdf_json_stream_data_e, | ||
| 1423 | - std::string const& file_prefix); | ||
| 1424 | - void writeJSONObject( | ||
| 1425 | - int version, Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&); | ||
| 1426 | - | ||
| 1427 | // Type conversion helper methods | 1414 | // Type conversion helper methods |
| 1428 | template <typename T> | 1415 | template <typename T> |
| 1429 | static qpdf_offset_t | 1416 | static qpdf_offset_t |
include/qpdf/QPDFJob.hh
| @@ -551,7 +551,6 @@ class QPDFJob | @@ -551,7 +551,6 @@ class QPDFJob | ||
| 551 | // JSON | 551 | // JSON |
| 552 | void doJSON(QPDF& pdf, Pipeline*); | 552 | void doJSON(QPDF& pdf, Pipeline*); |
| 553 | QPDFObjGen::set getWantedJSONObjects(); | 553 | QPDFObjGen::set getWantedJSONObjects(); |
| 554 | - void doJSONObject(Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&); | ||
| 555 | void doJSONObjects(Pipeline* p, bool& first, QPDF& pdf); | 554 | void doJSONObjects(Pipeline* p, bool& first, QPDF& pdf); |
| 556 | void doJSONObjectinfo(Pipeline* p, bool& first, QPDF& pdf); | 555 | void doJSONObjectinfo(Pipeline* p, bool& first, QPDF& pdf); |
| 557 | void doJSONPages(Pipeline* p, bool& first, QPDF& pdf); | 556 | void doJSONPages(Pipeline* p, bool& first, QPDF& pdf); |
include/qpdf/QPDFObjectHandle.hh
| @@ -1197,6 +1197,13 @@ class QPDFObjectHandle | @@ -1197,6 +1197,13 @@ class QPDFObjectHandle | ||
| 1197 | QPDF_DLL | 1197 | QPDF_DLL |
| 1198 | JSON getJSON(int json_version, bool dereference_indirect = false); | 1198 | JSON getJSON(int json_version, bool dereference_indirect = false); |
| 1199 | 1199 | ||
| 1200 | + // Write the object encoded as JSON to a pipeline. This is equivalent to, but more efficient | ||
| 1201 | + // than, calling getJSON(json_version, dereference_indirect).write(p, depth). See the | ||
| 1202 | + // documentation for getJSON and JSON::write for further detail. | ||
| 1203 | + QPDF_DLL | ||
| 1204 | + void | ||
| 1205 | + writeJSON(int json_version, Pipeline* p, bool dereference_indirect = false, size_t depth = 0); | ||
| 1206 | + | ||
| 1200 | // Deprecated version uses v1 for backward compatibility. | 1207 | // Deprecated version uses v1 for backward compatibility. |
| 1201 | // ABI: remove for qpdf 12 | 1208 | // ABI: remove for qpdf 12 |
| 1202 | [[deprecated("Use getJSON(int version)")]] QPDF_DLL JSON | 1209 | [[deprecated("Use getJSON(int version)")]] QPDF_DLL JSON |
| @@ -1353,6 +1360,8 @@ class QPDFObjectHandle | @@ -1353,6 +1360,8 @@ class QPDFObjectHandle | ||
| 1353 | return obj.get(); | 1360 | return obj.get(); |
| 1354 | } | 1361 | } |
| 1355 | 1362 | ||
| 1363 | + void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false); | ||
| 1364 | + | ||
| 1356 | private: | 1365 | private: |
| 1357 | QPDF_Array* asArray(); | 1366 | QPDF_Array* asArray(); |
| 1358 | QPDF_Bool* asBool(); | 1367 | QPDF_Bool* asBool(); |
libqpdf/JSON.cc
| 1 | #include <qpdf/JSON.hh> | 1 | #include <qpdf/JSON.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 4 | + | ||
| 3 | #include <qpdf/BufferInputSource.hh> | 5 | #include <qpdf/BufferInputSource.hh> |
| 4 | #include <qpdf/Pl_Base64.hh> | 6 | #include <qpdf/Pl_Base64.hh> |
| 5 | #include <qpdf/Pl_Concatenate.hh> | 7 | #include <qpdf/Pl_Concatenate.hh> |
| @@ -119,7 +121,7 @@ JSON::JSON_array::write(Pipeline* p, size_t depth) const | @@ -119,7 +121,7 @@ JSON::JSON_array::write(Pipeline* p, size_t depth) const | ||
| 119 | JSON::JSON_string::JSON_string(std::string const& utf8) : | 121 | JSON::JSON_string::JSON_string(std::string const& utf8) : |
| 120 | JSON_value(vt_string), | 122 | JSON_value(vt_string), |
| 121 | utf8(utf8), | 123 | utf8(utf8), |
| 122 | - encoded(encode_string(utf8)) | 124 | + encoded(Writer::encode_string(utf8)) |
| 123 | { | 125 | { |
| 124 | } | 126 | } |
| 125 | 127 | ||
| @@ -211,7 +213,7 @@ JSON::unparse() const | @@ -211,7 +213,7 @@ JSON::unparse() const | ||
| 211 | } | 213 | } |
| 212 | 214 | ||
| 213 | std::string | 215 | std::string |
| 214 | -JSON::encode_string(std::string const& str) | 216 | +JSON::Writer::encode_string(std::string const& str) |
| 215 | { | 217 | { |
| 216 | static auto constexpr hexchars = "0123456789abcdef"; | 218 | static auto constexpr hexchars = "0123456789abcdef"; |
| 217 | 219 | ||
| @@ -279,7 +281,7 @@ JSON | @@ -279,7 +281,7 @@ JSON | ||
| 279 | JSON::addDictionaryMember(std::string const& key, JSON const& val) | 281 | JSON::addDictionaryMember(std::string const& key, JSON const& val) |
| 280 | { | 282 | { |
| 281 | if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) { | 283 | if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) { |
| 282 | - return obj->members[encode_string(key)] = val.m ? val : makeNull(); | 284 | + return obj->members[Writer::encode_string(key)] = val.m ? val : makeNull(); |
| 283 | } else { | 285 | } else { |
| 284 | throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); | 286 | throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); |
| 285 | } | 287 | } |
libqpdf/QPDFJob.cc
| @@ -955,23 +955,6 @@ QPDFJob::getWantedJSONObjects() | @@ -955,23 +955,6 @@ QPDFJob::getWantedJSONObjects() | ||
| 955 | } | 955 | } |
| 956 | 956 | ||
| 957 | void | 957 | void |
| 958 | -QPDFJob::doJSONObject(Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle& obj) | ||
| 959 | -{ | ||
| 960 | - if (m->json_version == 1) { | ||
| 961 | - JSON::writeDictionaryItem(p, first, key, obj.getJSON(1, true), 2); | ||
| 962 | - } else { | ||
| 963 | - auto j = JSON::makeDictionary(); | ||
| 964 | - if (obj.isStream()) { | ||
| 965 | - j.addDictionaryMember("stream", JSON::makeDictionary()) | ||
| 966 | - .addDictionaryMember("dict", obj.getDict().getJSON(m->json_version, true)); | ||
| 967 | - } else { | ||
| 968 | - j.addDictionaryMember("value", obj.getJSON(m->json_version, true)); | ||
| 969 | - } | ||
| 970 | - JSON::writeDictionaryItem(p, first, key, j, 2); | ||
| 971 | - } | ||
| 972 | -} | ||
| 973 | - | ||
| 974 | -void | ||
| 975 | QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) | 958 | QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) |
| 976 | { | 959 | { |
| 977 | if (m->json_version == 1) { | 960 | if (m->json_version == 1) { |
| @@ -982,16 +965,17 @@ QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) | @@ -982,16 +965,17 @@ QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) | ||
| 982 | auto wanted_og = getWantedJSONObjects(); | 965 | auto wanted_og = getWantedJSONObjects(); |
| 983 | for (auto& obj: pdf.getAllObjects()) { | 966 | for (auto& obj: pdf.getAllObjects()) { |
| 984 | std::string key = obj.unparse(); | 967 | std::string key = obj.unparse(); |
| 985 | - if (m->json_version > 1) { | ||
| 986 | - key = "obj:" + key; | ||
| 987 | - } | 968 | + |
| 988 | if (all_objects || wanted_og.count(obj.getObjGen())) { | 969 | if (all_objects || wanted_og.count(obj.getObjGen())) { |
| 989 | - doJSONObject(p, first_object, key, obj); | 970 | + JSON::writeDictionaryKey(p, first_object, obj.unparse(), 2); |
| 971 | + obj.writeJSON(1, p, true, 2); | ||
| 972 | + first_object = false; | ||
| 990 | } | 973 | } |
| 991 | } | 974 | } |
| 992 | if (all_objects || m->json_objects.count("trailer")) { | 975 | if (all_objects || m->json_objects.count("trailer")) { |
| 993 | - auto trailer = pdf.getTrailer(); | ||
| 994 | - doJSONObject(p, first_object, "trailer", trailer); | 976 | + JSON::writeDictionaryKey(p, first_object, "trailer", 2); |
| 977 | + pdf.getTrailer().writeJSON(1, p, true, 2); | ||
| 978 | + first_object = false; | ||
| 995 | } | 979 | } |
| 996 | JSON::writeDictionaryClose(p, first_object, 1); | 980 | JSON::writeDictionaryClose(p, first_object, 1); |
| 997 | } else { | 981 | } else { |
| @@ -3097,9 +3081,10 @@ QPDFJob::writeOutfile(QPDF& pdf) | @@ -3097,9 +3081,10 @@ QPDFJob::writeOutfile(QPDF& pdf) | ||
| 3097 | try { | 3081 | try { |
| 3098 | QUtil::remove_file(backup.c_str()); | 3082 | QUtil::remove_file(backup.c_str()); |
| 3099 | } catch (QPDFSystemError& e) { | 3083 | } catch (QPDFSystemError& e) { |
| 3100 | - *m->log->getError() << m->message_prefix << ": unable to delete original file (" | ||
| 3101 | - << e.what() << ");" << " original file left in " << backup | ||
| 3102 | - << ", but the input was successfully replaced\n"; | 3084 | + *m->log->getError() |
| 3085 | + << m->message_prefix << ": unable to delete original file (" << e.what() << ");" | ||
| 3086 | + << " original file left in " << backup | ||
| 3087 | + << ", but the input was successfully replaced\n"; | ||
| 3103 | } | 3088 | } |
| 3104 | } | 3089 | } |
| 3105 | } | 3090 | } |
libqpdf/QPDFObjectHandle.cc
| @@ -3,6 +3,7 @@ | @@ -3,6 +3,7 @@ | ||
| 3 | #include <qpdf/BufferInputSource.hh> | 3 | #include <qpdf/BufferInputSource.hh> |
| 4 | #include <qpdf/Pl_Buffer.hh> | 4 | #include <qpdf/Pl_Buffer.hh> |
| 5 | #include <qpdf/Pl_QPDFTokenizer.hh> | 5 | #include <qpdf/Pl_QPDFTokenizer.hh> |
| 6 | +#include <qpdf/JSON_writer.hh> | ||
| 6 | #include <qpdf/QPDF.hh> | 7 | #include <qpdf/QPDF.hh> |
| 7 | #include <qpdf/QPDFExc.hh> | 8 | #include <qpdf/QPDFExc.hh> |
| 8 | #include <qpdf/QPDFLogger.hh> | 9 | #include <qpdf/QPDFLogger.hh> |
| @@ -1617,10 +1618,33 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) | @@ -1617,10 +1618,33 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) | ||
| 1617 | } else if (!dereference()) { | 1618 | } else if (!dereference()) { |
| 1618 | throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); | 1619 | throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); |
| 1619 | } else { | 1620 | } else { |
| 1620 | - return obj->getJSON(json_version); | 1621 | + Pl_Buffer p{"json"}; |
| 1622 | + JSON::Writer jw{&p, 0}; | ||
| 1623 | + writeJSON(json_version, jw, dereference_indirect); | ||
| 1624 | + p.finish(); | ||
| 1625 | + return JSON::parse(p.getString()); | ||
| 1621 | } | 1626 | } |
| 1622 | } | 1627 | } |
| 1623 | 1628 | ||
| 1629 | +void | ||
| 1630 | +QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect) | ||
| 1631 | +{ | ||
| 1632 | + if (!dereference_indirect && isIndirect()) { | ||
| 1633 | + p << "\"" << getObjGen().unparse(' ') << " R\""; | ||
| 1634 | + } else if (!dereference()) { | ||
| 1635 | + throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); | ||
| 1636 | + } else { | ||
| 1637 | + obj->writeJSON(json_version, p); | ||
| 1638 | + } | ||
| 1639 | +} | ||
| 1640 | + | ||
| 1641 | +void | ||
| 1642 | +QPDFObjectHandle::writeJSON(int json_version, Pipeline* p, bool dereference_indirect, size_t depth) | ||
| 1643 | +{ | ||
| 1644 | + JSON::Writer jw{p, depth}; | ||
| 1645 | + writeJSON(json_version, jw, dereference_indirect); | ||
| 1646 | +} | ||
| 1647 | + | ||
| 1624 | JSON | 1648 | JSON |
| 1625 | QPDFObjectHandle::getStreamJSON( | 1649 | QPDFObjectHandle::getStreamJSON( |
| 1626 | int json_version, | 1650 | int json_version, |
libqpdf/QPDF_Array.cc
| 1 | #include <qpdf/QPDF_Array.hh> | 1 | #include <qpdf/QPDF_Array.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QPDFObjectHandle.hh> | 4 | #include <qpdf/QPDFObjectHandle.hh> |
| 4 | #include <qpdf/QPDFObject_private.hh> | 5 | #include <qpdf/QPDFObject_private.hh> |
| 5 | #include <qpdf/QTC.hh> | 6 | #include <qpdf/QTC.hh> |
| @@ -148,36 +149,41 @@ QPDF_Array::unparse() | @@ -148,36 +149,41 @@ QPDF_Array::unparse() | ||
| 148 | return result; | 149 | return result; |
| 149 | } | 150 | } |
| 150 | 151 | ||
| 151 | -JSON | ||
| 152 | -QPDF_Array::getJSON(int json_version) | 152 | +void |
| 153 | +QPDF_Array::writeJSON(int json_version, JSON::Writer& p) | ||
| 153 | { | 154 | { |
| 154 | - static const JSON j_null = JSON::makeNull(); | ||
| 155 | - JSON j_array = JSON::makeArray(); | 155 | + p.writeStart('['); |
| 156 | if (sp) { | 156 | if (sp) { |
| 157 | int next = 0; | 157 | int next = 0; |
| 158 | for (auto& item: sp->elements) { | 158 | for (auto& item: sp->elements) { |
| 159 | int key = item.first; | 159 | int key = item.first; |
| 160 | for (int j = next; j < key; ++j) { | 160 | for (int j = next; j < key; ++j) { |
| 161 | - j_array.addArrayElement(j_null); | 161 | + p.writeNext() << "null"; |
| 162 | } | 162 | } |
| 163 | + p.writeNext(); | ||
| 163 | auto og = item.second->getObjGen(); | 164 | auto og = item.second->getObjGen(); |
| 164 | - j_array.addArrayElement( | ||
| 165 | - og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R") | ||
| 166 | - : item.second->getJSON(json_version)); | 165 | + if (og.isIndirect()) { |
| 166 | + p << "\"" << og.unparse(' ') << " R\""; | ||
| 167 | + } else { | ||
| 168 | + item.second->writeJSON(json_version, p); | ||
| 169 | + } | ||
| 167 | next = ++key; | 170 | next = ++key; |
| 168 | } | 171 | } |
| 169 | for (int j = next; j < sp->size; ++j) { | 172 | for (int j = next; j < sp->size; ++j) { |
| 170 | - j_array.addArrayElement(j_null); | 173 | + p.writeNext() << "null"; |
| 171 | } | 174 | } |
| 172 | } else { | 175 | } else { |
| 173 | for (auto const& item: elements) { | 176 | for (auto const& item: elements) { |
| 177 | + p.writeNext(); | ||
| 174 | auto og = item->getObjGen(); | 178 | auto og = item->getObjGen(); |
| 175 | - j_array.addArrayElement( | ||
| 176 | - og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R") | ||
| 177 | - : item->getJSON(json_version)); | 179 | + if (og.isIndirect()) { |
| 180 | + p << "\"" << og.unparse(' ') << " R\""; | ||
| 181 | + } else { | ||
| 182 | + item->writeJSON(json_version, p); | ||
| 183 | + } | ||
| 178 | } | 184 | } |
| 179 | } | 185 | } |
| 180 | - return j_array; | 186 | + p.writeEnd(']'); |
| 181 | } | 187 | } |
| 182 | 188 | ||
| 183 | QPDFObjectHandle | 189 | QPDFObjectHandle |
libqpdf/QPDF_Bool.cc
| 1 | #include <qpdf/QPDF_Bool.hh> | 1 | #include <qpdf/QPDF_Bool.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 4 | + | ||
| 3 | QPDF_Bool::QPDF_Bool(bool val) : | 5 | QPDF_Bool::QPDF_Bool(bool val) : |
| 4 | QPDFValue(::ot_boolean, "boolean"), | 6 | QPDFValue(::ot_boolean, "boolean"), |
| 5 | val(val) | 7 | val(val) |
| @@ -24,10 +26,10 @@ QPDF_Bool::unparse() | @@ -24,10 +26,10 @@ QPDF_Bool::unparse() | ||
| 24 | return (val ? "true" : "false"); | 26 | return (val ? "true" : "false"); |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | -JSON | ||
| 28 | -QPDF_Bool::getJSON(int json_version) | 29 | +void |
| 30 | +QPDF_Bool::writeJSON(int json_version, JSON::Writer& p) | ||
| 29 | { | 31 | { |
| 30 | - return JSON::makeBool(this->val); | 32 | + p << val; |
| 31 | } | 33 | } |
| 32 | 34 | ||
| 33 | bool | 35 | bool |
libqpdf/QPDF_Destroyed.cc
| @@ -28,9 +28,8 @@ QPDF_Destroyed::unparse() | @@ -28,9 +28,8 @@ QPDF_Destroyed::unparse() | ||
| 28 | return ""; | 28 | return ""; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | -JSON | ||
| 32 | -QPDF_Destroyed::getJSON(int json_version) | 31 | +void |
| 32 | +QPDF_Destroyed::writeJSON(int json_version, JSON::Writer& p) | ||
| 33 | { | 33 | { |
| 34 | throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF"); | 34 | throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF"); |
| 35 | - return JSON::makeNull(); | ||
| 36 | -} | 35 | +} |
| 37 | \ No newline at end of file | 36 | \ No newline at end of file |
libqpdf/QPDF_Dictionary.cc
| 1 | #include <qpdf/QPDF_Dictionary.hh> | 1 | #include <qpdf/QPDF_Dictionary.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QPDFObject_private.hh> | 4 | #include <qpdf/QPDFObject_private.hh> |
| 4 | #include <qpdf/QPDF_Name.hh> | 5 | #include <qpdf/QPDF_Name.hh> |
| 5 | #include <qpdf/QPDF_Null.hh> | 6 | #include <qpdf/QPDF_Null.hh> |
| @@ -67,28 +68,30 @@ QPDF_Dictionary::unparse() | @@ -67,28 +68,30 @@ QPDF_Dictionary::unparse() | ||
| 67 | return result; | 68 | return result; |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | -JSON | ||
| 71 | -QPDF_Dictionary::getJSON(int json_version) | 71 | +void |
| 72 | +QPDF_Dictionary::writeJSON(int json_version, JSON::Writer& p) | ||
| 72 | { | 73 | { |
| 73 | - JSON j = JSON::makeDictionary(); | 74 | + p.writeStart('{'); |
| 74 | for (auto& iter: this->items) { | 75 | for (auto& iter: this->items) { |
| 75 | if (!iter.second.isNull()) { | 76 | if (!iter.second.isNull()) { |
| 77 | + p.writeNext(); | ||
| 76 | if (json_version == 1) { | 78 | if (json_version == 1) { |
| 77 | - j.addDictionaryMember( | ||
| 78 | - QPDF_Name::normalizeName(iter.first), iter.second.getJSON(json_version)); | 79 | + p << "\"" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first)) |
| 80 | + << "\": "; | ||
| 81 | + } else if (auto res = QPDF_Name::analyzeJSONEncoding(iter.first); res.first) { | ||
| 82 | + if (res.second) { | ||
| 83 | + p << "\"" << iter.first << "\": "; | ||
| 84 | + } else { | ||
| 85 | + p << "\"" << JSON::Writer::encode_string(iter.first) << "\": "; | ||
| 86 | + } | ||
| 79 | } else { | 87 | } else { |
| 80 | - bool has_8bit_chars; | ||
| 81 | - bool is_valid_utf8; | ||
| 82 | - bool is_utf16; | ||
| 83 | - QUtil::analyze_encoding(iter.first, has_8bit_chars, is_valid_utf8, is_utf16); | ||
| 84 | - std::string key = !has_8bit_chars || is_valid_utf8 | ||
| 85 | - ? iter.first | ||
| 86 | - : "n:" + QPDF_Name::normalizeName(iter.first); | ||
| 87 | - j.addDictionaryMember(key, iter.second.getJSON(json_version)); | 88 | + p << "\"n:" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first)) |
| 89 | + << "\": "; | ||
| 88 | } | 90 | } |
| 91 | + iter.second.writeJSON(json_version, p); | ||
| 89 | } | 92 | } |
| 90 | } | 93 | } |
| 91 | - return j; | 94 | + p.writeEnd('}'); |
| 92 | } | 95 | } |
| 93 | 96 | ||
| 94 | bool | 97 | bool |
libqpdf/QPDF_InlineImage.cc
| 1 | #include <qpdf/QPDF_InlineImage.hh> | 1 | #include <qpdf/QPDF_InlineImage.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 4 | + | ||
| 3 | QPDF_InlineImage::QPDF_InlineImage(std::string const& val) : | 5 | QPDF_InlineImage::QPDF_InlineImage(std::string const& val) : |
| 4 | QPDFValue(::ot_inlineimage, "inline-image"), | 6 | QPDFValue(::ot_inlineimage, "inline-image"), |
| 5 | val(val) | 7 | val(val) |
| @@ -24,8 +26,8 @@ QPDF_InlineImage::unparse() | @@ -24,8 +26,8 @@ QPDF_InlineImage::unparse() | ||
| 24 | return this->val; | 26 | return this->val; |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | -JSON | ||
| 28 | -QPDF_InlineImage::getJSON(int json_version) | 29 | +void |
| 30 | +QPDF_InlineImage::writeJSON(int json_version, JSON::Writer& p) | ||
| 29 | { | 31 | { |
| 30 | - return JSON::makeNull(); | 32 | + p << "null"; |
| 31 | } | 33 | } |
libqpdf/QPDF_Integer.cc
| 1 | #include <qpdf/QPDF_Integer.hh> | 1 | #include <qpdf/QPDF_Integer.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | ||
| 5 | QPDF_Integer::QPDF_Integer(long long val) : | 6 | QPDF_Integer::QPDF_Integer(long long val) : |
| @@ -26,10 +27,10 @@ QPDF_Integer::unparse() | @@ -26,10 +27,10 @@ QPDF_Integer::unparse() | ||
| 26 | return std::to_string(this->val); | 27 | return std::to_string(this->val); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | -JSON | ||
| 30 | -QPDF_Integer::getJSON(int json_version) | 30 | +void |
| 31 | +QPDF_Integer::writeJSON(int json_version, JSON::Writer& p) | ||
| 31 | { | 32 | { |
| 32 | - return JSON::makeInt(this->val); | 33 | + p << std::to_string(this->val); |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | long long | 36 | long long |
libqpdf/QPDF_Name.cc
| 1 | #include <qpdf/QPDF_Name.hh> | 1 | #include <qpdf/QPDF_Name.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | ||
| 6 | +#include <string_view> | ||
| 7 | + | ||
| 5 | QPDF_Name::QPDF_Name(std::string const& name) : | 8 | QPDF_Name::QPDF_Name(std::string const& name) : |
| 6 | QPDFValue(::ot_name, "name"), | 9 | QPDFValue(::ot_name, "name"), |
| 7 | name(name) | 10 | name(name) |
| @@ -51,20 +54,71 @@ QPDF_Name::unparse() | @@ -51,20 +54,71 @@ QPDF_Name::unparse() | ||
| 51 | return normalizeName(this->name); | 54 | return normalizeName(this->name); |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | -JSON | ||
| 55 | -QPDF_Name::getJSON(int json_version) | 57 | +std::pair<bool, bool> |
| 58 | +QPDF_Name::analyzeJSONEncoding(const std::string& name) | ||
| 59 | +{ | ||
| 60 | + std::basic_string_view<unsigned char> view{ | ||
| 61 | + reinterpret_cast<const unsigned char*>(name.data()), name.size()}; | ||
| 62 | + | ||
| 63 | + int tail = 0; // Number of continuation characters expected. | ||
| 64 | + bool tail2 = false; // Potential overlong 3 octet utf-8. | ||
| 65 | + bool tail3 = false; // potential overlong 4 octet | ||
| 66 | + bool needs_escaping = false; | ||
| 67 | + for (auto const& c: view) { | ||
| 68 | + if (tail) { | ||
| 69 | + if ((c & 0xc0) != 0x80) { | ||
| 70 | + return {false, false}; | ||
| 71 | + } | ||
| 72 | + if (tail2) { | ||
| 73 | + if ((c & 0xe0) == 0x80) { | ||
| 74 | + return {false, false}; | ||
| 75 | + } | ||
| 76 | + tail2 = false; | ||
| 77 | + } else if (tail3) { | ||
| 78 | + if ((c & 0xf0) == 0x80) { | ||
| 79 | + return {false, false}; | ||
| 80 | + } | ||
| 81 | + tail3 = false; | ||
| 82 | + } | ||
| 83 | + tail--; | ||
| 84 | + } else if (c < 0x80) { | ||
| 85 | + if (!needs_escaping) { | ||
| 86 | + needs_escaping = !((c > 34 && c != '\\') || c == ' ' || c == 33); | ||
| 87 | + } | ||
| 88 | + } else if ((c & 0xe0) == 0xc0) { | ||
| 89 | + if ((c & 0xfe) == 0xc0) { | ||
| 90 | + return {false, false}; | ||
| 91 | + } | ||
| 92 | + tail = 1; | ||
| 93 | + } else if ((c & 0xf0) == 0xe0) { | ||
| 94 | + tail2 = (c == 0xe0); | ||
| 95 | + tail = 2; | ||
| 96 | + } else if ((c & 0xf8) == 0xf0) { | ||
| 97 | + tail3 = (c == 0xf0); | ||
| 98 | + tail = 3; | ||
| 99 | + } else { | ||
| 100 | + return {false, false}; | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | + return {tail == 0, !needs_escaping}; | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +void | ||
| 107 | +QPDF_Name::writeJSON(int json_version, JSON::Writer& p) | ||
| 56 | { | 108 | { |
| 109 | + // For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When updating | ||
| 110 | + // this method make sure QPDF_Dictionary is also update. | ||
| 57 | if (json_version == 1) { | 111 | if (json_version == 1) { |
| 58 | - return JSON::makeString(normalizeName(this->name)); | 112 | + p << "\"" << JSON::Writer::encode_string(normalizeName(name)) << "\""; |
| 59 | } else { | 113 | } else { |
| 60 | - bool has_8bit_chars; | ||
| 61 | - bool is_valid_utf8; | ||
| 62 | - bool is_utf16; | ||
| 63 | - QUtil::analyze_encoding(this->name, has_8bit_chars, is_valid_utf8, is_utf16); | ||
| 64 | - if (!has_8bit_chars || is_valid_utf8) { | ||
| 65 | - return JSON::makeString(this->name); | 114 | + if (auto res = analyzeJSONEncoding(name); res.first) { |
| 115 | + if (res.second) { | ||
| 116 | + p << "\"" << name << "\""; | ||
| 117 | + } else { | ||
| 118 | + p << "\"" << JSON::Writer::encode_string(name) << "\""; | ||
| 119 | + } | ||
| 66 | } else { | 120 | } else { |
| 67 | - return JSON::makeString("n:" + normalizeName(this->name)); | 121 | + p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\""; |
| 68 | } | 122 | } |
| 69 | } | 123 | } |
| 70 | } | 124 | } |
libqpdf/QPDF_Null.cc
| 1 | #include <qpdf/QPDF_Null.hh> | 1 | #include <qpdf/QPDF_Null.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QPDFObject_private.hh> | 4 | #include <qpdf/QPDFObject_private.hh> |
| 4 | 5 | ||
| 5 | QPDF_Null::QPDF_Null() : | 6 | QPDF_Null::QPDF_Null() : |
| @@ -43,9 +44,8 @@ QPDF_Null::unparse() | @@ -43,9 +44,8 @@ QPDF_Null::unparse() | ||
| 43 | return "null"; | 44 | return "null"; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | -JSON | ||
| 47 | -QPDF_Null::getJSON(int json_version) | 47 | +void |
| 48 | +QPDF_Null::writeJSON(int json_version, JSON::Writer& p) | ||
| 48 | { | 49 | { |
| 49 | - // If this is updated, QPDF_Array::getJSON must also be updated. | ||
| 50 | - return JSON::makeNull(); | 50 | + p << "null"; |
| 51 | } | 51 | } |
libqpdf/QPDF_Operator.cc
| 1 | #include <qpdf/QPDF_Operator.hh> | 1 | #include <qpdf/QPDF_Operator.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 4 | + | ||
| 3 | QPDF_Operator::QPDF_Operator(std::string const& val) : | 5 | QPDF_Operator::QPDF_Operator(std::string const& val) : |
| 4 | QPDFValue(::ot_operator, "operator"), | 6 | QPDFValue(::ot_operator, "operator"), |
| 5 | val(val) | 7 | val(val) |
| @@ -24,8 +26,8 @@ QPDF_Operator::unparse() | @@ -24,8 +26,8 @@ QPDF_Operator::unparse() | ||
| 24 | return val; | 26 | return val; |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | -JSON | ||
| 28 | -QPDF_Operator::getJSON(int json_version) | 29 | +void |
| 30 | +QPDF_Operator::writeJSON(int json_version, JSON::Writer& p) | ||
| 29 | { | 31 | { |
| 30 | - return JSON::makeNull(); | 32 | + p << "null"; |
| 31 | } | 33 | } |
libqpdf/QPDF_Real.cc
| 1 | #include <qpdf/QPDF_Real.hh> | 1 | #include <qpdf/QPDF_Real.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | ||
| 5 | QPDF_Real::QPDF_Real(std::string const& val) : | 6 | QPDF_Real::QPDF_Real(std::string const& val) : |
| @@ -38,21 +39,17 @@ QPDF_Real::unparse() | @@ -38,21 +39,17 @@ QPDF_Real::unparse() | ||
| 38 | return this->val; | 39 | return this->val; |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | -JSON | ||
| 42 | -QPDF_Real::getJSON(int json_version) | 42 | +void |
| 43 | +QPDF_Real::writeJSON(int json_version, JSON::Writer& p) | ||
| 43 | { | 44 | { |
| 44 | - // While PDF allows .x or -.x, JSON does not. Rather than converting from string to double and | ||
| 45 | - // back, just handle this as a special case for JSON. | ||
| 46 | - std::string result; | ||
| 47 | if (this->val.length() == 0) { | 45 | if (this->val.length() == 0) { |
| 48 | // Can't really happen... | 46 | // Can't really happen... |
| 49 | - result = "0"; | 47 | + p << "0"; |
| 50 | } else if (this->val.at(0) == '.') { | 48 | } else if (this->val.at(0) == '.') { |
| 51 | - result = "0" + this->val; | ||
| 52 | - } else if ((this->val.length() >= 2) && (this->val.at(0) == '-') && (this->val.at(1) == '.')) { | ||
| 53 | - result = "-0." + this->val.substr(2); | 49 | + p << "0" << this->val; |
| 50 | + } else if (this->val.length() >= 2 && this->val.at(0) == '-' && this->val.at(1) == '.') { | ||
| 51 | + p << "-0." << this->val.substr(2); | ||
| 54 | } else { | 52 | } else { |
| 55 | - result = this->val; | 53 | + p << this->val; |
| 56 | } | 54 | } |
| 57 | - return JSON::makeNumber(result); | ||
| 58 | } | 55 | } |
libqpdf/QPDF_Reserved.cc
| @@ -26,9 +26,8 @@ QPDF_Reserved::unparse() | @@ -26,9 +26,8 @@ QPDF_Reserved::unparse() | ||
| 26 | return ""; | 26 | return ""; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | -JSON | ||
| 30 | -QPDF_Reserved::getJSON(int json_version) | 29 | +void |
| 30 | +QPDF_Reserved::writeJSON(int json_version, JSON::Writer& p) | ||
| 31 | { | 31 | { |
| 32 | throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object"); | 32 | throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object"); |
| 33 | - return JSON::makeNull(); | ||
| 34 | } | 33 | } |
libqpdf/QPDF_Stream.cc
| 1 | #include <qpdf/QPDF_Stream.hh> | 1 | #include <qpdf/QPDF_Stream.hh> |
| 2 | 2 | ||
| 3 | #include <qpdf/ContentNormalizer.hh> | 3 | #include <qpdf/ContentNormalizer.hh> |
| 4 | +#include <qpdf/JSON_writer.hh> | ||
| 4 | #include <qpdf/Pipeline.hh> | 5 | #include <qpdf/Pipeline.hh> |
| 5 | #include <qpdf/Pl_Base64.hh> | 6 | #include <qpdf/Pl_Base64.hh> |
| 6 | #include <qpdf/Pl_Buffer.hh> | 7 | #include <qpdf/Pl_Buffer.hh> |
| @@ -176,13 +177,10 @@ QPDF_Stream::unparse() | @@ -176,13 +177,10 @@ QPDF_Stream::unparse() | ||
| 176 | return og.unparse(' ') + " R"; | 177 | return og.unparse(' ') + " R"; |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | -JSON | ||
| 180 | -QPDF_Stream::getJSON(int json_version) | 180 | +void |
| 181 | +QPDF_Stream::writeJSON(int json_version, JSON::Writer& jw) | ||
| 181 | { | 182 | { |
| 182 | - if (json_version == 1) { | ||
| 183 | - return this->stream_dict.getJSON(json_version); | ||
| 184 | - } | ||
| 185 | - return getStreamJSON(json_version, qpdf_sj_none, qpdf_dl_none, nullptr, ""); | 183 | + stream_dict.writeJSON(json_version, jw); |
| 186 | } | 184 | } |
| 187 | 185 | ||
| 188 | JSON | 186 | JSON |
| @@ -193,77 +191,108 @@ QPDF_Stream::getStreamJSON( | @@ -193,77 +191,108 @@ QPDF_Stream::getStreamJSON( | ||
| 193 | Pipeline* p, | 191 | Pipeline* p, |
| 194 | std::string const& data_filename) | 192 | std::string const& data_filename) |
| 195 | { | 193 | { |
| 194 | + Pl_Buffer pb{"streamjson"}; | ||
| 195 | + JSON::Writer jw{&pb, 0}; | ||
| 196 | + decode_level = | ||
| 197 | + writeStreamJSON(json_version, jw, json_data, decode_level, p, data_filename, true); | ||
| 198 | + pb.finish(); | ||
| 199 | + auto result = JSON::parse(pb.getString()); | ||
| 200 | + if (json_data == qpdf_sj_inline) { | ||
| 201 | + result.addDictionaryMember("data", JSON::makeBlob(StreamBlobProvider(this, decode_level))); | ||
| 202 | + } | ||
| 203 | + return result; | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +qpdf_stream_decode_level_e | ||
| 207 | +QPDF_Stream::writeStreamJSON( | ||
| 208 | + int json_version, | ||
| 209 | + JSON::Writer& jw, | ||
| 210 | + qpdf_json_stream_data_e json_data, | ||
| 211 | + qpdf_stream_decode_level_e decode_level, | ||
| 212 | + Pipeline* p, | ||
| 213 | + std::string const& data_filename, | ||
| 214 | + bool no_data_key) | ||
| 215 | +{ | ||
| 196 | switch (json_data) { | 216 | switch (json_data) { |
| 197 | case qpdf_sj_none: | 217 | case qpdf_sj_none: |
| 198 | case qpdf_sj_inline: | 218 | case qpdf_sj_inline: |
| 199 | if (p != nullptr) { | 219 | if (p != nullptr) { |
| 200 | - throw std::logic_error("QPDF_Stream::getStreamJSON: pipeline should only be supplied " | 220 | + throw std::logic_error("QPDF_Stream::writeStreamJSON: pipeline should only be supplied " |
| 201 | "when json_data is file"); | 221 | "when json_data is file"); |
| 202 | } | 222 | } |
| 203 | break; | 223 | break; |
| 204 | case qpdf_sj_file: | 224 | case qpdf_sj_file: |
| 205 | if (p == nullptr) { | 225 | if (p == nullptr) { |
| 206 | throw std::logic_error( | 226 | throw std::logic_error( |
| 207 | - "QPDF_Stream::getStreamJSON: pipeline must be supplied when json_data is file"); | 227 | + "QPDF_Stream::writeStreamJSON: pipeline must be supplied when json_data is file"); |
| 208 | } | 228 | } |
| 209 | if (data_filename.empty()) { | 229 | if (data_filename.empty()) { |
| 210 | - throw std::logic_error("QPDF_Stream::getStreamJSON: data_filename must be supplied " | 230 | + throw std::logic_error("QPDF_Stream::writeStreamJSON: data_filename must be supplied " |
| 211 | "when json_data is file"); | 231 | "when json_data is file"); |
| 212 | } | 232 | } |
| 213 | break; | 233 | break; |
| 214 | } | 234 | } |
| 215 | 235 | ||
| 216 | - auto dict = this->stream_dict; | ||
| 217 | - JSON result = JSON::makeDictionary(); | ||
| 218 | - if (json_data != qpdf_sj_none) { | ||
| 219 | - Pl_Discard discard; | ||
| 220 | - Pl_Buffer buf_pl{"stream data"}; | ||
| 221 | - // buf_pl contains valid data and is ready for retrieval of the data. | ||
| 222 | - bool buf_pl_ready = false; | ||
| 223 | - bool filtered = false; | ||
| 224 | - bool filter = (decode_level != qpdf_dl_none); | ||
| 225 | - for (int attempt = 1; attempt <= 2; ++attempt) { | ||
| 226 | - Pipeline* data_pipeline = &discard; | ||
| 227 | - if (json_data == qpdf_sj_file) { | ||
| 228 | - // We need to capture the data to write | ||
| 229 | - data_pipeline = &buf_pl; | ||
| 230 | - } | ||
| 231 | - bool succeeded = | ||
| 232 | - pipeStreamData(data_pipeline, &filtered, 0, decode_level, false, (attempt == 1)); | ||
| 233 | - if (!succeeded || (filter && !filtered)) { | ||
| 234 | - // Try again | ||
| 235 | - filter = false; | ||
| 236 | - decode_level = qpdf_dl_none; | ||
| 237 | - buf_pl.getString(); // reset buf_pl | ||
| 238 | - } else { | ||
| 239 | - if (json_data == qpdf_sj_file) { | ||
| 240 | - buf_pl_ready = true; | ||
| 241 | - } | ||
| 242 | - break; | ||
| 243 | - } | ||
| 244 | - } | ||
| 245 | - // We can use unsafeShallowCopy because we are only touching top-level keys. | ||
| 246 | - dict = this->stream_dict.unsafeShallowCopy(); | ||
| 247 | - dict.removeKey("/Length"); | ||
| 248 | - if (filter && filtered) { | ||
| 249 | - dict.removeKey("/Filter"); | ||
| 250 | - dict.removeKey("/DecodeParms"); | ||
| 251 | - } | ||
| 252 | - if (json_data == qpdf_sj_file) { | ||
| 253 | - result.addDictionaryMember("datafile", JSON::makeString(data_filename)); | ||
| 254 | - if (!buf_pl_ready) { | ||
| 255 | - throw std::logic_error("QPDF_Stream: failed to get stream data in json file mode"); | ||
| 256 | - } | ||
| 257 | - p->writeString(buf_pl.getString()); | ||
| 258 | - } else if (json_data == qpdf_sj_inline) { | ||
| 259 | - result.addDictionaryMember( | ||
| 260 | - "data", JSON::makeBlob(StreamBlobProvider(this, decode_level))); | 236 | + jw.writeStart('{'); |
| 237 | + | ||
| 238 | + if (json_data == qpdf_sj_none) { | ||
| 239 | + jw.writeNext(); | ||
| 240 | + jw << R"("dict": )"; | ||
| 241 | + stream_dict.writeJSON(json_version, jw); | ||
| 242 | + jw.writeEnd('}'); | ||
| 243 | + return decode_level; | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + Pl_Discard discard; | ||
| 247 | + Pl_Buffer buf_pl{"stream data"}; | ||
| 248 | + Pipeline* data_pipeline = &buf_pl; | ||
| 249 | + if (no_data_key && json_data == qpdf_sj_inline) { | ||
| 250 | + data_pipeline = &discard; | ||
| 251 | + } | ||
| 252 | + // pipeStreamData produced valid data. | ||
| 253 | + bool buf_pl_ready = false; | ||
| 254 | + bool filtered = false; | ||
| 255 | + bool filter = (decode_level != qpdf_dl_none); | ||
| 256 | + for (int attempt = 1; attempt <= 2; ++attempt) { | ||
| 257 | + bool succeeded = | ||
| 258 | + pipeStreamData(data_pipeline, &filtered, 0, decode_level, false, (attempt == 1)); | ||
| 259 | + if (!succeeded || (filter && !filtered)) { | ||
| 260 | + // Try again | ||
| 261 | + filter = false; | ||
| 262 | + decode_level = qpdf_dl_none; | ||
| 263 | + buf_pl.getString(); // reset buf_pl | ||
| 261 | } else { | 264 | } else { |
| 262 | - throw std::logic_error("QPDF_Stream: unexpected value of json_data"); | 265 | + buf_pl_ready = true; |
| 266 | + break; | ||
| 263 | } | 267 | } |
| 264 | } | 268 | } |
| 265 | - result.addDictionaryMember("dict", dict.getJSON(json_version)); | ||
| 266 | - return result; | 269 | + if (!buf_pl_ready) { |
| 270 | + throw std::logic_error("QPDF_Stream: failed to get stream data"); | ||
| 271 | + } | ||
| 272 | + // We can use unsafeShallowCopy because we are only touching top-level keys. | ||
| 273 | + auto dict = stream_dict.unsafeShallowCopy(); | ||
| 274 | + dict.removeKey("/Length"); | ||
| 275 | + if (filter && filtered) { | ||
| 276 | + dict.removeKey("/Filter"); | ||
| 277 | + dict.removeKey("/DecodeParms"); | ||
| 278 | + } | ||
| 279 | + if (json_data == qpdf_sj_file) { | ||
| 280 | + jw.writeNext() << R"("datafile": ")" << JSON::Writer::encode_string(data_filename) << "\""; | ||
| 281 | + p->writeString(buf_pl.getString()); | ||
| 282 | + } else if (json_data == qpdf_sj_inline) { | ||
| 283 | + if (!no_data_key) { | ||
| 284 | + jw.writeNext() << R"("data": ")"; | ||
| 285 | + jw.writeBase64(buf_pl.getString()) << "\""; | ||
| 286 | + } | ||
| 287 | + } else { | ||
| 288 | + throw std::logic_error("QPDF_Stream::writeStreamJSON : unexpected value of json_data"); | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + jw.writeNext() << R"("dict": )"; | ||
| 292 | + dict.writeJSON(json_version, jw); | ||
| 293 | + jw.writeEnd('}'); | ||
| 294 | + | ||
| 295 | + return decode_level; | ||
| 267 | } | 296 | } |
| 268 | 297 | ||
| 269 | void | 298 | void |
libqpdf/QPDF_String.cc
| 1 | #include <qpdf/QPDF_String.hh> | 1 | #include <qpdf/QPDF_String.hh> |
| 2 | 2 | ||
| 3 | +#include <qpdf/JSON_writer.hh> | ||
| 3 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | ||
| 5 | // DO NOT USE ctype -- it is locale dependent for some things, and it's not worth the risk of | 6 | // DO NOT USE ctype -- it is locale dependent for some things, and it's not worth the risk of |
| @@ -45,33 +46,28 @@ QPDF_String::unparse() | @@ -45,33 +46,28 @@ QPDF_String::unparse() | ||
| 45 | return unparse(false); | 46 | return unparse(false); |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | -JSON | ||
| 49 | -QPDF_String::getJSON(int json_version) | 49 | +void |
| 50 | +QPDF_String::writeJSON(int json_version, JSON::Writer& p) | ||
| 50 | { | 51 | { |
| 52 | + auto candidate = getUTF8Val(); | ||
| 51 | if (json_version == 1) { | 53 | if (json_version == 1) { |
| 52 | - return JSON::makeString(getUTF8Val()); | ||
| 53 | - } | ||
| 54 | - // See if we can unambiguously represent as Unicode. | ||
| 55 | - bool is_unicode = false; | ||
| 56 | - std::string result; | ||
| 57 | - std::string candidate = getUTF8Val(); | ||
| 58 | - if (QUtil::is_utf16(this->val) || QUtil::is_explicit_utf8(this->val)) { | ||
| 59 | - is_unicode = true; | ||
| 60 | - result = candidate; | ||
| 61 | - } else if (!useHexString()) { | ||
| 62 | - std::string test; | ||
| 63 | - if (QUtil::utf8_to_pdf_doc(candidate, test, '?') && (test == this->val)) { | ||
| 64 | - // This is a PDF-doc string that can be losslessly encoded as Unicode. | ||
| 65 | - is_unicode = true; | ||
| 66 | - result = candidate; | ||
| 67 | - } | ||
| 68 | - } | ||
| 69 | - if (is_unicode) { | ||
| 70 | - result = "u:" + result; | 54 | + |
| 55 | + p << "\"" << JSON::Writer::encode_string(candidate) << "\""; | ||
| 71 | } else { | 56 | } else { |
| 72 | - result = "b:" + QUtil::hex_encode(this->val); | 57 | + // See if we can unambiguously represent as Unicode. |
| 58 | + if (QUtil::is_utf16(this->val) || QUtil::is_explicit_utf8(this->val)) { | ||
| 59 | + p << "\"u:" << JSON::Writer::encode_string(candidate) <<"\""; | ||
| 60 | + return; | ||
| 61 | + } else if (!useHexString()) { | ||
| 62 | + std::string test; | ||
| 63 | + if (QUtil::utf8_to_pdf_doc(candidate, test, '?') && (test == this->val)) { | ||
| 64 | + // This is a PDF-doc string that can be losslessly encoded as Unicode. | ||
| 65 | + p << "\"u:" << JSON::Writer::encode_string(candidate) <<"\""; | ||
| 66 | + return; | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + p << "\"b:" << QUtil::hex_encode(val) <<"\""; | ||
| 73 | } | 70 | } |
| 74 | - return JSON::makeString(result); | ||
| 75 | } | 71 | } |
| 76 | 72 | ||
| 77 | bool | 73 | bool |
libqpdf/QPDF_Unresolved.cc
| @@ -27,9 +27,8 @@ QPDF_Unresolved::unparse() | @@ -27,9 +27,8 @@ QPDF_Unresolved::unparse() | ||
| 27 | return ""; | 27 | return ""; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | -JSON | ||
| 31 | -QPDF_Unresolved::getJSON(int json_version) | 30 | +void |
| 31 | +QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p) | ||
| 32 | { | 32 | { |
| 33 | throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle"); | 33 | throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle"); |
| 34 | - return JSON::makeNull(); | ||
| 35 | } | 34 | } |
libqpdf/QPDF_json.cc
| 1 | #include <qpdf/QPDF.hh> | 1 | #include <qpdf/QPDF.hh> |
| 2 | 2 | ||
| 3 | #include <qpdf/FileInputSource.hh> | 3 | #include <qpdf/FileInputSource.hh> |
| 4 | +#include <qpdf/JSON_writer.hh> | ||
| 4 | #include <qpdf/Pl_Base64.hh> | 5 | #include <qpdf/Pl_Base64.hh> |
| 5 | #include <qpdf/Pl_StdioFile.hh> | 6 | #include <qpdf/Pl_StdioFile.hh> |
| 6 | #include <qpdf/QIntC.hh> | 7 | #include <qpdf/QIntC.hh> |
| 7 | #include <qpdf/QPDFObject_private.hh> | 8 | #include <qpdf/QPDFObject_private.hh> |
| 8 | #include <qpdf/QPDFValue.hh> | 9 | #include <qpdf/QPDFValue.hh> |
| 9 | #include <qpdf/QPDF_Null.hh> | 10 | #include <qpdf/QPDF_Null.hh> |
| 11 | +#include <qpdf/QPDF_Stream.hh> | ||
| 10 | #include <qpdf/QTC.hh> | 12 | #include <qpdf/QTC.hh> |
| 11 | #include <qpdf/QUtil.hh> | 13 | #include <qpdf/QUtil.hh> |
| 12 | #include <algorithm> | 14 | #include <algorithm> |
| @@ -442,7 +444,9 @@ void | @@ -442,7 +444,9 @@ void | ||
| 442 | QPDF::JSONReactor::replaceObject(QPDFObjectHandle&& replacement, JSON const& value) | 444 | QPDF::JSONReactor::replaceObject(QPDFObjectHandle&& replacement, JSON const& value) |
| 443 | { | 445 | { |
| 444 | if (replacement.isIndirect()) { | 446 | if (replacement.isIndirect()) { |
| 445 | - error(replacement.getParsedOffset(), "the value of an object may not be an indirect object reference"); | 447 | + error( |
| 448 | + replacement.getParsedOffset(), | ||
| 449 | + "the value of an object may not be an indirect object reference"); | ||
| 446 | return; | 450 | return; |
| 447 | } | 451 | } |
| 448 | auto& tos = stack.back(); | 452 | auto& tos = stack.back(); |
| @@ -828,45 +832,20 @@ QPDF::importJSON(std::shared_ptr<InputSource> is, bool must_be_complete) | @@ -828,45 +832,20 @@ QPDF::importJSON(std::shared_ptr<InputSource> is, bool must_be_complete) | ||
| 828 | } | 832 | } |
| 829 | 833 | ||
| 830 | void | 834 | void |
| 831 | -QPDF::writeJSONStream( | 835 | +writeJSONStreamFile( |
| 832 | int version, | 836 | int version, |
| 833 | - Pipeline* p, | ||
| 834 | - bool& first, | ||
| 835 | - std::string const& key, | ||
| 836 | - QPDFObjectHandle& obj, | 837 | + JSON::Writer& jw, |
| 838 | + QPDF_Stream& stream, | ||
| 839 | + int id, | ||
| 837 | qpdf_stream_decode_level_e decode_level, | 840 | qpdf_stream_decode_level_e decode_level, |
| 838 | - qpdf_json_stream_data_e json_stream_data, | ||
| 839 | std::string const& file_prefix) | 841 | std::string const& file_prefix) |
| 840 | { | 842 | { |
| 841 | - Pipeline* stream_p = nullptr; | ||
| 842 | - FILE* f = nullptr; | ||
| 843 | - std::shared_ptr<Pl_StdioFile> f_pl; | ||
| 844 | - std::string filename; | ||
| 845 | - if (json_stream_data == qpdf_sj_file) { | ||
| 846 | - filename = file_prefix + "-" + std::to_string(obj.getObjectID()); | ||
| 847 | - f = QUtil::safe_fopen(filename.c_str(), "wb"); | ||
| 848 | - f_pl = std::make_shared<Pl_StdioFile>("stream data", f); | ||
| 849 | - stream_p = f_pl.get(); | ||
| 850 | - } | ||
| 851 | - auto j = JSON::makeDictionary(); | ||
| 852 | - j.addDictionaryMember( | ||
| 853 | - "stream", obj.getStreamJSON(version, json_stream_data, decode_level, stream_p, filename)); | ||
| 854 | - | ||
| 855 | - JSON::writeDictionaryItem(p, first, key, j, 3); | ||
| 856 | - if (f) { | ||
| 857 | - f_pl->finish(); | ||
| 858 | - f_pl = nullptr; | ||
| 859 | - fclose(f); | ||
| 860 | - } | ||
| 861 | -} | ||
| 862 | - | ||
| 863 | -void | ||
| 864 | -QPDF::writeJSONObject( | ||
| 865 | - int version, Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle& obj) | ||
| 866 | -{ | ||
| 867 | - auto j = JSON::makeDictionary(); | ||
| 868 | - j.addDictionaryMember("value", obj.getJSON(version, true)); | ||
| 869 | - JSON::writeDictionaryItem(p, first, key, j, 3); | 843 | + auto filename = file_prefix + "-" + std::to_string(id); |
| 844 | + auto* f = QUtil::safe_fopen(filename.c_str(), "wb"); | ||
| 845 | + Pl_StdioFile f_pl{"stream data", f}; | ||
| 846 | + stream.writeStreamJSON(version, jw, qpdf_sj_file, decode_level, &f_pl, filename); | ||
| 847 | + f_pl.finish(); | ||
| 848 | + fclose(f); | ||
| 870 | } | 849 | } |
| 871 | 850 | ||
| 872 | void | 851 | void |
| @@ -893,80 +872,75 @@ QPDF::writeJSON( | @@ -893,80 +872,75 @@ QPDF::writeJSON( | ||
| 893 | std::string const& file_prefix, | 872 | std::string const& file_prefix, |
| 894 | std::set<std::string> wanted_objects) | 873 | std::set<std::string> wanted_objects) |
| 895 | { | 874 | { |
| 896 | - int const depth_outer = 1; | ||
| 897 | - int const depth_top = 1; | ||
| 898 | - int const depth_qpdf = 2; | ||
| 899 | - int const depth_qpdf_inner = 3; | ||
| 900 | - | ||
| 901 | if (version != 2) { | 875 | if (version != 2) { |
| 902 | throw std::runtime_error("QPDF::writeJSON: only version 2 is supported"); | 876 | throw std::runtime_error("QPDF::writeJSON: only version 2 is supported"); |
| 903 | } | 877 | } |
| 904 | - bool first = true; | 878 | + JSON::Writer jw{p, 4}; |
| 905 | if (complete) { | 879 | if (complete) { |
| 906 | - JSON::writeDictionaryOpen(p, first, depth_outer); | ||
| 907 | - } else { | ||
| 908 | - first = first_key; | ||
| 909 | - } | ||
| 910 | - JSON::writeDictionaryKey(p, first, "qpdf", depth_top); | ||
| 911 | - bool first_qpdf = true; | ||
| 912 | - JSON::writeArrayOpen(p, first_qpdf, depth_top); | ||
| 913 | - JSON::writeNext(p, first_qpdf, depth_qpdf); | ||
| 914 | - bool first_qpdf_inner = true; | ||
| 915 | - JSON::writeDictionaryOpen(p, first_qpdf_inner, depth_qpdf); | ||
| 916 | - JSON::writeDictionaryItem( | ||
| 917 | - p, first_qpdf_inner, "jsonversion", JSON::makeInt(version), depth_qpdf_inner); | ||
| 918 | - JSON::writeDictionaryItem( | ||
| 919 | - p, first_qpdf_inner, "pdfversion", JSON::makeString(getPDFVersion()), depth_qpdf_inner); | ||
| 920 | - JSON::writeDictionaryItem( | ||
| 921 | - p, | ||
| 922 | - first_qpdf_inner, | ||
| 923 | - "pushedinheritedpageresources", | ||
| 924 | - JSON::makeBool(everPushedInheritedAttributesToPages()), | ||
| 925 | - depth_qpdf_inner); | ||
| 926 | - JSON::writeDictionaryItem( | ||
| 927 | - p, | ||
| 928 | - first_qpdf_inner, | ||
| 929 | - "calledgetallpages", | ||
| 930 | - JSON::makeBool(everCalledGetAllPages()), | ||
| 931 | - depth_qpdf_inner); | ||
| 932 | - JSON::writeDictionaryItem( | ||
| 933 | - p, | ||
| 934 | - first_qpdf_inner, | ||
| 935 | - "maxobjectid", | ||
| 936 | - JSON::makeInt(QIntC::to_longlong(getObjectCount())), | ||
| 937 | - depth_qpdf_inner); | ||
| 938 | - JSON::writeDictionaryClose(p, first_qpdf_inner, depth_qpdf); | ||
| 939 | - JSON::writeNext(p, first_qpdf, depth_qpdf); | ||
| 940 | - JSON::writeDictionaryOpen(p, first_qpdf_inner, depth_qpdf); | 880 | + jw << "{"; |
| 881 | + } else if (!first_key) { | ||
| 882 | + jw << ","; | ||
| 883 | + } | ||
| 884 | + first_key = false; | ||
| 885 | + | ||
| 886 | + /* clang-format off */ | ||
| 887 | + jw << "\n" | ||
| 888 | + " \"qpdf\": [\n" | ||
| 889 | + " {\n" | ||
| 890 | + " \"jsonversion\": " << std::to_string(version) << ",\n" | ||
| 891 | + " \"pdfversion\": \"" << getPDFVersion() << "\",\n" | ||
| 892 | + " \"pushedinheritedpageresources\": " << (everPushedInheritedAttributesToPages() ? "true" : "false") << ",\n" | ||
| 893 | + " \"calledgetallpages\": " << (everCalledGetAllPages() ? "true" : "false") << ",\n" | ||
| 894 | + " \"maxobjectid\": " << std::to_string(getObjectCount()) << "\n" | ||
| 895 | + " },\n" | ||
| 896 | + " {"; | ||
| 897 | + /* clang-format on */ | ||
| 898 | + | ||
| 941 | bool all_objects = wanted_objects.empty(); | 899 | bool all_objects = wanted_objects.empty(); |
| 900 | + bool first = true; | ||
| 942 | for (auto& obj: getAllObjects()) { | 901 | for (auto& obj: getAllObjects()) { |
| 943 | - std::string key = "obj:" + obj.unparse(); | 902 | + auto const og = obj.getObjGen(); |
| 903 | + std::string key = "obj:" + og.unparse(' ') + " R"; | ||
| 944 | if (all_objects || wanted_objects.count(key)) { | 904 | if (all_objects || wanted_objects.count(key)) { |
| 945 | - if (obj.isStream()) { | ||
| 946 | - writeJSONStream( | ||
| 947 | - version, | ||
| 948 | - p, | ||
| 949 | - first_qpdf_inner, | ||
| 950 | - key, | ||
| 951 | - obj, | ||
| 952 | - decode_level, | ||
| 953 | - json_stream_data, | ||
| 954 | - file_prefix); | 905 | + if (first) { |
| 906 | + jw << "\n \"" << key; | ||
| 907 | + first = false; | ||
| 955 | } else { | 908 | } else { |
| 956 | - writeJSONObject(version, p, first_qpdf_inner, key, obj); | 909 | + jw << "\n },\n \"" << key; |
| 910 | + } | ||
| 911 | + if (auto* stream = obj.getObjectPtr()->as<QPDF_Stream>()) { | ||
| 912 | + jw << "\": {\n \"stream\": "; | ||
| 913 | + if (json_stream_data == qpdf_sj_file) { | ||
| 914 | + writeJSONStreamFile( | ||
| 915 | + version, jw, *stream, og.getObj(), decode_level, file_prefix); | ||
| 916 | + } else { | ||
| 917 | + stream->writeStreamJSON( | ||
| 918 | + version, jw, json_stream_data, decode_level, nullptr, ""); | ||
| 919 | + } | ||
| 920 | + } else { | ||
| 921 | + jw << "\": {\n \"value\": "; | ||
| 922 | + obj.writeJSON(version, jw, true); | ||
| 957 | } | 923 | } |
| 958 | } | 924 | } |
| 959 | } | 925 | } |
| 960 | if (all_objects || wanted_objects.count("trailer")) { | 926 | if (all_objects || wanted_objects.count("trailer")) { |
| 961 | - auto trailer = getTrailer(); | ||
| 962 | - writeJSONObject(version, p, first_qpdf_inner, "trailer", trailer); | ||
| 963 | - } | ||
| 964 | - JSON::writeDictionaryClose(p, first_qpdf_inner, depth_qpdf); | ||
| 965 | - JSON::writeArrayClose(p, first_qpdf, depth_top); | 927 | + if (!first) { |
| 928 | + jw << "\n },"; | ||
| 929 | + } | ||
| 930 | + jw << "\n \"trailer\": {\n \"value\": "; | ||
| 931 | + getTrailer().writeJSON(version, jw, true); | ||
| 932 | + first = false; | ||
| 933 | + } | ||
| 934 | + if (!first) { | ||
| 935 | + jw << "\n }"; | ||
| 936 | + } | ||
| 937 | + /* clang-format off */ | ||
| 938 | + jw << "\n" | ||
| 939 | + " }\n" | ||
| 940 | + " ]"; | ||
| 941 | + /* clang-format on */ | ||
| 966 | if (complete) { | 942 | if (complete) { |
| 967 | - JSON::writeDictionaryClose(p, first, 0); | ||
| 968 | - *p << "\n"; | 943 | + jw << "\n}\n"; |
| 969 | p->finish(); | 944 | p->finish(); |
| 970 | } | 945 | } |
| 971 | - first_key = false; | ||
| 972 | } | 946 | } |
libqpdf/qpdf/JSON_writer.hh
0 โ 100644
| 1 | +#ifndef JSON_WRITER_HH | ||
| 2 | +#define JSON_WRITER_HH | ||
| 3 | + | ||
| 4 | +#include <qpdf/JSON.hh> | ||
| 5 | +#include <qpdf/Pipeline.hh> | ||
| 6 | +#include <qpdf/Pl_Base64.hh> | ||
| 7 | +#include <qpdf/Pl_Concatenate.hh> | ||
| 8 | + | ||
| 9 | +#include <string_view> | ||
| 10 | + | ||
| 11 | +// Writer is a small utility class to aid writing JSON to a pipeline. Methods are designed to allow | ||
| 12 | +// chaining of calls. | ||
| 13 | +// | ||
| 14 | +// Some uses of the class have a significant performance impact. The class is intended purely for | ||
| 15 | +// internal use to allow it to be adapted as needed to maintain performance. | ||
| 16 | +class JSON::Writer | ||
| 17 | +{ | ||
| 18 | + public: | ||
| 19 | + Writer(Pipeline* p, size_t depth) : | ||
| 20 | + p(p), | ||
| 21 | + indent(2 * depth) | ||
| 22 | + { | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + Writer& | ||
| 26 | + write(char const* data, size_t len) | ||
| 27 | + { | ||
| 28 | + p->write(reinterpret_cast<unsigned char const*>(data), len); | ||
| 29 | + return *this; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + Writer& | ||
| 33 | + writeBase64(std::string_view sv) | ||
| 34 | + { | ||
| 35 | + Pl_Concatenate cat{"writer concat", p}; | ||
| 36 | + Pl_Base64 base{"writer base64", &cat, Pl_Base64::a_encode}; | ||
| 37 | + base.write(reinterpret_cast<unsigned char const*>(sv.data()), sv.size()); | ||
| 38 | + base.finish(); | ||
| 39 | + return *this; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + Writer& | ||
| 43 | + writeNext() | ||
| 44 | + { | ||
| 45 | + auto n = indent; | ||
| 46 | + if (first) { | ||
| 47 | + first = false; | ||
| 48 | + write(&spaces[1], n % n_spaces + 1); | ||
| 49 | + } else { | ||
| 50 | + write(&spaces[0], n % n_spaces + 2); | ||
| 51 | + } | ||
| 52 | + while (n >= n_spaces) { | ||
| 53 | + write(&spaces[2], n_spaces); | ||
| 54 | + n -= n_spaces; | ||
| 55 | + } | ||
| 56 | + return *this; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + Writer& | ||
| 60 | + writeStart(char const& c) | ||
| 61 | + { | ||
| 62 | + write(&c, 1); | ||
| 63 | + first = true; | ||
| 64 | + indent += 2; | ||
| 65 | + return *this; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + Writer& | ||
| 69 | + writeEnd(char const& c) | ||
| 70 | + { | ||
| 71 | + if (indent > 1) { | ||
| 72 | + indent -= 2; | ||
| 73 | + } | ||
| 74 | + if (!first) { | ||
| 75 | + first = true; | ||
| 76 | + writeNext(); | ||
| 77 | + } | ||
| 78 | + first = false; | ||
| 79 | + write(&c, 1); | ||
| 80 | + return *this; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + Writer& | ||
| 84 | + operator<<(std::string_view sv) | ||
| 85 | + { | ||
| 86 | + p->write(reinterpret_cast<unsigned char const*>(sv.data()), sv.size()); | ||
| 87 | + return *this; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + Writer& | ||
| 91 | + operator<<(char const* s) | ||
| 92 | + { | ||
| 93 | + *this << std::string_view{s}; | ||
| 94 | + return *this; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + Writer& | ||
| 98 | + operator<<(bool val) | ||
| 99 | + { | ||
| 100 | + *this << (val ? "true" : "false"); | ||
| 101 | + return *this; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + Writer& | ||
| 105 | + operator<<(int val) | ||
| 106 | + { | ||
| 107 | + *this << std::to_string(val); | ||
| 108 | + return *this; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + Writer& | ||
| 112 | + operator<<(size_t val) | ||
| 113 | + { | ||
| 114 | + *this << std::to_string(val); | ||
| 115 | + return *this; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + Writer& | ||
| 119 | + operator<<(JSON&& j) | ||
| 120 | + { | ||
| 121 | + j.write(p, indent / 2); | ||
| 122 | + return *this; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + static std::string encode_string(std::string const& utf8); | ||
| 126 | + | ||
| 127 | + private: | ||
| 128 | + Pipeline* p; | ||
| 129 | + bool first{true}; | ||
| 130 | + size_t indent; | ||
| 131 | + | ||
| 132 | + static constexpr std::string_view spaces = | ||
| 133 | + ",\n "; | ||
| 134 | + static constexpr auto n_spaces = spaces.size() - 2; | ||
| 135 | +}; | ||
| 136 | + | ||
| 137 | +#endif // JSON_WRITER_HH |
libqpdf/qpdf/QPDFObject_private.hh
| @@ -33,10 +33,10 @@ class QPDFObject | @@ -33,10 +33,10 @@ class QPDFObject | ||
| 33 | { | 33 | { |
| 34 | return value->unparse(); | 34 | return value->unparse(); |
| 35 | } | 35 | } |
| 36 | - JSON | ||
| 37 | - getJSON(int json_version) | 36 | + void |
| 37 | + writeJSON(int json_version, JSON::Writer& p) | ||
| 38 | { | 38 | { |
| 39 | - return value->getJSON(json_version); | 39 | + return value->writeJSON(json_version, p); |
| 40 | } | 40 | } |
| 41 | std::string | 41 | std::string |
| 42 | getStringValue() const | 42 | getStringValue() const |
libqpdf/qpdf/QPDFValue.hh
| @@ -24,7 +24,7 @@ class QPDFValue: public std::enable_shared_from_this<QPDFValue> | @@ -24,7 +24,7 @@ class QPDFValue: public std::enable_shared_from_this<QPDFValue> | ||
| 24 | 24 | ||
| 25 | virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0; | 25 | virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0; |
| 26 | virtual std::string unparse() = 0; | 26 | virtual std::string unparse() = 0; |
| 27 | - virtual JSON getJSON(int json_version) = 0; | 27 | + virtual void writeJSON(int json_version, JSON::Writer& p) = 0; |
| 28 | 28 | ||
| 29 | struct JSON_Descr | 29 | struct JSON_Descr |
| 30 | { | 30 | { |
libqpdf/qpdf/QPDF_Array.hh
| @@ -22,7 +22,7 @@ class QPDF_Array: public QPDFValue | @@ -22,7 +22,7 @@ class QPDF_Array: public QPDFValue | ||
| 22 | create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); | 22 | create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); |
| 23 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 23 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 24 | std::string unparse() override; | 24 | std::string unparse() override; |
| 25 | - JSON getJSON(int json_version) override; | 25 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 26 | void disconnect() override; | 26 | void disconnect() override; |
| 27 | 27 | ||
| 28 | int | 28 | int |
libqpdf/qpdf/QPDF_Bool.hh
| @@ -10,7 +10,8 @@ class QPDF_Bool: public QPDFValue | @@ -10,7 +10,8 @@ class QPDF_Bool: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(bool val); | 10 | static std::shared_ptr<QPDFObject> create(bool val); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | + | ||
| 14 | bool getVal() const; | 15 | bool getVal() const; |
| 15 | 16 | ||
| 16 | private: | 17 | private: |
libqpdf/qpdf/QPDF_Destroyed.hh
| @@ -9,7 +9,7 @@ class QPDF_Destroyed: public QPDFValue | @@ -9,7 +9,7 @@ class QPDF_Destroyed: public QPDFValue | ||
| 9 | ~QPDF_Destroyed() override = default; | 9 | ~QPDF_Destroyed() override = default; |
| 10 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 10 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 11 | std::string unparse() override; | 11 | std::string unparse() override; |
| 12 | - JSON getJSON(int json_version) override; | 12 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 13 | static std::shared_ptr<QPDFValue> getInstance(); | 13 | static std::shared_ptr<QPDFValue> getInstance(); |
| 14 | 14 | ||
| 15 | private: | 15 | private: |
libqpdf/qpdf/QPDF_Dictionary.hh
| @@ -16,7 +16,7 @@ class QPDF_Dictionary: public QPDFValue | @@ -16,7 +16,7 @@ class QPDF_Dictionary: public QPDFValue | ||
| 16 | static std::shared_ptr<QPDFObject> create(std::map<std::string, QPDFObjectHandle>&& items); | 16 | static std::shared_ptr<QPDFObject> create(std::map<std::string, QPDFObjectHandle>&& items); |
| 17 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 17 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 18 | std::string unparse() override; | 18 | std::string unparse() override; |
| 19 | - JSON getJSON(int json_version) override; | 19 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 20 | void disconnect() override; | 20 | void disconnect() override; |
| 21 | 21 | ||
| 22 | // hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns | 22 | // hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns |
libqpdf/qpdf/QPDF_InlineImage.hh
| @@ -10,7 +10,7 @@ class QPDF_InlineImage: public QPDFValue | @@ -10,7 +10,7 @@ class QPDF_InlineImage: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(std::string const& val); | 10 | static std::shared_ptr<QPDFObject> create(std::string const& val); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | std::string | 14 | std::string |
| 15 | getStringValue() const override | 15 | getStringValue() const override |
| 16 | { | 16 | { |
libqpdf/qpdf/QPDF_Integer.hh
| @@ -10,7 +10,7 @@ class QPDF_Integer: public QPDFValue | @@ -10,7 +10,7 @@ class QPDF_Integer: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(long long value); | 10 | static std::shared_ptr<QPDFObject> create(long long value); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | long long getVal() const; | 14 | long long getVal() const; |
| 15 | 15 | ||
| 16 | private: | 16 | private: |
libqpdf/qpdf/QPDF_Name.hh
| @@ -10,10 +10,15 @@ class QPDF_Name: public QPDFValue | @@ -10,10 +10,15 @@ class QPDF_Name: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(std::string const& name); | 10 | static std::shared_ptr<QPDFObject> create(std::string const& name); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | 14 | ||
| 15 | // Put # into strings with characters unsuitable for name token | 15 | // Put # into strings with characters unsuitable for name token |
| 16 | static std::string normalizeName(std::string const& name); | 16 | static std::string normalizeName(std::string const& name); |
| 17 | + | ||
| 18 | + // Check whether name is valid utf-8 and whether it contains characters that require escaping. | ||
| 19 | + // Return {false, false} if the name is not valid utf-8, otherwise return {true, true} if no | ||
| 20 | + // characters require or {true, false} if escaping is required. | ||
| 21 | + static std::pair<bool, bool> analyzeJSONEncoding(std::string const& name); | ||
| 17 | std::string | 22 | std::string |
| 18 | getStringValue() const override | 23 | getStringValue() const override |
| 19 | { | 24 | { |
libqpdf/qpdf/QPDF_Null.hh
| @@ -18,7 +18,7 @@ class QPDF_Null: public QPDFValue | @@ -18,7 +18,7 @@ class QPDF_Null: public QPDFValue | ||
| 18 | std::string var_descr); | 18 | std::string var_descr); |
| 19 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 19 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 20 | std::string unparse() override; | 20 | std::string unparse() override; |
| 21 | - JSON getJSON(int json_version) override; | 21 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 22 | 22 | ||
| 23 | private: | 23 | private: |
| 24 | QPDF_Null(); | 24 | QPDF_Null(); |
libqpdf/qpdf/QPDF_Operator.hh
| @@ -10,7 +10,7 @@ class QPDF_Operator: public QPDFValue | @@ -10,7 +10,7 @@ class QPDF_Operator: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(std::string const& val); | 10 | static std::shared_ptr<QPDFObject> create(std::string const& val); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | std::string | 14 | std::string |
| 15 | getStringValue() const override | 15 | getStringValue() const override |
| 16 | { | 16 | { |
libqpdf/qpdf/QPDF_Real.hh
| @@ -12,7 +12,7 @@ class QPDF_Real: public QPDFValue | @@ -12,7 +12,7 @@ class QPDF_Real: public QPDFValue | ||
| 12 | create(double value, int decimal_places, bool trim_trailing_zeroes); | 12 | create(double value, int decimal_places, bool trim_trailing_zeroes); |
| 13 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 13 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 14 | std::string unparse() override; | 14 | std::string unparse() override; |
| 15 | - JSON getJSON(int json_version) override; | 15 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 16 | std::string | 16 | std::string |
| 17 | getStringValue() const override | 17 | getStringValue() const override |
| 18 | { | 18 | { |
libqpdf/qpdf/QPDF_Reserved.hh
| @@ -10,7 +10,7 @@ class QPDF_Reserved: public QPDFValue | @@ -10,7 +10,7 @@ class QPDF_Reserved: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(); | 10 | static std::shared_ptr<QPDFObject> create(); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | 14 | ||
| 15 | private: | 15 | private: |
| 16 | QPDF_Reserved(); | 16 | QPDF_Reserved(); |
libqpdf/qpdf/QPDF_Stream.hh
| @@ -25,7 +25,7 @@ class QPDF_Stream: public QPDFValue | @@ -25,7 +25,7 @@ class QPDF_Stream: public QPDFValue | ||
| 25 | size_t length); | 25 | size_t length); |
| 26 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 26 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 27 | std::string unparse() override; | 27 | std::string unparse() override; |
| 28 | - JSON getJSON(int json_version) override; | 28 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 29 | void setDescription( | 29 | void setDescription( |
| 30 | QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) override; | 30 | QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) override; |
| 31 | void disconnect() override; | 31 | void disconnect() override; |
| @@ -64,6 +64,14 @@ class QPDF_Stream: public QPDFValue | @@ -64,6 +64,14 @@ class QPDF_Stream: public QPDFValue | ||
| 64 | qpdf_stream_decode_level_e decode_level, | 64 | qpdf_stream_decode_level_e decode_level, |
| 65 | Pipeline* p, | 65 | Pipeline* p, |
| 66 | std::string const& data_filename); | 66 | std::string const& data_filename); |
| 67 | + qpdf_stream_decode_level_e writeStreamJSON( | ||
| 68 | + int json_version, | ||
| 69 | + JSON::Writer& jw, | ||
| 70 | + qpdf_json_stream_data_e json_data, | ||
| 71 | + qpdf_stream_decode_level_e decode_level, | ||
| 72 | + Pipeline* p, | ||
| 73 | + std::string const& data_filename, | ||
| 74 | + bool no_data_key = false); | ||
| 67 | 75 | ||
| 68 | void replaceDict(QPDFObjectHandle const& new_dict); | 76 | void replaceDict(QPDFObjectHandle const& new_dict); |
| 69 | 77 |
libqpdf/qpdf/QPDF_String.hh
| @@ -16,7 +16,7 @@ class QPDF_String: public QPDFValue | @@ -16,7 +16,7 @@ class QPDF_String: public QPDFValue | ||
| 16 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 16 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 17 | std::string unparse() override; | 17 | std::string unparse() override; |
| 18 | std::string unparse(bool force_binary); | 18 | std::string unparse(bool force_binary); |
| 19 | - JSON getJSON(int json_version) override; | 19 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 20 | std::string getUTF8Val() const; | 20 | std::string getUTF8Val() const; |
| 21 | std::string | 21 | std::string |
| 22 | getStringValue() const override | 22 | getStringValue() const override |
libqpdf/qpdf/QPDF_Unresolved.hh
| @@ -10,7 +10,7 @@ class QPDF_Unresolved: public QPDFValue | @@ -10,7 +10,7 @@ class QPDF_Unresolved: public QPDFValue | ||
| 10 | static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen const& og); | 10 | static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen const& og); |
| 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | std::string unparse() override; | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | 13 | + void writeJSON(int json_version, JSON::Writer& p) override; |
| 14 | 14 | ||
| 15 | private: | 15 | private: |
| 16 | QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og); | 16 | QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og); |
qpdf/qtest/many-nulls.test
| @@ -33,5 +33,22 @@ $td->runtest("copy sparse array", | @@ -33,5 +33,22 @@ $td->runtest("copy sparse array", | ||
| 33 | {$td->COMMAND => "test_driver 97 many-nulls.pdf"}, | 33 | {$td->COMMAND => "test_driver 97 many-nulls.pdf"}, |
| 34 | {$td->STRING => "test 97 done\n", $td->EXIT_STATUS => 0}, | 34 | {$td->STRING => "test 97 done\n", $td->EXIT_STATUS => 0}, |
| 35 | $td->NORMALIZE_NEWLINES); | 35 | $td->NORMALIZE_NEWLINES); |
| 36 | +$td->runtest("copy file with many nulls", | ||
| 37 | + {$td->COMMAND => | ||
| 38 | + "qpdf minimal-nulls.pdf --qdf --static-id --no-original-object-ids a.pdf"}, | ||
| 39 | + {$td->STRING => "", $td->EXIT_STATUS => 0}, | ||
| 40 | + $td->NORMALIZE_NEWLINES); | ||
| 41 | +$td->runtest("compare files", | ||
| 42 | + {$td->FILE => "a.pdf"}, | ||
| 43 | + {$td->FILE => "minimal-nulls.pdf"}); | ||
| 44 | +$td->runtest("file with many nulls to JSON v1", | ||
| 45 | + {$td->COMMAND => "qpdf minimal-nulls.pdf --json=1 -"}, | ||
| 46 | + {$td->FILE => "minimal-nulls-1.json", $td->EXIT_STATUS => 0}, | ||
| 47 | + $td->NORMALIZE_NEWLINES); | ||
| 48 | +$td->runtest("file with many nulls to JSON v2", | ||
| 49 | + {$td->COMMAND => "qpdf minimal-nulls.pdf --json=2 -"}, | ||
| 50 | + {$td->FILE => "minimal-nulls-2.json", $td->EXIT_STATUS => 0}, | ||
| 51 | + $td->NORMALIZE_NEWLINES); | ||
| 52 | + | ||
| 36 | cleanup(); | 53 | cleanup(); |
| 37 | -$td->report(4); | 54 | +$td->report(8); |
qpdf/qtest/qpdf-json.test
| @@ -350,7 +350,7 @@ $td->runtest("check C API write to JSON stream", | @@ -350,7 +350,7 @@ $td->runtest("check C API write to JSON stream", | ||
| 350 | # (using #xx) would generate invalid JSON, even though qpdf's own JSON | 350 | # (using #xx) would generate invalid JSON, even though qpdf's own JSON |
| 351 | # parser would accept it. Also, the JSON spec allows real numbers in | 351 | # parser would accept it. Also, the JSON spec allows real numbers in |
| 352 | # scientific notation, but the PDF spec does not. | 352 | # scientific notation, but the PDF spec does not. |
| 353 | -$n_tests += 4; | 353 | +$n_tests += 7; |
| 354 | $td->runtest("handle binary names", | 354 | $td->runtest("handle binary names", |
| 355 | {$td->COMMAND => | 355 | {$td->COMMAND => |
| 356 | "qpdf --json-output weird-tokens.pdf a.json"}, | 356 | "qpdf --json-output weird-tokens.pdf a.json"}, |
| @@ -371,6 +371,17 @@ $td->runtest("weird tokens with scientific notation", | @@ -371,6 +371,17 @@ $td->runtest("weird tokens with scientific notation", | ||
| 371 | "qpdf --json-input --json-output weird-tokens-alt.json -"}, | 371 | "qpdf --json-input --json-output weird-tokens-alt.json -"}, |
| 372 | {$td->FILE => "weird-tokens.json", $td->EXIT_STATUS => 0}, | 372 | {$td->FILE => "weird-tokens.json", $td->EXIT_STATUS => 0}, |
| 373 | $td->NORMALIZE_NEWLINES); | 373 | $td->NORMALIZE_NEWLINES); |
| 374 | - | 374 | +$td->runtest("handle binary names (JSON v1)", |
| 375 | + {$td->COMMAND => | ||
| 376 | + "qpdf --json=1 weird-tokens.pdf a.json"}, | ||
| 377 | + {$td->STRING => "", $td->EXIT_STATUS => 0}); | ||
| 378 | +$td->runtest("check json", | ||
| 379 | + {$td->FILE => "a.json"}, | ||
| 380 | + {$td->FILE => "weird-tokens-v1.json"}, | ||
| 381 | + $td->NORMALIZE_NEWLINES); | ||
| 382 | +$td->runtest("write JSON to pipeline", | ||
| 383 | + {$td->COMMAND => "test_driver 98 minimal.pdf ''"}, | ||
| 384 | + {$td->STRING => "test 98 done\n", $td->EXIT_STATUS => 0}, | ||
| 385 | + $td->NORMALIZE_NEWLINES); | ||
| 375 | cleanup(); | 386 | cleanup(); |
| 376 | $td->report($n_tests); | 387 | $td->report($n_tests); |
qpdf/qtest/qpdf/minimal-nulls-1.json
0 โ 100644
| 1 | +{ | ||
| 2 | + "version": 1, | ||
| 3 | + "parameters": { | ||
| 4 | + "decodelevel": "generalized" | ||
| 5 | + }, | ||
| 6 | + "pages": [ | ||
| 7 | + { | ||
| 8 | + "contents": [ | ||
| 9 | + "4 0 R" | ||
| 10 | + ], | ||
| 11 | + "images": [], | ||
| 12 | + "label": null, | ||
| 13 | + "object": "3 0 R", | ||
| 14 | + "outlines": [], | ||
| 15 | + "pageposfrom1": 1 | ||
| 16 | + } | ||
| 17 | + ], | ||
| 18 | + "pagelabels": [], | ||
| 19 | + "acroform": { | ||
| 20 | + "fields": [], | ||
| 21 | + "hasacroform": false, | ||
| 22 | + "needappearances": false | ||
| 23 | + }, | ||
| 24 | + "attachments": {}, | ||
| 25 | + "encrypt": { | ||
| 26 | + "capabilities": { | ||
| 27 | + "accessibility": true, | ||
| 28 | + "extract": true, | ||
| 29 | + "moddifyannotations": true, | ||
| 30 | + "modify": true, | ||
| 31 | + "modifyassembly": true, | ||
| 32 | + "modifyforms": true, | ||
| 33 | + "modifyother": true, | ||
| 34 | + "printhigh": true, | ||
| 35 | + "printlow": true | ||
| 36 | + }, | ||
| 37 | + "encrypted": false, | ||
| 38 | + "ownerpasswordmatched": false, | ||
| 39 | + "parameters": { | ||
| 40 | + "P": 0, | ||
| 41 | + "R": 0, | ||
| 42 | + "V": 0, | ||
| 43 | + "bits": 0, | ||
| 44 | + "filemethod": "none", | ||
| 45 | + "key": null, | ||
| 46 | + "method": "none", | ||
| 47 | + "streammethod": "none", | ||
| 48 | + "stringmethod": "none" | ||
| 49 | + }, | ||
| 50 | + "recovereduserpassword": null, | ||
| 51 | + "userpasswordmatched": false | ||
| 52 | + }, | ||
| 53 | + "outlines": [], | ||
| 54 | + "objects": { | ||
| 55 | + "1 0 R": { | ||
| 56 | + "/Pages": "2 0 R", | ||
| 57 | + "/Type": "/Catalog" | ||
| 58 | + }, | ||
| 59 | + "2 0 R": { | ||
| 60 | + "/Count": 1, | ||
| 61 | + "/Kids": [ | ||
| 62 | + "3 0 R" | ||
| 63 | + ], | ||
| 64 | + "/Type": "/Pages" | ||
| 65 | + }, | ||
| 66 | + "3 0 R": { | ||
| 67 | + "/Contents": "4 0 R", | ||
| 68 | + "/MediaBox": [ | ||
| 69 | + 0, | ||
| 70 | + 0, | ||
| 71 | + 612, | ||
| 72 | + 792 | ||
| 73 | + ], | ||
| 74 | + "/Nulls": [ | ||
| 75 | + null, | ||
| 76 | + null, | ||
| 77 | + null, | ||
| 78 | + null, | ||
| 79 | + null, | ||
| 80 | + null, | ||
| 81 | + null, | ||
| 82 | + null, | ||
| 83 | + null, | ||
| 84 | + null, | ||
| 85 | + "6 0 R", | ||
| 86 | + null, | ||
| 87 | + null, | ||
| 88 | + null, | ||
| 89 | + null, | ||
| 90 | + null, | ||
| 91 | + null, | ||
| 92 | + null, | ||
| 93 | + null, | ||
| 94 | + null, | ||
| 95 | + null, | ||
| 96 | + 10, | ||
| 97 | + null, | ||
| 98 | + null, | ||
| 99 | + null, | ||
| 100 | + null, | ||
| 101 | + null, | ||
| 102 | + null, | ||
| 103 | + null, | ||
| 104 | + null, | ||
| 105 | + null, | ||
| 106 | + null, | ||
| 107 | + 10, | ||
| 108 | + null, | ||
| 109 | + null, | ||
| 110 | + null, | ||
| 111 | + null, | ||
| 112 | + null, | ||
| 113 | + null, | ||
| 114 | + null, | ||
| 115 | + null, | ||
| 116 | + null, | ||
| 117 | + null, | ||
| 118 | + 10, | ||
| 119 | + null, | ||
| 120 | + null, | ||
| 121 | + null, | ||
| 122 | + null, | ||
| 123 | + null, | ||
| 124 | + null, | ||
| 125 | + null, | ||
| 126 | + null, | ||
| 127 | + null, | ||
| 128 | + null, | ||
| 129 | + 10, | ||
| 130 | + null, | ||
| 131 | + null, | ||
| 132 | + null, | ||
| 133 | + null, | ||
| 134 | + null, | ||
| 135 | + null, | ||
| 136 | + null, | ||
| 137 | + null, | ||
| 138 | + null, | ||
| 139 | + null, | ||
| 140 | + 10, | ||
| 141 | + null, | ||
| 142 | + null, | ||
| 143 | + null, | ||
| 144 | + null, | ||
| 145 | + null, | ||
| 146 | + null, | ||
| 147 | + null, | ||
| 148 | + null, | ||
| 149 | + null, | ||
| 150 | + null, | ||
| 151 | + 10, | ||
| 152 | + null, | ||
| 153 | + null, | ||
| 154 | + null, | ||
| 155 | + null, | ||
| 156 | + null, | ||
| 157 | + null, | ||
| 158 | + null, | ||
| 159 | + null, | ||
| 160 | + null, | ||
| 161 | + null, | ||
| 162 | + 10, | ||
| 163 | + null, | ||
| 164 | + null, | ||
| 165 | + null, | ||
| 166 | + null, | ||
| 167 | + null, | ||
| 168 | + null, | ||
| 169 | + null, | ||
| 170 | + null, | ||
| 171 | + null, | ||
| 172 | + null, | ||
| 173 | + 10, | ||
| 174 | + null, | ||
| 175 | + null, | ||
| 176 | + null, | ||
| 177 | + null, | ||
| 178 | + null, | ||
| 179 | + null, | ||
| 180 | + null, | ||
| 181 | + null, | ||
| 182 | + null, | ||
| 183 | + null, | ||
| 184 | + 10, | ||
| 185 | + null, | ||
| 186 | + null, | ||
| 187 | + null, | ||
| 188 | + null, | ||
| 189 | + null, | ||
| 190 | + null, | ||
| 191 | + null, | ||
| 192 | + null, | ||
| 193 | + null, | ||
| 194 | + null, | ||
| 195 | + 10, | ||
| 196 | + null, | ||
| 197 | + null, | ||
| 198 | + null, | ||
| 199 | + null, | ||
| 200 | + null, | ||
| 201 | + null, | ||
| 202 | + null, | ||
| 203 | + null, | ||
| 204 | + null, | ||
| 205 | + null, | ||
| 206 | + 10, | ||
| 207 | + null, | ||
| 208 | + null, | ||
| 209 | + null, | ||
| 210 | + null, | ||
| 211 | + null, | ||
| 212 | + null, | ||
| 213 | + null, | ||
| 214 | + null, | ||
| 215 | + null, | ||
| 216 | + null, | ||
| 217 | + 10, | ||
| 218 | + null, | ||
| 219 | + null, | ||
| 220 | + null, | ||
| 221 | + null, | ||
| 222 | + null, | ||
| 223 | + null, | ||
| 224 | + null, | ||
| 225 | + null, | ||
| 226 | + null, | ||
| 227 | + null, | ||
| 228 | + 10, | ||
| 229 | + null, | ||
| 230 | + null, | ||
| 231 | + null, | ||
| 232 | + null, | ||
| 233 | + null, | ||
| 234 | + null, | ||
| 235 | + null, | ||
| 236 | + null, | ||
| 237 | + null, | ||
| 238 | + null, | ||
| 239 | + 10, | ||
| 240 | + null, | ||
| 241 | + null, | ||
| 242 | + null, | ||
| 243 | + null, | ||
| 244 | + null, | ||
| 245 | + null, | ||
| 246 | + null, | ||
| 247 | + null, | ||
| 248 | + null, | ||
| 249 | + null, | ||
| 250 | + 10, | ||
| 251 | + null, | ||
| 252 | + null, | ||
| 253 | + null, | ||
| 254 | + null, | ||
| 255 | + null, | ||
| 256 | + null, | ||
| 257 | + null, | ||
| 258 | + null, | ||
| 259 | + null, | ||
| 260 | + null, | ||
| 261 | + 10, | ||
| 262 | + null, | ||
| 263 | + null, | ||
| 264 | + null, | ||
| 265 | + null, | ||
| 266 | + null, | ||
| 267 | + null, | ||
| 268 | + null, | ||
| 269 | + null, | ||
| 270 | + null, | ||
| 271 | + null, | ||
| 272 | + 10, | ||
| 273 | + null, | ||
| 274 | + null, | ||
| 275 | + null, | ||
| 276 | + null, | ||
| 277 | + null, | ||
| 278 | + null, | ||
| 279 | + null, | ||
| 280 | + null, | ||
| 281 | + null, | ||
| 282 | + null, | ||
| 283 | + 10, | ||
| 284 | + null, | ||
| 285 | + null, | ||
| 286 | + null, | ||
| 287 | + null, | ||
| 288 | + null, | ||
| 289 | + null, | ||
| 290 | + null, | ||
| 291 | + null, | ||
| 292 | + null, | ||
| 293 | + null, | ||
| 294 | + 10, | ||
| 295 | + null, | ||
| 296 | + null, | ||
| 297 | + null, | ||
| 298 | + null, | ||
| 299 | + null, | ||
| 300 | + null, | ||
| 301 | + null, | ||
| 302 | + null, | ||
| 303 | + null, | ||
| 304 | + null, | ||
| 305 | + 10, | ||
| 306 | + null, | ||
| 307 | + null, | ||
| 308 | + null, | ||
| 309 | + null, | ||
| 310 | + null, | ||
| 311 | + null, | ||
| 312 | + null, | ||
| 313 | + null, | ||
| 314 | + null, | ||
| 315 | + null, | ||
| 316 | + 10, | ||
| 317 | + null, | ||
| 318 | + null, | ||
| 319 | + null, | ||
| 320 | + null, | ||
| 321 | + null, | ||
| 322 | + null, | ||
| 323 | + null, | ||
| 324 | + null, | ||
| 325 | + null, | ||
| 326 | + null, | ||
| 327 | + 10, | ||
| 328 | + null, | ||
| 329 | + null, | ||
| 330 | + null, | ||
| 331 | + null, | ||
| 332 | + null, | ||
| 333 | + null, | ||
| 334 | + null, | ||
| 335 | + null, | ||
| 336 | + null, | ||
| 337 | + null, | ||
| 338 | + 10, | ||
| 339 | + null, | ||
| 340 | + null, | ||
| 341 | + null, | ||
| 342 | + null, | ||
| 343 | + null, | ||
| 344 | + null, | ||
| 345 | + null, | ||
| 346 | + null, | ||
| 347 | + null, | ||
| 348 | + null, | ||
| 349 | + 10, | ||
| 350 | + null, | ||
| 351 | + null, | ||
| 352 | + null, | ||
| 353 | + null, | ||
| 354 | + null, | ||
| 355 | + null, | ||
| 356 | + null, | ||
| 357 | + null, | ||
| 358 | + null, | ||
| 359 | + null | ||
| 360 | + ], | ||
| 361 | + "/Parent": "2 0 R", | ||
| 362 | + "/Resources": { | ||
| 363 | + "/Font": { | ||
| 364 | + "/F1": "7 0 R" | ||
| 365 | + }, | ||
| 366 | + "/ProcSet": "8 0 R" | ||
| 367 | + }, | ||
| 368 | + "/Type": "/Page" | ||
| 369 | + }, | ||
| 370 | + "4 0 R": { | ||
| 371 | + "/Length": "5 0 R" | ||
| 372 | + }, | ||
| 373 | + "5 0 R": 44, | ||
| 374 | + "6 0 R": null, | ||
| 375 | + "7 0 R": { | ||
| 376 | + "/BaseFont": "/Helvetica", | ||
| 377 | + "/Encoding": "/WinAnsiEncoding", | ||
| 378 | + "/Name": "/F1", | ||
| 379 | + "/Subtype": "/Type1", | ||
| 380 | + "/Type": "/Font" | ||
| 381 | + }, | ||
| 382 | + "8 0 R": [ | ||
| 383 | + "/PDF", | ||
| 384 | + "/Text" | ||
| 385 | + ], | ||
| 386 | + "trailer": { | ||
| 387 | + "/ID": [ | ||
| 388 | + "รรฎgE๏ฟฝEMรโนรรยข$ยฒ\u0005#", | ||
| 389 | + "1AY&SXล ๏ฌ#โbd3โฆ'ล" | ||
| 390 | + ], | ||
| 391 | + "/Root": "1 0 R", | ||
| 392 | + "/Size": 9 | ||
| 393 | + } | ||
| 394 | + }, | ||
| 395 | + "objectinfo": { | ||
| 396 | + "1 0 R": { | ||
| 397 | + "stream": { | ||
| 398 | + "filter": null, | ||
| 399 | + "is": false, | ||
| 400 | + "length": null | ||
| 401 | + } | ||
| 402 | + }, | ||
| 403 | + "2 0 R": { | ||
| 404 | + "stream": { | ||
| 405 | + "filter": null, | ||
| 406 | + "is": false, | ||
| 407 | + "length": null | ||
| 408 | + } | ||
| 409 | + }, | ||
| 410 | + "3 0 R": { | ||
| 411 | + "stream": { | ||
| 412 | + "filter": null, | ||
| 413 | + "is": false, | ||
| 414 | + "length": null | ||
| 415 | + } | ||
| 416 | + }, | ||
| 417 | + "4 0 R": { | ||
| 418 | + "stream": { | ||
| 419 | + "filter": null, | ||
| 420 | + "is": true, | ||
| 421 | + "length": 44 | ||
| 422 | + } | ||
| 423 | + }, | ||
| 424 | + "5 0 R": { | ||
| 425 | + "stream": { | ||
| 426 | + "filter": null, | ||
| 427 | + "is": false, | ||
| 428 | + "length": null | ||
| 429 | + } | ||
| 430 | + }, | ||
| 431 | + "6 0 R": { | ||
| 432 | + "stream": { | ||
| 433 | + "filter": null, | ||
| 434 | + "is": false, | ||
| 435 | + "length": null | ||
| 436 | + } | ||
| 437 | + }, | ||
| 438 | + "7 0 R": { | ||
| 439 | + "stream": { | ||
| 440 | + "filter": null, | ||
| 441 | + "is": false, | ||
| 442 | + "length": null | ||
| 443 | + } | ||
| 444 | + }, | ||
| 445 | + "8 0 R": { | ||
| 446 | + "stream": { | ||
| 447 | + "filter": null, | ||
| 448 | + "is": false, | ||
| 449 | + "length": null | ||
| 450 | + } | ||
| 451 | + } | ||
| 452 | + } | ||
| 453 | +} |
qpdf/qtest/qpdf/minimal-nulls-2.json
0 โ 100644
| 1 | +{ | ||
| 2 | + "version": 2, | ||
| 3 | + "parameters": { | ||
| 4 | + "decodelevel": "generalized" | ||
| 5 | + }, | ||
| 6 | + "pages": [ | ||
| 7 | + { | ||
| 8 | + "contents": [ | ||
| 9 | + "4 0 R" | ||
| 10 | + ], | ||
| 11 | + "images": [], | ||
| 12 | + "label": null, | ||
| 13 | + "object": "3 0 R", | ||
| 14 | + "outlines": [], | ||
| 15 | + "pageposfrom1": 1 | ||
| 16 | + } | ||
| 17 | + ], | ||
| 18 | + "pagelabels": [], | ||
| 19 | + "acroform": { | ||
| 20 | + "fields": [], | ||
| 21 | + "hasacroform": false, | ||
| 22 | + "needappearances": false | ||
| 23 | + }, | ||
| 24 | + "attachments": {}, | ||
| 25 | + "encrypt": { | ||
| 26 | + "capabilities": { | ||
| 27 | + "accessibility": true, | ||
| 28 | + "extract": true, | ||
| 29 | + "modify": true, | ||
| 30 | + "modifyannotations": true, | ||
| 31 | + "modifyassembly": true, | ||
| 32 | + "modifyforms": true, | ||
| 33 | + "modifyother": true, | ||
| 34 | + "printhigh": true, | ||
| 35 | + "printlow": true | ||
| 36 | + }, | ||
| 37 | + "encrypted": false, | ||
| 38 | + "ownerpasswordmatched": false, | ||
| 39 | + "parameters": { | ||
| 40 | + "P": 0, | ||
| 41 | + "R": 0, | ||
| 42 | + "V": 0, | ||
| 43 | + "bits": 0, | ||
| 44 | + "filemethod": "none", | ||
| 45 | + "key": null, | ||
| 46 | + "method": "none", | ||
| 47 | + "streammethod": "none", | ||
| 48 | + "stringmethod": "none" | ||
| 49 | + }, | ||
| 50 | + "recovereduserpassword": null, | ||
| 51 | + "userpasswordmatched": false | ||
| 52 | + }, | ||
| 53 | + "outlines": [], | ||
| 54 | + "qpdf": [ | ||
| 55 | + { | ||
| 56 | + "jsonversion": 2, | ||
| 57 | + "pdfversion": "1.3", | ||
| 58 | + "pushedinheritedpageresources": false, | ||
| 59 | + "calledgetallpages": true, | ||
| 60 | + "maxobjectid": 8 | ||
| 61 | + }, | ||
| 62 | + { | ||
| 63 | + "obj:1 0 R": { | ||
| 64 | + "value": { | ||
| 65 | + "/Pages": "2 0 R", | ||
| 66 | + "/Type": "/Catalog" | ||
| 67 | + } | ||
| 68 | + }, | ||
| 69 | + "obj:2 0 R": { | ||
| 70 | + "value": { | ||
| 71 | + "/Count": 1, | ||
| 72 | + "/Kids": [ | ||
| 73 | + "3 0 R" | ||
| 74 | + ], | ||
| 75 | + "/Type": "/Pages" | ||
| 76 | + } | ||
| 77 | + }, | ||
| 78 | + "obj:3 0 R": { | ||
| 79 | + "value": { | ||
| 80 | + "/Contents": "4 0 R", | ||
| 81 | + "/MediaBox": [ | ||
| 82 | + 0, | ||
| 83 | + 0, | ||
| 84 | + 612, | ||
| 85 | + 792 | ||
| 86 | + ], | ||
| 87 | + "/Nulls": [ | ||
| 88 | + null, | ||
| 89 | + null, | ||
| 90 | + null, | ||
| 91 | + null, | ||
| 92 | + null, | ||
| 93 | + null, | ||
| 94 | + null, | ||
| 95 | + null, | ||
| 96 | + null, | ||
| 97 | + null, | ||
| 98 | + "6 0 R", | ||
| 99 | + null, | ||
| 100 | + null, | ||
| 101 | + null, | ||
| 102 | + null, | ||
| 103 | + null, | ||
| 104 | + null, | ||
| 105 | + null, | ||
| 106 | + null, | ||
| 107 | + null, | ||
| 108 | + null, | ||
| 109 | + 10, | ||
| 110 | + null, | ||
| 111 | + null, | ||
| 112 | + null, | ||
| 113 | + null, | ||
| 114 | + null, | ||
| 115 | + null, | ||
| 116 | + null, | ||
| 117 | + null, | ||
| 118 | + null, | ||
| 119 | + null, | ||
| 120 | + 10, | ||
| 121 | + null, | ||
| 122 | + null, | ||
| 123 | + null, | ||
| 124 | + null, | ||
| 125 | + null, | ||
| 126 | + null, | ||
| 127 | + null, | ||
| 128 | + null, | ||
| 129 | + null, | ||
| 130 | + null, | ||
| 131 | + 10, | ||
| 132 | + null, | ||
| 133 | + null, | ||
| 134 | + null, | ||
| 135 | + null, | ||
| 136 | + null, | ||
| 137 | + null, | ||
| 138 | + null, | ||
| 139 | + null, | ||
| 140 | + null, | ||
| 141 | + null, | ||
| 142 | + 10, | ||
| 143 | + null, | ||
| 144 | + null, | ||
| 145 | + null, | ||
| 146 | + null, | ||
| 147 | + null, | ||
| 148 | + null, | ||
| 149 | + null, | ||
| 150 | + null, | ||
| 151 | + null, | ||
| 152 | + null, | ||
| 153 | + 10, | ||
| 154 | + null, | ||
| 155 | + null, | ||
| 156 | + null, | ||
| 157 | + null, | ||
| 158 | + null, | ||
| 159 | + null, | ||
| 160 | + null, | ||
| 161 | + null, | ||
| 162 | + null, | ||
| 163 | + null, | ||
| 164 | + 10, | ||
| 165 | + null, | ||
| 166 | + null, | ||
| 167 | + null, | ||
| 168 | + null, | ||
| 169 | + null, | ||
| 170 | + null, | ||
| 171 | + null, | ||
| 172 | + null, | ||
| 173 | + null, | ||
| 174 | + null, | ||
| 175 | + 10, | ||
| 176 | + null, | ||
| 177 | + null, | ||
| 178 | + null, | ||
| 179 | + null, | ||
| 180 | + null, | ||
| 181 | + null, | ||
| 182 | + null, | ||
| 183 | + null, | ||
| 184 | + null, | ||
| 185 | + null, | ||
| 186 | + 10, | ||
| 187 | + null, | ||
| 188 | + null, | ||
| 189 | + null, | ||
| 190 | + null, | ||
| 191 | + null, | ||
| 192 | + null, | ||
| 193 | + null, | ||
| 194 | + null, | ||
| 195 | + null, | ||
| 196 | + null, | ||
| 197 | + 10, | ||
| 198 | + null, | ||
| 199 | + null, | ||
| 200 | + null, | ||
| 201 | + null, | ||
| 202 | + null, | ||
| 203 | + null, | ||
| 204 | + null, | ||
| 205 | + null, | ||
| 206 | + null, | ||
| 207 | + null, | ||
| 208 | + 10, | ||
| 209 | + null, | ||
| 210 | + null, | ||
| 211 | + null, | ||
| 212 | + null, | ||
| 213 | + null, | ||
| 214 | + null, | ||
| 215 | + null, | ||
| 216 | + null, | ||
| 217 | + null, | ||
| 218 | + null, | ||
| 219 | + 10, | ||
| 220 | + null, | ||
| 221 | + null, | ||
| 222 | + null, | ||
| 223 | + null, | ||
| 224 | + null, | ||
| 225 | + null, | ||
| 226 | + null, | ||
| 227 | + null, | ||
| 228 | + null, | ||
| 229 | + null, | ||
| 230 | + 10, | ||
| 231 | + null, | ||
| 232 | + null, | ||
| 233 | + null, | ||
| 234 | + null, | ||
| 235 | + null, | ||
| 236 | + null, | ||
| 237 | + null, | ||
| 238 | + null, | ||
| 239 | + null, | ||
| 240 | + null, | ||
| 241 | + 10, | ||
| 242 | + null, | ||
| 243 | + null, | ||
| 244 | + null, | ||
| 245 | + null, | ||
| 246 | + null, | ||
| 247 | + null, | ||
| 248 | + null, | ||
| 249 | + null, | ||
| 250 | + null, | ||
| 251 | + null, | ||
| 252 | + 10, | ||
| 253 | + null, | ||
| 254 | + null, | ||
| 255 | + null, | ||
| 256 | + null, | ||
| 257 | + null, | ||
| 258 | + null, | ||
| 259 | + null, | ||
| 260 | + null, | ||
| 261 | + null, | ||
| 262 | + null, | ||
| 263 | + 10, | ||
| 264 | + null, | ||
| 265 | + null, | ||
| 266 | + null, | ||
| 267 | + null, | ||
| 268 | + null, | ||
| 269 | + null, | ||
| 270 | + null, | ||
| 271 | + null, | ||
| 272 | + null, | ||
| 273 | + null, | ||
| 274 | + 10, | ||
| 275 | + null, | ||
| 276 | + null, | ||
| 277 | + null, | ||
| 278 | + null, | ||
| 279 | + null, | ||
| 280 | + null, | ||
| 281 | + null, | ||
| 282 | + null, | ||
| 283 | + null, | ||
| 284 | + null, | ||
| 285 | + 10, | ||
| 286 | + null, | ||
| 287 | + null, | ||
| 288 | + null, | ||
| 289 | + null, | ||
| 290 | + null, | ||
| 291 | + null, | ||
| 292 | + null, | ||
| 293 | + null, | ||
| 294 | + null, | ||
| 295 | + null, | ||
| 296 | + 10, | ||
| 297 | + null, | ||
| 298 | + null, | ||
| 299 | + null, | ||
| 300 | + null, | ||
| 301 | + null, | ||
| 302 | + null, | ||
| 303 | + null, | ||
| 304 | + null, | ||
| 305 | + null, | ||
| 306 | + null, | ||
| 307 | + 10, | ||
| 308 | + null, | ||
| 309 | + null, | ||
| 310 | + null, | ||
| 311 | + null, | ||
| 312 | + null, | ||
| 313 | + null, | ||
| 314 | + null, | ||
| 315 | + null, | ||
| 316 | + null, | ||
| 317 | + null, | ||
| 318 | + 10, | ||
| 319 | + null, | ||
| 320 | + null, | ||
| 321 | + null, | ||
| 322 | + null, | ||
| 323 | + null, | ||
| 324 | + null, | ||
| 325 | + null, | ||
| 326 | + null, | ||
| 327 | + null, | ||
| 328 | + null, | ||
| 329 | + 10, | ||
| 330 | + null, | ||
| 331 | + null, | ||
| 332 | + null, | ||
| 333 | + null, | ||
| 334 | + null, | ||
| 335 | + null, | ||
| 336 | + null, | ||
| 337 | + null, | ||
| 338 | + null, | ||
| 339 | + null, | ||
| 340 | + 10, | ||
| 341 | + null, | ||
| 342 | + null, | ||
| 343 | + null, | ||
| 344 | + null, | ||
| 345 | + null, | ||
| 346 | + null, | ||
| 347 | + null, | ||
| 348 | + null, | ||
| 349 | + null, | ||
| 350 | + null, | ||
| 351 | + 10, | ||
| 352 | + null, | ||
| 353 | + null, | ||
| 354 | + null, | ||
| 355 | + null, | ||
| 356 | + null, | ||
| 357 | + null, | ||
| 358 | + null, | ||
| 359 | + null, | ||
| 360 | + null, | ||
| 361 | + null, | ||
| 362 | + 10, | ||
| 363 | + null, | ||
| 364 | + null, | ||
| 365 | + null, | ||
| 366 | + null, | ||
| 367 | + null, | ||
| 368 | + null, | ||
| 369 | + null, | ||
| 370 | + null, | ||
| 371 | + null, | ||
| 372 | + null | ||
| 373 | + ], | ||
| 374 | + "/Parent": "2 0 R", | ||
| 375 | + "/Resources": { | ||
| 376 | + "/Font": { | ||
| 377 | + "/F1": "7 0 R" | ||
| 378 | + }, | ||
| 379 | + "/ProcSet": "8 0 R" | ||
| 380 | + }, | ||
| 381 | + "/Type": "/Page" | ||
| 382 | + } | ||
| 383 | + }, | ||
| 384 | + "obj:4 0 R": { | ||
| 385 | + "stream": { | ||
| 386 | + "dict": { | ||
| 387 | + "/Length": "5 0 R" | ||
| 388 | + } | ||
| 389 | + } | ||
| 390 | + }, | ||
| 391 | + "obj:5 0 R": { | ||
| 392 | + "value": 44 | ||
| 393 | + }, | ||
| 394 | + "obj:6 0 R": { | ||
| 395 | + "value": null | ||
| 396 | + }, | ||
| 397 | + "obj:7 0 R": { | ||
| 398 | + "value": { | ||
| 399 | + "/BaseFont": "/Helvetica", | ||
| 400 | + "/Encoding": "/WinAnsiEncoding", | ||
| 401 | + "/Name": "/F1", | ||
| 402 | + "/Subtype": "/Type1", | ||
| 403 | + "/Type": "/Font" | ||
| 404 | + } | ||
| 405 | + }, | ||
| 406 | + "obj:8 0 R": { | ||
| 407 | + "value": [ | ||
| 408 | + "/PDF", | ||
| 409 | + "/Text" | ||
| 410 | + ] | ||
| 411 | + }, | ||
| 412 | + "trailer": { | ||
| 413 | + "value": { | ||
| 414 | + "/ID": [ | ||
| 415 | + "b:cfee6745ad454ddb88cadfa224b20523", | ||
| 416 | + "b:31415926535897932384626433832795" | ||
| 417 | + ], | ||
| 418 | + "/Root": "1 0 R", | ||
| 419 | + "/Size": 9 | ||
| 420 | + } | ||
| 421 | + } | ||
| 422 | + } | ||
| 423 | + ] | ||
| 424 | +} |
qpdf/qtest/qpdf/minimal-nulls.pdf
0 โ 100644
| 1 | +%PDF-1.3 | ||
| 2 | +%ยฟรทยขรพ | ||
| 3 | +%QDF-1.0 | ||
| 4 | + | ||
| 5 | +1 0 obj | ||
| 6 | +<< | ||
| 7 | + /Pages 2 0 R | ||
| 8 | + /Type /Catalog | ||
| 9 | +>> | ||
| 10 | +endobj | ||
| 11 | + | ||
| 12 | +2 0 obj | ||
| 13 | +<< | ||
| 14 | + /Count 1 | ||
| 15 | + /Kids [ | ||
| 16 | + 3 0 R | ||
| 17 | + ] | ||
| 18 | + /Type /Pages | ||
| 19 | +>> | ||
| 20 | +endobj | ||
| 21 | + | ||
| 22 | +%% Page 1 | ||
| 23 | +3 0 obj | ||
| 24 | +<< | ||
| 25 | + /Contents 4 0 R | ||
| 26 | + /MediaBox [ | ||
| 27 | + 0 | ||
| 28 | + 0 | ||
| 29 | + 612 | ||
| 30 | + 792 | ||
| 31 | + ] | ||
| 32 | + /Nulls [ | ||
| 33 | + null | ||
| 34 | + null | ||
| 35 | + null | ||
| 36 | + null | ||
| 37 | + null | ||
| 38 | + null | ||
| 39 | + null | ||
| 40 | + null | ||
| 41 | + null | ||
| 42 | + null | ||
| 43 | + 6 0 R | ||
| 44 | + null | ||
| 45 | + null | ||
| 46 | + null | ||
| 47 | + null | ||
| 48 | + null | ||
| 49 | + null | ||
| 50 | + null | ||
| 51 | + null | ||
| 52 | + null | ||
| 53 | + null | ||
| 54 | + 10 | ||
| 55 | + null | ||
| 56 | + null | ||
| 57 | + null | ||
| 58 | + null | ||
| 59 | + null | ||
| 60 | + null | ||
| 61 | + null | ||
| 62 | + null | ||
| 63 | + null | ||
| 64 | + null | ||
| 65 | + 10 | ||
| 66 | + null | ||
| 67 | + null | ||
| 68 | + null | ||
| 69 | + null | ||
| 70 | + null | ||
| 71 | + null | ||
| 72 | + null | ||
| 73 | + null | ||
| 74 | + null | ||
| 75 | + null | ||
| 76 | + 10 | ||
| 77 | + null | ||
| 78 | + null | ||
| 79 | + null | ||
| 80 | + null | ||
| 81 | + null | ||
| 82 | + null | ||
| 83 | + null | ||
| 84 | + null | ||
| 85 | + null | ||
| 86 | + null | ||
| 87 | + 10 | ||
| 88 | + null | ||
| 89 | + null | ||
| 90 | + null | ||
| 91 | + null | ||
| 92 | + null | ||
| 93 | + null | ||
| 94 | + null | ||
| 95 | + null | ||
| 96 | + null | ||
| 97 | + null | ||
| 98 | + 10 | ||
| 99 | + null | ||
| 100 | + null | ||
| 101 | + null | ||
| 102 | + null | ||
| 103 | + null | ||
| 104 | + null | ||
| 105 | + null | ||
| 106 | + null | ||
| 107 | + null | ||
| 108 | + null | ||
| 109 | + 10 | ||
| 110 | + null | ||
| 111 | + null | ||
| 112 | + null | ||
| 113 | + null | ||
| 114 | + null | ||
| 115 | + null | ||
| 116 | + null | ||
| 117 | + null | ||
| 118 | + null | ||
| 119 | + null | ||
| 120 | + 10 | ||
| 121 | + null | ||
| 122 | + null | ||
| 123 | + null | ||
| 124 | + null | ||
| 125 | + null | ||
| 126 | + null | ||
| 127 | + null | ||
| 128 | + null | ||
| 129 | + null | ||
| 130 | + null | ||
| 131 | + 10 | ||
| 132 | + null | ||
| 133 | + null | ||
| 134 | + null | ||
| 135 | + null | ||
| 136 | + null | ||
| 137 | + null | ||
| 138 | + null | ||
| 139 | + null | ||
| 140 | + null | ||
| 141 | + null | ||
| 142 | + 10 | ||
| 143 | + null | ||
| 144 | + null | ||
| 145 | + null | ||
| 146 | + null | ||
| 147 | + null | ||
| 148 | + null | ||
| 149 | + null | ||
| 150 | + null | ||
| 151 | + null | ||
| 152 | + null | ||
| 153 | + 10 | ||
| 154 | + null | ||
| 155 | + null | ||
| 156 | + null | ||
| 157 | + null | ||
| 158 | + null | ||
| 159 | + null | ||
| 160 | + null | ||
| 161 | + null | ||
| 162 | + null | ||
| 163 | + null | ||
| 164 | + 10 | ||
| 165 | + null | ||
| 166 | + null | ||
| 167 | + null | ||
| 168 | + null | ||
| 169 | + null | ||
| 170 | + null | ||
| 171 | + null | ||
| 172 | + null | ||
| 173 | + null | ||
| 174 | + null | ||
| 175 | + 10 | ||
| 176 | + null | ||
| 177 | + null | ||
| 178 | + null | ||
| 179 | + null | ||
| 180 | + null | ||
| 181 | + null | ||
| 182 | + null | ||
| 183 | + null | ||
| 184 | + null | ||
| 185 | + null | ||
| 186 | + 10 | ||
| 187 | + null | ||
| 188 | + null | ||
| 189 | + null | ||
| 190 | + null | ||
| 191 | + null | ||
| 192 | + null | ||
| 193 | + null | ||
| 194 | + null | ||
| 195 | + null | ||
| 196 | + null | ||
| 197 | + 10 | ||
| 198 | + null | ||
| 199 | + null | ||
| 200 | + null | ||
| 201 | + null | ||
| 202 | + null | ||
| 203 | + null | ||
| 204 | + null | ||
| 205 | + null | ||
| 206 | + null | ||
| 207 | + null | ||
| 208 | + 10 | ||
| 209 | + null | ||
| 210 | + null | ||
| 211 | + null | ||
| 212 | + null | ||
| 213 | + null | ||
| 214 | + null | ||
| 215 | + null | ||
| 216 | + null | ||
| 217 | + null | ||
| 218 | + null | ||
| 219 | + 10 | ||
| 220 | + null | ||
| 221 | + null | ||
| 222 | + null | ||
| 223 | + null | ||
| 224 | + null | ||
| 225 | + null | ||
| 226 | + null | ||
| 227 | + null | ||
| 228 | + null | ||
| 229 | + null | ||
| 230 | + 10 | ||
| 231 | + null | ||
| 232 | + null | ||
| 233 | + null | ||
| 234 | + null | ||
| 235 | + null | ||
| 236 | + null | ||
| 237 | + null | ||
| 238 | + null | ||
| 239 | + null | ||
| 240 | + null | ||
| 241 | + 10 | ||
| 242 | + null | ||
| 243 | + null | ||
| 244 | + null | ||
| 245 | + null | ||
| 246 | + null | ||
| 247 | + null | ||
| 248 | + null | ||
| 249 | + null | ||
| 250 | + null | ||
| 251 | + null | ||
| 252 | + 10 | ||
| 253 | + null | ||
| 254 | + null | ||
| 255 | + null | ||
| 256 | + null | ||
| 257 | + null | ||
| 258 | + null | ||
| 259 | + null | ||
| 260 | + null | ||
| 261 | + null | ||
| 262 | + null | ||
| 263 | + 10 | ||
| 264 | + null | ||
| 265 | + null | ||
| 266 | + null | ||
| 267 | + null | ||
| 268 | + null | ||
| 269 | + null | ||
| 270 | + null | ||
| 271 | + null | ||
| 272 | + null | ||
| 273 | + null | ||
| 274 | + 10 | ||
| 275 | + null | ||
| 276 | + null | ||
| 277 | + null | ||
| 278 | + null | ||
| 279 | + null | ||
| 280 | + null | ||
| 281 | + null | ||
| 282 | + null | ||
| 283 | + null | ||
| 284 | + null | ||
| 285 | + 10 | ||
| 286 | + null | ||
| 287 | + null | ||
| 288 | + null | ||
| 289 | + null | ||
| 290 | + null | ||
| 291 | + null | ||
| 292 | + null | ||
| 293 | + null | ||
| 294 | + null | ||
| 295 | + null | ||
| 296 | + 10 | ||
| 297 | + null | ||
| 298 | + null | ||
| 299 | + null | ||
| 300 | + null | ||
| 301 | + null | ||
| 302 | + null | ||
| 303 | + null | ||
| 304 | + null | ||
| 305 | + null | ||
| 306 | + null | ||
| 307 | + 10 | ||
| 308 | + null | ||
| 309 | + null | ||
| 310 | + null | ||
| 311 | + null | ||
| 312 | + null | ||
| 313 | + null | ||
| 314 | + null | ||
| 315 | + null | ||
| 316 | + null | ||
| 317 | + null | ||
| 318 | + ] | ||
| 319 | + /Parent 2 0 R | ||
| 320 | + /Resources << | ||
| 321 | + /Font << | ||
| 322 | + /F1 7 0 R | ||
| 323 | + >> | ||
| 324 | + /ProcSet 8 0 R | ||
| 325 | + >> | ||
| 326 | + /Type /Page | ||
| 327 | +>> | ||
| 328 | +endobj | ||
| 329 | + | ||
| 330 | +%% Contents for page 1 | ||
| 331 | +4 0 obj | ||
| 332 | +<< | ||
| 333 | + /Length 5 0 R | ||
| 334 | +>> | ||
| 335 | +stream | ||
| 336 | +BT | ||
| 337 | + /F1 24 Tf | ||
| 338 | + 72 720 Td | ||
| 339 | + (Potato) Tj | ||
| 340 | +ET | ||
| 341 | +endstream | ||
| 342 | +endobj | ||
| 343 | + | ||
| 344 | +5 0 obj | ||
| 345 | +44 | ||
| 346 | +endobj | ||
| 347 | + | ||
| 348 | +6 0 obj | ||
| 349 | +null | ||
| 350 | +endobj | ||
| 351 | + | ||
| 352 | +7 0 obj | ||
| 353 | +<< | ||
| 354 | + /BaseFont /Helvetica | ||
| 355 | + /Encoding /WinAnsiEncoding | ||
| 356 | + /Name /F1 | ||
| 357 | + /Subtype /Type1 | ||
| 358 | + /Type /Font | ||
| 359 | +>> | ||
| 360 | +endobj | ||
| 361 | + | ||
| 362 | +8 0 obj | ||
| 363 | +[ | ||
| 364 | |||
| 365 | + /Text | ||
| 366 | +] | ||
| 367 | +endobj | ||
| 368 | + | ||
| 369 | +xref | ||
| 370 | +0 9 | ||
| 371 | +0000000000 65535 f | ||
| 372 | +0000000025 00000 n | ||
| 373 | +0000000079 00000 n | ||
| 374 | +0000000161 00000 n | ||
| 375 | +0000002909 00000 n | ||
| 376 | +0000003008 00000 n | ||
| 377 | +0000003027 00000 n | ||
| 378 | +0000003048 00000 n | ||
| 379 | +0000003166 00000 n | ||
| 380 | +trailer << | ||
| 381 | + /Root 1 0 R | ||
| 382 | + /Size 9 | ||
| 383 | + /ID [<cfee6745ad454ddb88cadfa224b20523><31415926535897932384626433832795>] | ||
| 384 | +>> | ||
| 385 | +startxref | ||
| 386 | +3201 | ||
| 387 | +%%EOF |
qpdf/qtest/qpdf/weird-tokens-alt.json
| @@ -10,21 +10,155 @@ | @@ -10,21 +10,155 @@ | ||
| 10 | { | 10 | { |
| 11 | "obj:1 0 R": { | 11 | "obj:1 0 R": { |
| 12 | "value": { | 12 | "value": { |
| 13 | + "/Escape\\Key": 42, | ||
| 13 | "/Extra": [ | 14 | "/Extra": [ |
| 14 | "u:Names with binary data", | 15 | "u:Names with binary data", |
| 15 | "n:/ABCDEF+#ba#da#cc#e5", | 16 | "n:/ABCDEF+#ba#da#cc#e5", |
| 16 | "n:/OVERLONG+#c0#81", | 17 | "n:/OVERLONG+#c0#81", |
| 18 | + "n:/OVERLONG+#c1#ff", | ||
| 19 | + "/Ok+ย", | ||
| 17 | "n:/OVERLONG+#e0#81#82", | 20 | "n:/OVERLONG+#e0#81#82", |
| 21 | + "n:/OVERLONG+#e0#9f#ff", | ||
| 22 | + "/Ok+เ ", | ||
| 18 | "n:/OVERLONG+#f0#81#82#83", | 23 | "n:/OVERLONG+#f0#81#82#83", |
| 24 | + "n:/OVERLONG+#f0#8f#ff#ff", | ||
| 25 | + "/Ok+๐", | ||
| 19 | "n:/range+#01", | 26 | "n:/range+#01", |
| 20 | "n:/low+#18", | 27 | "n:/low+#18", |
| 21 | "/ABCEDEF+ฯ", | 28 | "/ABCEDEF+ฯ", |
| 22 | "n:/one+#a0two", | 29 | "n:/one+#a0two", |
| 23 | "n:/text#2fplain", | 30 | "n:/text#2fplain", |
| 31 | + "u:Names requiring escaping in JSON", | ||
| 32 | + "/Back\\shlash", | ||
| 33 | + "/Low\u0022", | ||
| 34 | + "/Low\u001f", | ||
| 35 | + "/ExceptSpace ", | ||
| 36 | + "/Except!", | ||
| 24 | "u:Very small/large reals", | 37 | "u:Very small/large reals", |
| 25 | 1e-05, | 38 | 1e-05, |
| 26 | 1e12 | 39 | 1e12 |
| 27 | ], | 40 | ], |
| 41 | + "/Nested": { | ||
| 42 | + "/1": { | ||
| 43 | + "/2": { | ||
| 44 | + "/3": { | ||
| 45 | + "/4": { | ||
| 46 | + "/5": { | ||
| 47 | + "/6": { | ||
| 48 | + "/7": { | ||
| 49 | + "/8": { | ||
| 50 | + "/9": { | ||
| 51 | + "/10": { | ||
| 52 | + "/1": { | ||
| 53 | + "/2": { | ||
| 54 | + "/3": { | ||
| 55 | + "/4": { | ||
| 56 | + "/5": { | ||
| 57 | + "/6": { | ||
| 58 | + "/7": { | ||
| 59 | + "/8": { | ||
| 60 | + "/9": { | ||
| 61 | + "/10": { | ||
| 62 | + "/1": { | ||
| 63 | + "/2": { | ||
| 64 | + "/3": { | ||
| 65 | + "/4": { | ||
| 66 | + "/5": { | ||
| 67 | + "/6": { | ||
| 68 | + "/7": { | ||
| 69 | + "/8": { | ||
| 70 | + "/9": { | ||
| 71 | + "/10": { | ||
| 72 | + "/1": { | ||
| 73 | + "/2": { | ||
| 74 | + "/3": { | ||
| 75 | + "/4": { | ||
| 76 | + "/5": { | ||
| 77 | + "/6": { | ||
| 78 | + "/7": { | ||
| 79 | + "/8": { | ||
| 80 | + "/9": { | ||
| 81 | + "/10": { | ||
| 82 | + "/1": { | ||
| 83 | + "/2": { | ||
| 84 | + "/3": { | ||
| 85 | + "/4": { | ||
| 86 | + "/5": { | ||
| 87 | + "/6": { | ||
| 88 | + "/7": { | ||
| 89 | + "/8": { | ||
| 90 | + "/9": { | ||
| 91 | + "/10": { | ||
| 92 | + "/1": { | ||
| 93 | + "/2": { | ||
| 94 | + "/3": { | ||
| 95 | + "/4": { | ||
| 96 | + "/5": { | ||
| 97 | + "/6": { | ||
| 98 | + "/7": { | ||
| 99 | + "/8": { | ||
| 100 | + "/9": { | ||
| 101 | + "/10": 42 | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + }, | ||
| 28 | "/Pages": "2 0 R", | 162 | "/Pages": "2 0 R", |
| 29 | "/Type": "/Catalog", | 163 | "/Type": "/Catalog", |
| 30 | "n:/WeirdKey+#ba#da#cc#e5": 42 | 164 | "n:/WeirdKey+#ba#da#cc#e5": 42 |
| @@ -78,7 +212,7 @@ | @@ -78,7 +212,7 @@ | ||
| 78 | "value": { | 212 | "value": { |
| 79 | "/ID": [ | 213 | "/ID": [ |
| 80 | "b:42841c13bbf709d79a200fa1691836f8", | 214 | "b:42841c13bbf709d79a200fa1691836f8", |
| 81 | - "b:728c020f464c3cf7e02c12605fa7d88b" | 215 | + "b:31415926535897932384626433832795" |
| 82 | ], | 216 | ], |
| 83 | "/Root": "1 0 R", | 217 | "/Root": "1 0 R", |
| 84 | "/Size": 7 | 218 | "/Size": 7 |
qpdf/qtest/qpdf/weird-tokens-v1.json
0 โ 100644
| 1 | +{ | ||
| 2 | + "version": 1, | ||
| 3 | + "parameters": { | ||
| 4 | + "decodelevel": "generalized" | ||
| 5 | + }, | ||
| 6 | + "pages": [ | ||
| 7 | + { | ||
| 8 | + "contents": [ | ||
| 9 | + "4 0 R" | ||
| 10 | + ], | ||
| 11 | + "images": [], | ||
| 12 | + "label": null, | ||
| 13 | + "object": "3 0 R", | ||
| 14 | + "outlines": [], | ||
| 15 | + "pageposfrom1": 1 | ||
| 16 | + } | ||
| 17 | + ], | ||
| 18 | + "pagelabels": [], | ||
| 19 | + "acroform": { | ||
| 20 | + "fields": [], | ||
| 21 | + "hasacroform": false, | ||
| 22 | + "needappearances": false | ||
| 23 | + }, | ||
| 24 | + "attachments": {}, | ||
| 25 | + "encrypt": { | ||
| 26 | + "capabilities": { | ||
| 27 | + "accessibility": true, | ||
| 28 | + "extract": true, | ||
| 29 | + "moddifyannotations": true, | ||
| 30 | + "modify": true, | ||
| 31 | + "modifyassembly": true, | ||
| 32 | + "modifyforms": true, | ||
| 33 | + "modifyother": true, | ||
| 34 | + "printhigh": true, | ||
| 35 | + "printlow": true | ||
| 36 | + }, | ||
| 37 | + "encrypted": false, | ||
| 38 | + "ownerpasswordmatched": false, | ||
| 39 | + "parameters": { | ||
| 40 | + "P": 0, | ||
| 41 | + "R": 0, | ||
| 42 | + "V": 0, | ||
| 43 | + "bits": 0, | ||
| 44 | + "filemethod": "none", | ||
| 45 | + "key": null, | ||
| 46 | + "method": "none", | ||
| 47 | + "streammethod": "none", | ||
| 48 | + "stringmethod": "none" | ||
| 49 | + }, | ||
| 50 | + "recovereduserpassword": null, | ||
| 51 | + "userpasswordmatched": false | ||
| 52 | + }, | ||
| 53 | + "outlines": [], | ||
| 54 | + "objects": { | ||
| 55 | + "1 0 R": { | ||
| 56 | + "/Escape\\Key": 42, | ||
| 57 | + "/Extra": [ | ||
| 58 | + "Names with binary data", | ||
| 59 | + "/ABCDEF+#ba#da#cc#e5", | ||
| 60 | + "/OVERLONG+#c0#81", | ||
| 61 | + "/OVERLONG+#c1#ff", | ||
| 62 | + "/Ok+#c2#80", | ||
| 63 | + "/OVERLONG+#e0#81#82", | ||
| 64 | + "/OVERLONG+#e0#9f#ff", | ||
| 65 | + "/Ok+#e0#a0#80", | ||
| 66 | + "/OVERLONG+#f0#81#82#83", | ||
| 67 | + "/OVERLONG+#f0#8f#ff#ff", | ||
| 68 | + "/Ok+#f0#90#80#80", | ||
| 69 | + "/range+#01", | ||
| 70 | + "/low+#18", | ||
| 71 | + "/ABCEDEF+#cf#80", | ||
| 72 | + "/one+#a0two", | ||
| 73 | + "/text#2fplain", | ||
| 74 | + "Names requiring escaping in JSON", | ||
| 75 | + "/Back\\shlash", | ||
| 76 | + "/Low\"", | ||
| 77 | + "/Low#1f", | ||
| 78 | + "/ExceptSpace#20", | ||
| 79 | + "/Except!", | ||
| 80 | + "Very small/large reals", | ||
| 81 | + 0.00001, | ||
| 82 | + 1000000000000 | ||
| 83 | + ], | ||
| 84 | + "/Nested": { | ||
| 85 | + "/1": { | ||
| 86 | + "/2": { | ||
| 87 | + "/3": { | ||
| 88 | + "/4": { | ||
| 89 | + "/5": { | ||
| 90 | + "/6": { | ||
| 91 | + "/7": { | ||
| 92 | + "/8": { | ||
| 93 | + "/9": { | ||
| 94 | + "/10": { | ||
| 95 | + "/1": { | ||
| 96 | + "/2": { | ||
| 97 | + "/3": { | ||
| 98 | + "/4": { | ||
| 99 | + "/5": { | ||
| 100 | + "/6": { | ||
| 101 | + "/7": { | ||
| 102 | + "/8": { | ||
| 103 | + "/9": { | ||
| 104 | + "/10": { | ||
| 105 | + "/1": { | ||
| 106 | + "/2": { | ||
| 107 | + "/3": { | ||
| 108 | + "/4": { | ||
| 109 | + "/5": { | ||
| 110 | + "/6": { | ||
| 111 | + "/7": { | ||
| 112 | + "/8": { | ||
| 113 | + "/9": { | ||
| 114 | + "/10": { | ||
| 115 | + "/1": { | ||
| 116 | + "/2": { | ||
| 117 | + "/3": { | ||
| 118 | + "/4": { | ||
| 119 | + "/5": { | ||
| 120 | + "/6": { | ||
| 121 | + "/7": { | ||
| 122 | + "/8": { | ||
| 123 | + "/9": { | ||
| 124 | + "/10": { | ||
| 125 | + "/1": { | ||
| 126 | + "/2": { | ||
| 127 | + "/3": { | ||
| 128 | + "/4": { | ||
| 129 | + "/5": { | ||
| 130 | + "/6": { | ||
| 131 | + "/7": { | ||
| 132 | + "/8": { | ||
| 133 | + "/9": { | ||
| 134 | + "/10": { | ||
| 135 | + "/1": { | ||
| 136 | + "/2": { | ||
| 137 | + "/3": { | ||
| 138 | + "/4": { | ||
| 139 | + "/5": { | ||
| 140 | + "/6": { | ||
| 141 | + "/7": { | ||
| 142 | + "/8": { | ||
| 143 | + "/9": { | ||
| 144 | + "/10": 42 | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + } | ||
| 162 | + } | ||
| 163 | + } | ||
| 164 | + } | ||
| 165 | + } | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + } | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + } | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + } | ||
| 194 | + } | ||
| 195 | + } | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | + }, | ||
| 205 | + "/Pages": "2 0 R", | ||
| 206 | + "/Type": "/Catalog", | ||
| 207 | + "/WeirdKey+#ba#da#cc#e5": 42 | ||
| 208 | + }, | ||
| 209 | + "2 0 R": { | ||
| 210 | + "/Count": 1, | ||
| 211 | + "/Kids": [ | ||
| 212 | + "3 0 R" | ||
| 213 | + ], | ||
| 214 | + "/Type": "/Pages" | ||
| 215 | + }, | ||
| 216 | + "3 0 R": { | ||
| 217 | + "/Contents": "4 0 R", | ||
| 218 | + "/MediaBox": [ | ||
| 219 | + 0, | ||
| 220 | + 0, | ||
| 221 | + 612, | ||
| 222 | + 792 | ||
| 223 | + ], | ||
| 224 | + "/Parent": "2 0 R", | ||
| 225 | + "/Resources": { | ||
| 226 | + "/Font": { | ||
| 227 | + "/F1": "6 0 R" | ||
| 228 | + } | ||
| 229 | + }, | ||
| 230 | + "/Type": "/Page" | ||
| 231 | + }, | ||
| 232 | + "4 0 R": { | ||
| 233 | + "/Length": "5 0 R" | ||
| 234 | + }, | ||
| 235 | + "5 0 R": 44, | ||
| 236 | + "6 0 R": { | ||
| 237 | + "/BaseFont": "/Helvetica", | ||
| 238 | + "/Encoding": "/WinAnsiEncoding", | ||
| 239 | + "/Subtype": "/Type1", | ||
| 240 | + "/Type": "/Font" | ||
| 241 | + }, | ||
| 242 | + "trailer": { | ||
| 243 | + "/ID": [ | ||
| 244 | + "Bโห\u0013ยปรท\tรฤฑ \u000fยกiห6รธ", | ||
| 245 | + "1AY&SXล ๏ฌ#โbd3โฆ'ล" | ||
| 246 | + ], | ||
| 247 | + "/Root": "1 0 R", | ||
| 248 | + "/Size": 7 | ||
| 249 | + } | ||
| 250 | + }, | ||
| 251 | + "objectinfo": { | ||
| 252 | + "1 0 R": { | ||
| 253 | + "stream": { | ||
| 254 | + "filter": null, | ||
| 255 | + "is": false, | ||
| 256 | + "length": null | ||
| 257 | + } | ||
| 258 | + }, | ||
| 259 | + "2 0 R": { | ||
| 260 | + "stream": { | ||
| 261 | + "filter": null, | ||
| 262 | + "is": false, | ||
| 263 | + "length": null | ||
| 264 | + } | ||
| 265 | + }, | ||
| 266 | + "3 0 R": { | ||
| 267 | + "stream": { | ||
| 268 | + "filter": null, | ||
| 269 | + "is": false, | ||
| 270 | + "length": null | ||
| 271 | + } | ||
| 272 | + }, | ||
| 273 | + "4 0 R": { | ||
| 274 | + "stream": { | ||
| 275 | + "filter": null, | ||
| 276 | + "is": true, | ||
| 277 | + "length": 44 | ||
| 278 | + } | ||
| 279 | + }, | ||
| 280 | + "5 0 R": { | ||
| 281 | + "stream": { | ||
| 282 | + "filter": null, | ||
| 283 | + "is": false, | ||
| 284 | + "length": null | ||
| 285 | + } | ||
| 286 | + }, | ||
| 287 | + "6 0 R": { | ||
| 288 | + "stream": { | ||
| 289 | + "filter": null, | ||
| 290 | + "is": false, | ||
| 291 | + "length": null | ||
| 292 | + } | ||
| 293 | + } | ||
| 294 | + } | ||
| 295 | +} |
qpdf/qtest/qpdf/weird-tokens.json
| @@ -10,21 +10,155 @@ | @@ -10,21 +10,155 @@ | ||
| 10 | { | 10 | { |
| 11 | "obj:1 0 R": { | 11 | "obj:1 0 R": { |
| 12 | "value": { | 12 | "value": { |
| 13 | + "/Escape\\Key": 42, | ||
| 13 | "/Extra": [ | 14 | "/Extra": [ |
| 14 | "u:Names with binary data", | 15 | "u:Names with binary data", |
| 15 | "n:/ABCDEF+#ba#da#cc#e5", | 16 | "n:/ABCDEF+#ba#da#cc#e5", |
| 16 | "n:/OVERLONG+#c0#81", | 17 | "n:/OVERLONG+#c0#81", |
| 18 | + "n:/OVERLONG+#c1#ff", | ||
| 19 | + "/Ok+ย", | ||
| 17 | "n:/OVERLONG+#e0#81#82", | 20 | "n:/OVERLONG+#e0#81#82", |
| 21 | + "n:/OVERLONG+#e0#9f#ff", | ||
| 22 | + "/Ok+เ ", | ||
| 18 | "n:/OVERLONG+#f0#81#82#83", | 23 | "n:/OVERLONG+#f0#81#82#83", |
| 24 | + "n:/OVERLONG+#f0#8f#ff#ff", | ||
| 25 | + "/Ok+๐", | ||
| 19 | "/range+\u0001", | 26 | "/range+\u0001", |
| 20 | "/low+\u0018", | 27 | "/low+\u0018", |
| 21 | "/ABCEDEF+ฯ", | 28 | "/ABCEDEF+ฯ", |
| 22 | "n:/one+#a0two", | 29 | "n:/one+#a0two", |
| 23 | "/text/plain", | 30 | "/text/plain", |
| 31 | + "u:Names requiring escaping in JSON", | ||
| 32 | + "/Back\\shlash", | ||
| 33 | + "/Low\"", | ||
| 34 | + "/Low\u001f", | ||
| 35 | + "/ExceptSpace ", | ||
| 36 | + "/Except!", | ||
| 24 | "u:Very small/large reals", | 37 | "u:Very small/large reals", |
| 25 | 0.00001, | 38 | 0.00001, |
| 26 | 1000000000000 | 39 | 1000000000000 |
| 27 | ], | 40 | ], |
| 41 | + "/Nested": { | ||
| 42 | + "/1": { | ||
| 43 | + "/2": { | ||
| 44 | + "/3": { | ||
| 45 | + "/4": { | ||
| 46 | + "/5": { | ||
| 47 | + "/6": { | ||
| 48 | + "/7": { | ||
| 49 | + "/8": { | ||
| 50 | + "/9": { | ||
| 51 | + "/10": { | ||
| 52 | + "/1": { | ||
| 53 | + "/2": { | ||
| 54 | + "/3": { | ||
| 55 | + "/4": { | ||
| 56 | + "/5": { | ||
| 57 | + "/6": { | ||
| 58 | + "/7": { | ||
| 59 | + "/8": { | ||
| 60 | + "/9": { | ||
| 61 | + "/10": { | ||
| 62 | + "/1": { | ||
| 63 | + "/2": { | ||
| 64 | + "/3": { | ||
| 65 | + "/4": { | ||
| 66 | + "/5": { | ||
| 67 | + "/6": { | ||
| 68 | + "/7": { | ||
| 69 | + "/8": { | ||
| 70 | + "/9": { | ||
| 71 | + "/10": { | ||
| 72 | + "/1": { | ||
| 73 | + "/2": { | ||
| 74 | + "/3": { | ||
| 75 | + "/4": { | ||
| 76 | + "/5": { | ||
| 77 | + "/6": { | ||
| 78 | + "/7": { | ||
| 79 | + "/8": { | ||
| 80 | + "/9": { | ||
| 81 | + "/10": { | ||
| 82 | + "/1": { | ||
| 83 | + "/2": { | ||
| 84 | + "/3": { | ||
| 85 | + "/4": { | ||
| 86 | + "/5": { | ||
| 87 | + "/6": { | ||
| 88 | + "/7": { | ||
| 89 | + "/8": { | ||
| 90 | + "/9": { | ||
| 91 | + "/10": { | ||
| 92 | + "/1": { | ||
| 93 | + "/2": { | ||
| 94 | + "/3": { | ||
| 95 | + "/4": { | ||
| 96 | + "/5": { | ||
| 97 | + "/6": { | ||
| 98 | + "/7": { | ||
| 99 | + "/8": { | ||
| 100 | + "/9": { | ||
| 101 | + "/10": 42 | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + }, | ||
| 28 | "/Pages": "2 0 R", | 162 | "/Pages": "2 0 R", |
| 29 | "/Type": "/Catalog", | 163 | "/Type": "/Catalog", |
| 30 | "n:/WeirdKey+#ba#da#cc#e5": 42 | 164 | "n:/WeirdKey+#ba#da#cc#e5": 42 |
| @@ -78,7 +212,7 @@ | @@ -78,7 +212,7 @@ | ||
| 78 | "value": { | 212 | "value": { |
| 79 | "/ID": [ | 213 | "/ID": [ |
| 80 | "b:42841c13bbf709d79a200fa1691836f8", | 214 | "b:42841c13bbf709d79a200fa1691836f8", |
| 81 | - "b:728c020f464c3cf7e02c12605fa7d88b" | 215 | + "b:31415926535897932384626433832795" |
| 82 | ], | 216 | ], |
| 83 | "/Root": "1 0 R", | 217 | "/Root": "1 0 R", |
| 84 | "/Size": 7 | 218 | "/Size": 7 |
qpdf/qtest/qpdf/weird-tokens.pdf
| @@ -4,21 +4,155 @@ | @@ -4,21 +4,155 @@ | ||
| 4 | 4 | ||
| 5 | 1 0 obj | 5 | 1 0 obj |
| 6 | << | 6 | << |
| 7 | + /Escape\Key 42 | ||
| 7 | /Extra [ | 8 | /Extra [ |
| 8 | (Names with binary data) | 9 | (Names with binary data) |
| 9 | /ABCDEF+#ba#da#cc#e5 | 10 | /ABCDEF+#ba#da#cc#e5 |
| 10 | /OVERLONG+#c0#81 | 11 | /OVERLONG+#c0#81 |
| 12 | + /OVERLONG+#c1#ff | ||
| 13 | + /Ok+#c2#80 | ||
| 11 | /OVERLONG+#e0#81#82 | 14 | /OVERLONG+#e0#81#82 |
| 15 | + /OVERLONG+#e0#9f#ff | ||
| 16 | + /Ok+#e0#a0#80 | ||
| 12 | /OVERLONG+#f0#81#82#83 | 17 | /OVERLONG+#f0#81#82#83 |
| 18 | + /OVERLONG+#f0#8f#ff#ff | ||
| 19 | + /Ok+#f0#90#80#80 | ||
| 13 | /range+#01 | 20 | /range+#01 |
| 14 | /low+#18 | 21 | /low+#18 |
| 15 | /ABCEDEF+#cf#80 | 22 | /ABCEDEF+#cf#80 |
| 16 | /one+#a0two | 23 | /one+#a0two |
| 17 | /text#2fplain | 24 | /text#2fplain |
| 25 | + (Names requiring escaping in JSON) | ||
| 26 | + /Back\shlash | ||
| 27 | + /Low" | ||
| 28 | + /Low#1f | ||
| 29 | + /ExceptSpace#20 | ||
| 30 | + /Except! | ||
| 18 | (Very small/large reals) | 31 | (Very small/large reals) |
| 19 | 0.00001 | 32 | 0.00001 |
| 20 | 1000000000000 | 33 | 1000000000000 |
| 21 | ] | 34 | ] |
| 35 | + /Nested << | ||
| 36 | + /1 << | ||
| 37 | + /2 << | ||
| 38 | + /3 << | ||
| 39 | + /4 << | ||
| 40 | + /5 << | ||
| 41 | + /6 << | ||
| 42 | + /7 << | ||
| 43 | + /8 << | ||
| 44 | + /9 << | ||
| 45 | + /10 << | ||
| 46 | + /1 << | ||
| 47 | + /2 << | ||
| 48 | + /3 << | ||
| 49 | + /4 << | ||
| 50 | + /5 << | ||
| 51 | + /6 << | ||
| 52 | + /7 << | ||
| 53 | + /8 << | ||
| 54 | + /9 << | ||
| 55 | + /10 << | ||
| 56 | + /1 << | ||
| 57 | + /2 << | ||
| 58 | + /3 << | ||
| 59 | + /4 << | ||
| 60 | + /5 << | ||
| 61 | + /6 << | ||
| 62 | + /7 << | ||
| 63 | + /8 << | ||
| 64 | + /9 << | ||
| 65 | + /10 << | ||
| 66 | + /1 << | ||
| 67 | + /2 << | ||
| 68 | + /3 << | ||
| 69 | + /4 << | ||
| 70 | + /5 << | ||
| 71 | + /6 << | ||
| 72 | + /7 << | ||
| 73 | + /8 << | ||
| 74 | + /9 << | ||
| 75 | + /10 << | ||
| 76 | + /1 << | ||
| 77 | + /2 << | ||
| 78 | + /3 << | ||
| 79 | + /4 << | ||
| 80 | + /5 << | ||
| 81 | + /6 << | ||
| 82 | + /7 << | ||
| 83 | + /8 << | ||
| 84 | + /9 << | ||
| 85 | + /10 << | ||
| 86 | + /1 << | ||
| 87 | + /2 << | ||
| 88 | + /3 << | ||
| 89 | + /4 << | ||
| 90 | + /5 << | ||
| 91 | + /6 << | ||
| 92 | + /7 << | ||
| 93 | + /8 << | ||
| 94 | + /9 << | ||
| 95 | + /10 42 | ||
| 96 | + >> | ||
| 97 | + >> | ||
| 98 | + >> | ||
| 99 | + >> | ||
| 100 | + >> | ||
| 101 | + >> | ||
| 102 | + >> | ||
| 103 | + >> | ||
| 104 | + >> | ||
| 105 | + >> | ||
| 106 | + >> | ||
| 107 | + >> | ||
| 108 | + >> | ||
| 109 | + >> | ||
| 110 | + >> | ||
| 111 | + >> | ||
| 112 | + >> | ||
| 113 | + >> | ||
| 114 | + >> | ||
| 115 | + >> | ||
| 116 | + >> | ||
| 117 | + >> | ||
| 118 | + >> | ||
| 119 | + >> | ||
| 120 | + >> | ||
| 121 | + >> | ||
| 122 | + >> | ||
| 123 | + >> | ||
| 124 | + >> | ||
| 125 | + >> | ||
| 126 | + >> | ||
| 127 | + >> | ||
| 128 | + >> | ||
| 129 | + >> | ||
| 130 | + >> | ||
| 131 | + >> | ||
| 132 | + >> | ||
| 133 | + >> | ||
| 134 | + >> | ||
| 135 | + >> | ||
| 136 | + >> | ||
| 137 | + >> | ||
| 138 | + >> | ||
| 139 | + >> | ||
| 140 | + >> | ||
| 141 | + >> | ||
| 142 | + >> | ||
| 143 | + >> | ||
| 144 | + >> | ||
| 145 | + >> | ||
| 146 | + >> | ||
| 147 | + >> | ||
| 148 | + >> | ||
| 149 | + >> | ||
| 150 | + >> | ||
| 151 | + >> | ||
| 152 | + >> | ||
| 153 | + >> | ||
| 154 | + >> | ||
| 155 | + >> | ||
| 22 | /Pages 2 0 R | 156 | /Pages 2 0 R |
| 23 | /Type /Catalog | 157 | /Type /Catalog |
| 24 | /WeirdKey+#ba#da#cc#e5 42 | 158 | /WeirdKey+#ba#da#cc#e5 42 |
| @@ -86,16 +220,16 @@ xref | @@ -86,16 +220,16 @@ xref | ||
| 86 | 0 7 | 220 | 0 7 |
| 87 | 0000000000 65535 f | 221 | 0000000000 65535 f |
| 88 | 0000000025 00000 n | 222 | 0000000025 00000 n |
| 89 | -0000000389 00000 n | ||
| 90 | -0000000471 00000 n | ||
| 91 | -0000000667 00000 n | ||
| 92 | -0000000766 00000 n | ||
| 93 | -0000000785 00000 n | 223 | +0000008642 00000 n |
| 224 | +0000008724 00000 n | ||
| 225 | +0000008920 00000 n | ||
| 226 | +0000009019 00000 n | ||
| 227 | +0000009038 00000 n | ||
| 94 | trailer << | 228 | trailer << |
| 95 | /Root 1 0 R | 229 | /Root 1 0 R |
| 96 | /Size 7 | 230 | /Size 7 |
| 97 | - /ID [<42841c13bbf709d79a200fa1691836f8><728c020f464c3cf7e02c12605fa7d88b>] | 231 | + /ID [<42841c13bbf709d79a200fa1691836f8><31415926535897932384626433832795>] |
| 98 | >> | 232 | >> |
| 99 | startxref | 233 | startxref |
| 100 | -891 | 234 | +9144 |
| 101 | %%EOF | 235 | %%EOF |
qpdf/test_driver.cc
| @@ -3382,6 +3382,22 @@ test_97(QPDF& pdf, char const* arg2) | @@ -3382,6 +3382,22 @@ test_97(QPDF& pdf, char const* arg2) | ||
| 3382 | assert(nulls.unparse() == nulls2.unparse()); | 3382 | assert(nulls.unparse() == nulls2.unparse()); |
| 3383 | } | 3383 | } |
| 3384 | 3384 | ||
| 3385 | +static void | ||
| 3386 | +test_98(QPDF& pdf, char const* arg2) | ||
| 3387 | +{ | ||
| 3388 | + // Test QPDFObjectHandle::writeJSON. This test is built for minimal.pdf. | ||
| 3389 | + for (int i = 1; i < 7; ++i) { | ||
| 3390 | + auto oh = pdf.getObject(i, 0); | ||
| 3391 | + Pl_Buffer bf1{"write", nullptr}; | ||
| 3392 | + Pl_Buffer bf2{"get", nullptr}; | ||
| 3393 | + oh.writeJSON(JSON::LATEST, &bf1, true, 7); | ||
| 3394 | + bf1.finish(); | ||
| 3395 | + oh.getJSON(JSON::LATEST, true).write(&bf2, 7); | ||
| 3396 | + bf2.finish(); | ||
| 3397 | + assert(bf1.getString() == bf2.getString()); | ||
| 3398 | + } | ||
| 3399 | +} | ||
| 3400 | + | ||
| 3385 | void | 3401 | void |
| 3386 | runtest(int n, char const* filename1, char const* arg2) | 3402 | runtest(int n, char const* filename1, char const* arg2) |
| 3387 | { | 3403 | { |
| @@ -3483,7 +3499,7 @@ runtest(int n, char const* filename1, char const* arg2) | @@ -3483,7 +3499,7 @@ runtest(int n, char const* filename1, char const* arg2) | ||
| 3483 | {78, test_78}, {79, test_79}, {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, | 3499 | {78, test_78}, {79, test_79}, {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, |
| 3484 | {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, {88, test_88}, {89, test_89}, | 3500 | {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, {88, test_88}, {89, test_89}, |
| 3485 | {90, test_90}, {91, test_91}, {92, test_92}, {93, test_93}, {94, test_94}, {95, test_95}, | 3501 | {90, test_90}, {91, test_91}, {92, test_92}, {93, test_93}, {94, test_94}, {95, test_95}, |
| 3486 | - {96, test_96}, {97, test_97}}; | 3502 | + {96, test_96}, {97, test_97}, {98, test_98}}; |
| 3487 | 3503 | ||
| 3488 | auto fn = test_functions.find(n); | 3504 | auto fn = test_functions.find(n); |
| 3489 | if (fn == test_functions.end()) { | 3505 | if (fn == test_functions.end()) { |