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 | 290 | QPDF_DLL |
| 291 | 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 | 297 | private: |
| 294 | - static std::string encode_string(std::string const& utf8); | |
| 295 | 298 | static void writeClose(Pipeline* p, bool first, size_t depth, char const* delimeter); |
| 296 | 299 | |
| 297 | 300 | enum value_type_e { | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -1411,19 +1411,6 @@ class QPDF |
| 1411 | 1411 | // JSON import |
| 1412 | 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 | 1414 | // Type conversion helper methods |
| 1428 | 1415 | template <typename T> |
| 1429 | 1416 | static qpdf_offset_t | ... | ... |
include/qpdf/QPDFJob.hh
| ... | ... | @@ -551,7 +551,6 @@ class QPDFJob |
| 551 | 551 | // JSON |
| 552 | 552 | void doJSON(QPDF& pdf, Pipeline*); |
| 553 | 553 | QPDFObjGen::set getWantedJSONObjects(); |
| 554 | - void doJSONObject(Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&); | |
| 555 | 554 | void doJSONObjects(Pipeline* p, bool& first, QPDF& pdf); |
| 556 | 555 | void doJSONObjectinfo(Pipeline* p, bool& first, QPDF& pdf); |
| 557 | 556 | void doJSONPages(Pipeline* p, bool& first, QPDF& pdf); | ... | ... |
include/qpdf/QPDFObjectHandle.hh
| ... | ... | @@ -1197,6 +1197,13 @@ class QPDFObjectHandle |
| 1197 | 1197 | QPDF_DLL |
| 1198 | 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 | 1207 | // Deprecated version uses v1 for backward compatibility. |
| 1201 | 1208 | // ABI: remove for qpdf 12 |
| 1202 | 1209 | [[deprecated("Use getJSON(int version)")]] QPDF_DLL JSON |
| ... | ... | @@ -1353,6 +1360,8 @@ class QPDFObjectHandle |
| 1353 | 1360 | return obj.get(); |
| 1354 | 1361 | } |
| 1355 | 1362 | |
| 1363 | + void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false); | |
| 1364 | + | |
| 1356 | 1365 | private: |
| 1357 | 1366 | QPDF_Array* asArray(); |
| 1358 | 1367 | QPDF_Bool* asBool(); | ... | ... |
libqpdf/JSON.cc
| 1 | 1 | #include <qpdf/JSON.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 4 | + | |
| 3 | 5 | #include <qpdf/BufferInputSource.hh> |
| 4 | 6 | #include <qpdf/Pl_Base64.hh> |
| 5 | 7 | #include <qpdf/Pl_Concatenate.hh> |
| ... | ... | @@ -119,7 +121,7 @@ JSON::JSON_array::write(Pipeline* p, size_t depth) const |
| 119 | 121 | JSON::JSON_string::JSON_string(std::string const& utf8) : |
| 120 | 122 | JSON_value(vt_string), |
| 121 | 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 | 213 | } |
| 212 | 214 | |
| 213 | 215 | std::string |
| 214 | -JSON::encode_string(std::string const& str) | |
| 216 | +JSON::Writer::encode_string(std::string const& str) | |
| 215 | 217 | { |
| 216 | 218 | static auto constexpr hexchars = "0123456789abcdef"; |
| 217 | 219 | |
| ... | ... | @@ -279,7 +281,7 @@ JSON |
| 279 | 281 | JSON::addDictionaryMember(std::string const& key, JSON const& val) |
| 280 | 282 | { |
| 281 | 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 | 285 | } else { |
| 284 | 286 | throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); |
| 285 | 287 | } | ... | ... |
libqpdf/QPDFJob.cc
| ... | ... | @@ -955,23 +955,6 @@ QPDFJob::getWantedJSONObjects() |
| 955 | 955 | } |
| 956 | 956 | |
| 957 | 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 | 958 | QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) |
| 976 | 959 | { |
| 977 | 960 | if (m->json_version == 1) { |
| ... | ... | @@ -982,16 +965,17 @@ QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) |
| 982 | 965 | auto wanted_og = getWantedJSONObjects(); |
| 983 | 966 | for (auto& obj: pdf.getAllObjects()) { |
| 984 | 967 | std::string key = obj.unparse(); |
| 985 | - if (m->json_version > 1) { | |
| 986 | - key = "obj:" + key; | |
| 987 | - } | |
| 968 | + | |
| 988 | 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 | 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 | 980 | JSON::writeDictionaryClose(p, first_object, 1); |
| 997 | 981 | } else { |
| ... | ... | @@ -3097,9 +3081,10 @@ QPDFJob::writeOutfile(QPDF& pdf) |
| 3097 | 3081 | try { |
| 3098 | 3082 | QUtil::remove_file(backup.c_str()); |
| 3099 | 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 | 3 | #include <qpdf/BufferInputSource.hh> |
| 4 | 4 | #include <qpdf/Pl_Buffer.hh> |
| 5 | 5 | #include <qpdf/Pl_QPDFTokenizer.hh> |
| 6 | +#include <qpdf/JSON_writer.hh> | |
| 6 | 7 | #include <qpdf/QPDF.hh> |
| 7 | 8 | #include <qpdf/QPDFExc.hh> |
| 8 | 9 | #include <qpdf/QPDFLogger.hh> |
| ... | ... | @@ -1617,10 +1618,33 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) |
| 1617 | 1618 | } else if (!dereference()) { |
| 1618 | 1619 | throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); |
| 1619 | 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 | 1648 | JSON |
| 1625 | 1649 | QPDFObjectHandle::getStreamJSON( |
| 1626 | 1650 | int json_version, | ... | ... |
libqpdf/QPDF_Array.cc
| 1 | 1 | #include <qpdf/QPDF_Array.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QPDFObjectHandle.hh> |
| 4 | 5 | #include <qpdf/QPDFObject_private.hh> |
| 5 | 6 | #include <qpdf/QTC.hh> |
| ... | ... | @@ -148,36 +149,41 @@ QPDF_Array::unparse() |
| 148 | 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 | 156 | if (sp) { |
| 157 | 157 | int next = 0; |
| 158 | 158 | for (auto& item: sp->elements) { |
| 159 | 159 | int key = item.first; |
| 160 | 160 | for (int j = next; j < key; ++j) { |
| 161 | - j_array.addArrayElement(j_null); | |
| 161 | + p.writeNext() << "null"; | |
| 162 | 162 | } |
| 163 | + p.writeNext(); | |
| 163 | 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 | 170 | next = ++key; |
| 168 | 171 | } |
| 169 | 172 | for (int j = next; j < sp->size; ++j) { |
| 170 | - j_array.addArrayElement(j_null); | |
| 173 | + p.writeNext() << "null"; | |
| 171 | 174 | } |
| 172 | 175 | } else { |
| 173 | 176 | for (auto const& item: elements) { |
| 177 | + p.writeNext(); | |
| 174 | 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 | 189 | QPDFObjectHandle | ... | ... |
libqpdf/QPDF_Bool.cc
| 1 | 1 | #include <qpdf/QPDF_Bool.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 4 | + | |
| 3 | 5 | QPDF_Bool::QPDF_Bool(bool val) : |
| 4 | 6 | QPDFValue(::ot_boolean, "boolean"), |
| 5 | 7 | val(val) |
| ... | ... | @@ -24,10 +26,10 @@ QPDF_Bool::unparse() |
| 24 | 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 | 35 | bool | ... | ... |
libqpdf/QPDF_Destroyed.cc
| ... | ... | @@ -28,9 +28,8 @@ QPDF_Destroyed::unparse() |
| 28 | 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 | 34 | throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF"); |
| 35 | - return JSON::makeNull(); | |
| 36 | -} | |
| 35 | +} | |
| 37 | 36 | \ No newline at end of file | ... | ... |
libqpdf/QPDF_Dictionary.cc
| 1 | 1 | #include <qpdf/QPDF_Dictionary.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QPDFObject_private.hh> |
| 4 | 5 | #include <qpdf/QPDF_Name.hh> |
| 5 | 6 | #include <qpdf/QPDF_Null.hh> |
| ... | ... | @@ -67,28 +68,30 @@ QPDF_Dictionary::unparse() |
| 67 | 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 | 75 | for (auto& iter: this->items) { |
| 75 | 76 | if (!iter.second.isNull()) { |
| 77 | + p.writeNext(); | |
| 76 | 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 | 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 | 97 | bool | ... | ... |
libqpdf/QPDF_InlineImage.cc
| 1 | 1 | #include <qpdf/QPDF_InlineImage.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 4 | + | |
| 3 | 5 | QPDF_InlineImage::QPDF_InlineImage(std::string const& val) : |
| 4 | 6 | QPDFValue(::ot_inlineimage, "inline-image"), |
| 5 | 7 | val(val) |
| ... | ... | @@ -24,8 +26,8 @@ QPDF_InlineImage::unparse() |
| 24 | 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 | 1 | #include <qpdf/QPDF_Integer.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | |
| 5 | 6 | QPDF_Integer::QPDF_Integer(long long val) : |
| ... | ... | @@ -26,10 +27,10 @@ QPDF_Integer::unparse() |
| 26 | 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 | 36 | long long | ... | ... |
libqpdf/QPDF_Name.cc
| 1 | 1 | #include <qpdf/QPDF_Name.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | |
| 6 | +#include <string_view> | |
| 7 | + | |
| 5 | 8 | QPDF_Name::QPDF_Name(std::string const& name) : |
| 6 | 9 | QPDFValue(::ot_name, "name"), |
| 7 | 10 | name(name) |
| ... | ... | @@ -51,20 +54,71 @@ QPDF_Name::unparse() |
| 51 | 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 | 111 | if (json_version == 1) { |
| 58 | - return JSON::makeString(normalizeName(this->name)); | |
| 112 | + p << "\"" << JSON::Writer::encode_string(normalizeName(name)) << "\""; | |
| 59 | 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 | 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 | 1 | #include <qpdf/QPDF_Null.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QPDFObject_private.hh> |
| 4 | 5 | |
| 5 | 6 | QPDF_Null::QPDF_Null() : |
| ... | ... | @@ -43,9 +44,8 @@ QPDF_Null::unparse() |
| 43 | 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 | 1 | #include <qpdf/QPDF_Operator.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 4 | + | |
| 3 | 5 | QPDF_Operator::QPDF_Operator(std::string const& val) : |
| 4 | 6 | QPDFValue(::ot_operator, "operator"), |
| 5 | 7 | val(val) |
| ... | ... | @@ -24,8 +26,8 @@ QPDF_Operator::unparse() |
| 24 | 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 | 1 | #include <qpdf/QPDF_Real.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | |
| 5 | 6 | QPDF_Real::QPDF_Real(std::string const& val) : |
| ... | ... | @@ -38,21 +39,17 @@ QPDF_Real::unparse() |
| 38 | 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 | 45 | if (this->val.length() == 0) { |
| 48 | 46 | // Can't really happen... |
| 49 | - result = "0"; | |
| 47 | + p << "0"; | |
| 50 | 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 | 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 | 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 | 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 | 1 | #include <qpdf/QPDF_Stream.hh> |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/ContentNormalizer.hh> |
| 4 | +#include <qpdf/JSON_writer.hh> | |
| 4 | 5 | #include <qpdf/Pipeline.hh> |
| 5 | 6 | #include <qpdf/Pl_Base64.hh> |
| 6 | 7 | #include <qpdf/Pl_Buffer.hh> |
| ... | ... | @@ -176,13 +177,10 @@ QPDF_Stream::unparse() |
| 176 | 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 | 186 | JSON |
| ... | ... | @@ -193,77 +191,108 @@ QPDF_Stream::getStreamJSON( |
| 193 | 191 | Pipeline* p, |
| 194 | 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 | 216 | switch (json_data) { |
| 197 | 217 | case qpdf_sj_none: |
| 198 | 218 | case qpdf_sj_inline: |
| 199 | 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 | 221 | "when json_data is file"); |
| 202 | 222 | } |
| 203 | 223 | break; |
| 204 | 224 | case qpdf_sj_file: |
| 205 | 225 | if (p == nullptr) { |
| 206 | 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 | 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 | 231 | "when json_data is file"); |
| 212 | 232 | } |
| 213 | 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 | 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 | 298 | void | ... | ... |
libqpdf/QPDF_String.cc
| 1 | 1 | #include <qpdf/QPDF_String.hh> |
| 2 | 2 | |
| 3 | +#include <qpdf/JSON_writer.hh> | |
| 3 | 4 | #include <qpdf/QUtil.hh> |
| 4 | 5 | |
| 5 | 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 | 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 | 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 | 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 | 73 | bool | ... | ... |
libqpdf/QPDF_Unresolved.cc
| ... | ... | @@ -27,9 +27,8 @@ QPDF_Unresolved::unparse() |
| 27 | 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 | 33 | throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle"); |
| 34 | - return JSON::makeNull(); | |
| 35 | 34 | } | ... | ... |
libqpdf/QPDF_json.cc
| 1 | 1 | #include <qpdf/QPDF.hh> |
| 2 | 2 | |
| 3 | 3 | #include <qpdf/FileInputSource.hh> |
| 4 | +#include <qpdf/JSON_writer.hh> | |
| 4 | 5 | #include <qpdf/Pl_Base64.hh> |
| 5 | 6 | #include <qpdf/Pl_StdioFile.hh> |
| 6 | 7 | #include <qpdf/QIntC.hh> |
| 7 | 8 | #include <qpdf/QPDFObject_private.hh> |
| 8 | 9 | #include <qpdf/QPDFValue.hh> |
| 9 | 10 | #include <qpdf/QPDF_Null.hh> |
| 11 | +#include <qpdf/QPDF_Stream.hh> | |
| 10 | 12 | #include <qpdf/QTC.hh> |
| 11 | 13 | #include <qpdf/QUtil.hh> |
| 12 | 14 | #include <algorithm> |
| ... | ... | @@ -442,7 +444,9 @@ void |
| 442 | 444 | QPDF::JSONReactor::replaceObject(QPDFObjectHandle&& replacement, JSON const& value) |
| 443 | 445 | { |
| 444 | 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 | 450 | return; |
| 447 | 451 | } |
| 448 | 452 | auto& tos = stack.back(); |
| ... | ... | @@ -828,45 +832,20 @@ QPDF::importJSON(std::shared_ptr<InputSource> is, bool must_be_complete) |
| 828 | 832 | } |
| 829 | 833 | |
| 830 | 834 | void |
| 831 | -QPDF::writeJSONStream( | |
| 835 | +writeJSONStreamFile( | |
| 832 | 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 | 840 | qpdf_stream_decode_level_e decode_level, |
| 838 | - qpdf_json_stream_data_e json_stream_data, | |
| 839 | 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 | 851 | void |
| ... | ... | @@ -893,80 +872,75 @@ QPDF::writeJSON( |
| 893 | 872 | std::string const& file_prefix, |
| 894 | 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 | 875 | if (version != 2) { |
| 902 | 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 | 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 | 899 | bool all_objects = wanted_objects.empty(); |
| 900 | + bool first = true; | |
| 942 | 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 | 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 | 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 | 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 | 942 | if (complete) { |
| 967 | - JSON::writeDictionaryClose(p, first, 0); | |
| 968 | - *p << "\n"; | |
| 943 | + jw << "\n}\n"; | |
| 969 | 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 | 33 | { |
| 34 | 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 | 41 | std::string |
| 42 | 42 | getStringValue() const | ... | ... |
libqpdf/qpdf/QPDFValue.hh
| ... | ... | @@ -24,7 +24,7 @@ class QPDFValue: public std::enable_shared_from_this<QPDFValue> |
| 24 | 24 | |
| 25 | 25 | virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0; |
| 26 | 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 | 29 | struct JSON_Descr |
| 30 | 30 | { | ... | ... |
libqpdf/qpdf/QPDF_Array.hh
| ... | ... | @@ -22,7 +22,7 @@ class QPDF_Array: public QPDFValue |
| 22 | 22 | create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); |
| 23 | 23 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 24 | 24 | std::string unparse() override; |
| 25 | - JSON getJSON(int json_version) override; | |
| 25 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 26 | 26 | void disconnect() override; |
| 27 | 27 | |
| 28 | 28 | int | ... | ... |
libqpdf/qpdf/QPDF_Bool.hh
| ... | ... | @@ -10,7 +10,8 @@ class QPDF_Bool: public QPDFValue |
| 10 | 10 | static std::shared_ptr<QPDFObject> create(bool val); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 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 | bool getVal() const; |
| 15 | 16 | |
| 16 | 17 | private: | ... | ... |
libqpdf/qpdf/QPDF_Destroyed.hh
| ... | ... | @@ -9,7 +9,7 @@ class QPDF_Destroyed: public QPDFValue |
| 9 | 9 | ~QPDF_Destroyed() override = default; |
| 10 | 10 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 11 | 11 | std::string unparse() override; |
| 12 | - JSON getJSON(int json_version) override; | |
| 12 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 13 | 13 | static std::shared_ptr<QPDFValue> getInstance(); |
| 14 | 14 | |
| 15 | 15 | private: | ... | ... |
libqpdf/qpdf/QPDF_Dictionary.hh
| ... | ... | @@ -16,7 +16,7 @@ class QPDF_Dictionary: public QPDFValue |
| 16 | 16 | static std::shared_ptr<QPDFObject> create(std::map<std::string, QPDFObjectHandle>&& items); |
| 17 | 17 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 18 | 18 | std::string unparse() override; |
| 19 | - JSON getJSON(int json_version) override; | |
| 19 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 20 | 20 | void disconnect() override; |
| 21 | 21 | |
| 22 | 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 | 10 | static std::shared_ptr<QPDFObject> create(std::string const& val); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | |
| 13 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 14 | 14 | std::string |
| 15 | 15 | getStringValue() const override |
| 16 | 16 | { | ... | ... |
libqpdf/qpdf/QPDF_Integer.hh
| ... | ... | @@ -10,7 +10,7 @@ class QPDF_Integer: public QPDFValue |
| 10 | 10 | static std::shared_ptr<QPDFObject> create(long long value); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | |
| 13 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 14 | 14 | long long getVal() const; |
| 15 | 15 | |
| 16 | 16 | private: | ... | ... |
libqpdf/qpdf/QPDF_Name.hh
| ... | ... | @@ -10,10 +10,15 @@ class QPDF_Name: public QPDFValue |
| 10 | 10 | static std::shared_ptr<QPDFObject> create(std::string const& name); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 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 | 15 | // Put # into strings with characters unsuitable for name token |
| 16 | 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 | 22 | std::string |
| 18 | 23 | getStringValue() const override |
| 19 | 24 | { | ... | ... |
libqpdf/qpdf/QPDF_Null.hh
| ... | ... | @@ -18,7 +18,7 @@ class QPDF_Null: public QPDFValue |
| 18 | 18 | std::string var_descr); |
| 19 | 19 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 20 | 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 | 23 | private: |
| 24 | 24 | QPDF_Null(); | ... | ... |
libqpdf/qpdf/QPDF_Operator.hh
| ... | ... | @@ -10,7 +10,7 @@ class QPDF_Operator: public QPDFValue |
| 10 | 10 | static std::shared_ptr<QPDFObject> create(std::string const& val); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 12 | std::string unparse() override; |
| 13 | - JSON getJSON(int json_version) override; | |
| 13 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 14 | 14 | std::string |
| 15 | 15 | getStringValue() const override |
| 16 | 16 | { | ... | ... |
libqpdf/qpdf/QPDF_Real.hh
| ... | ... | @@ -12,7 +12,7 @@ class QPDF_Real: public QPDFValue |
| 12 | 12 | create(double value, int decimal_places, bool trim_trailing_zeroes); |
| 13 | 13 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 14 | 14 | std::string unparse() override; |
| 15 | - JSON getJSON(int json_version) override; | |
| 15 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 16 | 16 | std::string |
| 17 | 17 | getStringValue() const override |
| 18 | 18 | { | ... | ... |
libqpdf/qpdf/QPDF_Reserved.hh
| ... | ... | @@ -10,7 +10,7 @@ class QPDF_Reserved: public QPDFValue |
| 10 | 10 | static std::shared_ptr<QPDFObject> create(); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 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 | 15 | private: |
| 16 | 16 | QPDF_Reserved(); | ... | ... |
libqpdf/qpdf/QPDF_Stream.hh
| ... | ... | @@ -25,7 +25,7 @@ class QPDF_Stream: public QPDFValue |
| 25 | 25 | size_t length); |
| 26 | 26 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 27 | 27 | std::string unparse() override; |
| 28 | - JSON getJSON(int json_version) override; | |
| 28 | + void writeJSON(int json_version, JSON::Writer& p) override; | |
| 29 | 29 | void setDescription( |
| 30 | 30 | QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) override; |
| 31 | 31 | void disconnect() override; |
| ... | ... | @@ -64,6 +64,14 @@ class QPDF_Stream: public QPDFValue |
| 64 | 64 | qpdf_stream_decode_level_e decode_level, |
| 65 | 65 | Pipeline* p, |
| 66 | 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 | 76 | void replaceDict(QPDFObjectHandle const& new_dict); |
| 69 | 77 | ... | ... |
libqpdf/qpdf/QPDF_String.hh
| ... | ... | @@ -16,7 +16,7 @@ class QPDF_String: public QPDFValue |
| 16 | 16 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 17 | 17 | std::string unparse() override; |
| 18 | 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 | 20 | std::string getUTF8Val() const; |
| 21 | 21 | std::string |
| 22 | 22 | getStringValue() const override | ... | ... |
libqpdf/qpdf/QPDF_Unresolved.hh
| ... | ... | @@ -10,7 +10,7 @@ class QPDF_Unresolved: public QPDFValue |
| 10 | 10 | static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen const& og); |
| 11 | 11 | std::shared_ptr<QPDFObject> copy(bool shallow = false) override; |
| 12 | 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 | 15 | private: |
| 16 | 16 | QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og); | ... | ... |
qpdf/qtest/many-nulls.test
| ... | ... | @@ -33,5 +33,22 @@ $td->runtest("copy sparse array", |
| 33 | 33 | {$td->COMMAND => "test_driver 97 many-nulls.pdf"}, |
| 34 | 34 | {$td->STRING => "test 97 done\n", $td->EXIT_STATUS => 0}, |
| 35 | 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 | 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 | 350 | # (using #xx) would generate invalid JSON, even though qpdf's own JSON |
| 351 | 351 | # parser would accept it. Also, the JSON spec allows real numbers in |
| 352 | 352 | # scientific notation, but the PDF spec does not. |
| 353 | -$n_tests += 4; | |
| 353 | +$n_tests += 7; | |
| 354 | 354 | $td->runtest("handle binary names", |
| 355 | 355 | {$td->COMMAND => |
| 356 | 356 | "qpdf --json-output weird-tokens.pdf a.json"}, |
| ... | ... | @@ -371,6 +371,17 @@ $td->runtest("weird tokens with scientific notation", |
| 371 | 371 | "qpdf --json-input --json-output weird-tokens-alt.json -"}, |
| 372 | 372 | {$td->FILE => "weird-tokens.json", $td->EXIT_STATUS => 0}, |
| 373 | 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 | 386 | cleanup(); |
| 376 | 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 | 10 | { |
| 11 | 11 | "obj:1 0 R": { |
| 12 | 12 | "value": { |
| 13 | + "/Escape\\Key": 42, | |
| 13 | 14 | "/Extra": [ |
| 14 | 15 | "u:Names with binary data", |
| 15 | 16 | "n:/ABCDEF+#ba#da#cc#e5", |
| 16 | 17 | "n:/OVERLONG+#c0#81", |
| 18 | + "n:/OVERLONG+#c1#ff", | |
| 19 | + "/Ok+ย", | |
| 17 | 20 | "n:/OVERLONG+#e0#81#82", |
| 21 | + "n:/OVERLONG+#e0#9f#ff", | |
| 22 | + "/Ok+เ ", | |
| 18 | 23 | "n:/OVERLONG+#f0#81#82#83", |
| 24 | + "n:/OVERLONG+#f0#8f#ff#ff", | |
| 25 | + "/Ok+๐", | |
| 19 | 26 | "n:/range+#01", |
| 20 | 27 | "n:/low+#18", |
| 21 | 28 | "/ABCEDEF+ฯ", |
| 22 | 29 | "n:/one+#a0two", |
| 23 | 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 | 37 | "u:Very small/large reals", |
| 25 | 38 | 1e-05, |
| 26 | 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 | 162 | "/Pages": "2 0 R", |
| 29 | 163 | "/Type": "/Catalog", |
| 30 | 164 | "n:/WeirdKey+#ba#da#cc#e5": 42 |
| ... | ... | @@ -78,7 +212,7 @@ |
| 78 | 212 | "value": { |
| 79 | 213 | "/ID": [ |
| 80 | 214 | "b:42841c13bbf709d79a200fa1691836f8", |
| 81 | - "b:728c020f464c3cf7e02c12605fa7d88b" | |
| 215 | + "b:31415926535897932384626433832795" | |
| 82 | 216 | ], |
| 83 | 217 | "/Root": "1 0 R", |
| 84 | 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 | 10 | { |
| 11 | 11 | "obj:1 0 R": { |
| 12 | 12 | "value": { |
| 13 | + "/Escape\\Key": 42, | |
| 13 | 14 | "/Extra": [ |
| 14 | 15 | "u:Names with binary data", |
| 15 | 16 | "n:/ABCDEF+#ba#da#cc#e5", |
| 16 | 17 | "n:/OVERLONG+#c0#81", |
| 18 | + "n:/OVERLONG+#c1#ff", | |
| 19 | + "/Ok+ย", | |
| 17 | 20 | "n:/OVERLONG+#e0#81#82", |
| 21 | + "n:/OVERLONG+#e0#9f#ff", | |
| 22 | + "/Ok+เ ", | |
| 18 | 23 | "n:/OVERLONG+#f0#81#82#83", |
| 24 | + "n:/OVERLONG+#f0#8f#ff#ff", | |
| 25 | + "/Ok+๐", | |
| 19 | 26 | "/range+\u0001", |
| 20 | 27 | "/low+\u0018", |
| 21 | 28 | "/ABCEDEF+ฯ", |
| 22 | 29 | "n:/one+#a0two", |
| 23 | 30 | "/text/plain", |
| 31 | + "u:Names requiring escaping in JSON", | |
| 32 | + "/Back\\shlash", | |
| 33 | + "/Low\"", | |
| 34 | + "/Low\u001f", | |
| 35 | + "/ExceptSpace ", | |
| 36 | + "/Except!", | |
| 24 | 37 | "u:Very small/large reals", |
| 25 | 38 | 0.00001, |
| 26 | 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 | 162 | "/Pages": "2 0 R", |
| 29 | 163 | "/Type": "/Catalog", |
| 30 | 164 | "n:/WeirdKey+#ba#da#cc#e5": 42 |
| ... | ... | @@ -78,7 +212,7 @@ |
| 78 | 212 | "value": { |
| 79 | 213 | "/ID": [ |
| 80 | 214 | "b:42841c13bbf709d79a200fa1691836f8", |
| 81 | - "b:728c020f464c3cf7e02c12605fa7d88b" | |
| 215 | + "b:31415926535897932384626433832795" | |
| 82 | 216 | ], |
| 83 | 217 | "/Root": "1 0 R", |
| 84 | 218 | "/Size": 7 | ... | ... |
qpdf/qtest/qpdf/weird-tokens.pdf
| ... | ... | @@ -4,21 +4,155 @@ |
| 4 | 4 | |
| 5 | 5 | 1 0 obj |
| 6 | 6 | << |
| 7 | + /Escape\Key 42 | |
| 7 | 8 | /Extra [ |
| 8 | 9 | (Names with binary data) |
| 9 | 10 | /ABCDEF+#ba#da#cc#e5 |
| 10 | 11 | /OVERLONG+#c0#81 |
| 12 | + /OVERLONG+#c1#ff | |
| 13 | + /Ok+#c2#80 | |
| 11 | 14 | /OVERLONG+#e0#81#82 |
| 15 | + /OVERLONG+#e0#9f#ff | |
| 16 | + /Ok+#e0#a0#80 | |
| 12 | 17 | /OVERLONG+#f0#81#82#83 |
| 18 | + /OVERLONG+#f0#8f#ff#ff | |
| 19 | + /Ok+#f0#90#80#80 | |
| 13 | 20 | /range+#01 |
| 14 | 21 | /low+#18 |
| 15 | 22 | /ABCEDEF+#cf#80 |
| 16 | 23 | /one+#a0two |
| 17 | 24 | /text#2fplain |
| 25 | + (Names requiring escaping in JSON) | |
| 26 | + /Back\shlash | |
| 27 | + /Low" | |
| 28 | + /Low#1f | |
| 29 | + /ExceptSpace#20 | |
| 30 | + /Except! | |
| 18 | 31 | (Very small/large reals) |
| 19 | 32 | 0.00001 |
| 20 | 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 | 156 | /Pages 2 0 R |
| 23 | 157 | /Type /Catalog |
| 24 | 158 | /WeirdKey+#ba#da#cc#e5 42 |
| ... | ... | @@ -86,16 +220,16 @@ xref |
| 86 | 220 | 0 7 |
| 87 | 221 | 0000000000 65535 f |
| 88 | 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 | 228 | trailer << |
| 95 | 229 | /Root 1 0 R |
| 96 | 230 | /Size 7 |
| 97 | - /ID [<42841c13bbf709d79a200fa1691836f8><728c020f464c3cf7e02c12605fa7d88b>] | |
| 231 | + /ID [<42841c13bbf709d79a200fa1691836f8><31415926535897932384626433832795>] | |
| 98 | 232 | >> |
| 99 | 233 | startxref |
| 100 | -891 | |
| 234 | +9144 | |
| 101 | 235 | %%EOF | ... | ... |
qpdf/test_driver.cc
| ... | ... | @@ -3382,6 +3382,22 @@ test_97(QPDF& pdf, char const* arg2) |
| 3382 | 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 | 3401 | void |
| 3386 | 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 | 3499 | {78, test_78}, {79, test_79}, {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, |
| 3484 | 3500 | {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, {88, test_88}, {89, test_89}, |
| 3485 | 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 | 3504 | auto fn = test_functions.find(n); |
| 3489 | 3505 | if (fn == test_functions.end()) { | ... | ... |