mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
Merge branch 'jw' from #1146 into work
This commit is contained in:
commit
e362bce8e8
@ -290,8 +290,11 @@ class JSON
|
||||
QPDF_DLL
|
||||
qpdf_offset_t getEnd() const;
|
||||
|
||||
// The following class does not form part of the public API and is for internal use only.
|
||||
|
||||
class Writer;
|
||||
|
||||
private:
|
||||
static std::string encode_string(std::string const& utf8);
|
||||
static void writeClose(Pipeline* p, bool first, size_t depth, char const* delimeter);
|
||||
|
||||
enum value_type_e {
|
||||
|
@ -1411,19 +1411,6 @@ class QPDF
|
||||
// JSON import
|
||||
void importJSON(std::shared_ptr<InputSource>, bool must_be_complete);
|
||||
|
||||
// JSON write
|
||||
void writeJSONStream(
|
||||
int version,
|
||||
Pipeline* p,
|
||||
bool& first,
|
||||
std::string const& key,
|
||||
QPDFObjectHandle&,
|
||||
qpdf_stream_decode_level_e,
|
||||
qpdf_json_stream_data_e,
|
||||
std::string const& file_prefix);
|
||||
void writeJSONObject(
|
||||
int version, Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&);
|
||||
|
||||
// Type conversion helper methods
|
||||
template <typename T>
|
||||
static qpdf_offset_t
|
||||
|
@ -551,7 +551,6 @@ class QPDFJob
|
||||
// JSON
|
||||
void doJSON(QPDF& pdf, Pipeline*);
|
||||
QPDFObjGen::set getWantedJSONObjects();
|
||||
void doJSONObject(Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle&);
|
||||
void doJSONObjects(Pipeline* p, bool& first, QPDF& pdf);
|
||||
void doJSONObjectinfo(Pipeline* p, bool& first, QPDF& pdf);
|
||||
void doJSONPages(Pipeline* p, bool& first, QPDF& pdf);
|
||||
|
@ -1197,6 +1197,13 @@ class QPDFObjectHandle
|
||||
QPDF_DLL
|
||||
JSON getJSON(int json_version, bool dereference_indirect = false);
|
||||
|
||||
// Write the object encoded as JSON to a pipeline. This is equivalent to, but more efficient
|
||||
// than, calling getJSON(json_version, dereference_indirect).write(p, depth). See the
|
||||
// documentation for getJSON and JSON::write for further detail.
|
||||
QPDF_DLL
|
||||
void
|
||||
writeJSON(int json_version, Pipeline* p, bool dereference_indirect = false, size_t depth = 0);
|
||||
|
||||
// Deprecated version uses v1 for backward compatibility.
|
||||
// ABI: remove for qpdf 12
|
||||
[[deprecated("Use getJSON(int version)")]] QPDF_DLL JSON
|
||||
@ -1353,6 +1360,8 @@ class QPDFObjectHandle
|
||||
return obj.get();
|
||||
}
|
||||
|
||||
void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false);
|
||||
|
||||
private:
|
||||
QPDF_Array* asArray();
|
||||
QPDF_Bool* asBool();
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <qpdf/JSON.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
|
||||
#include <qpdf/BufferInputSource.hh>
|
||||
#include <qpdf/Pl_Base64.hh>
|
||||
#include <qpdf/Pl_Concatenate.hh>
|
||||
@ -119,7 +121,7 @@ JSON::JSON_array::write(Pipeline* p, size_t depth) const
|
||||
JSON::JSON_string::JSON_string(std::string const& utf8) :
|
||||
JSON_value(vt_string),
|
||||
utf8(utf8),
|
||||
encoded(encode_string(utf8))
|
||||
encoded(Writer::encode_string(utf8))
|
||||
{
|
||||
}
|
||||
|
||||
@ -211,7 +213,7 @@ JSON::unparse() const
|
||||
}
|
||||
|
||||
std::string
|
||||
JSON::encode_string(std::string const& str)
|
||||
JSON::Writer::encode_string(std::string const& str)
|
||||
{
|
||||
static auto constexpr hexchars = "0123456789abcdef";
|
||||
|
||||
@ -279,7 +281,7 @@ JSON
|
||||
JSON::addDictionaryMember(std::string const& key, JSON const& val)
|
||||
{
|
||||
if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) {
|
||||
return obj->members[encode_string(key)] = val.m ? val : makeNull();
|
||||
return obj->members[Writer::encode_string(key)] = val.m ? val : makeNull();
|
||||
} else {
|
||||
throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary");
|
||||
}
|
||||
|
@ -954,23 +954,6 @@ QPDFJob::getWantedJSONObjects()
|
||||
return wanted_og;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFJob::doJSONObject(Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle& obj)
|
||||
{
|
||||
if (m->json_version == 1) {
|
||||
JSON::writeDictionaryItem(p, first, key, obj.getJSON(1, true), 2);
|
||||
} else {
|
||||
auto j = JSON::makeDictionary();
|
||||
if (obj.isStream()) {
|
||||
j.addDictionaryMember("stream", JSON::makeDictionary())
|
||||
.addDictionaryMember("dict", obj.getDict().getJSON(m->json_version, true));
|
||||
} else {
|
||||
j.addDictionaryMember("value", obj.getJSON(m->json_version, true));
|
||||
}
|
||||
JSON::writeDictionaryItem(p, first, key, j, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf)
|
||||
{
|
||||
@ -982,16 +965,17 @@ QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf)
|
||||
auto wanted_og = getWantedJSONObjects();
|
||||
for (auto& obj: pdf.getAllObjects()) {
|
||||
std::string key = obj.unparse();
|
||||
if (m->json_version > 1) {
|
||||
key = "obj:" + key;
|
||||
}
|
||||
|
||||
if (all_objects || wanted_og.count(obj.getObjGen())) {
|
||||
doJSONObject(p, first_object, key, obj);
|
||||
JSON::writeDictionaryKey(p, first_object, obj.unparse(), 2);
|
||||
obj.writeJSON(1, p, true, 2);
|
||||
first_object = false;
|
||||
}
|
||||
}
|
||||
if (all_objects || m->json_objects.count("trailer")) {
|
||||
auto trailer = pdf.getTrailer();
|
||||
doJSONObject(p, first_object, "trailer", trailer);
|
||||
JSON::writeDictionaryKey(p, first_object, "trailer", 2);
|
||||
pdf.getTrailer().writeJSON(1, p, true, 2);
|
||||
first_object = false;
|
||||
}
|
||||
JSON::writeDictionaryClose(p, first_object, 1);
|
||||
} else {
|
||||
@ -3097,8 +3081,9 @@ QPDFJob::writeOutfile(QPDF& pdf)
|
||||
try {
|
||||
QUtil::remove_file(backup.c_str());
|
||||
} catch (QPDFSystemError& e) {
|
||||
*m->log->getError() << m->message_prefix << ": unable to delete original file ("
|
||||
<< e.what() << ");" << " original file left in " << backup
|
||||
*m->log->getError()
|
||||
<< m->message_prefix << ": unable to delete original file (" << e.what() << ");"
|
||||
<< " original file left in " << backup
|
||||
<< ", but the input was successfully replaced\n";
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <qpdf/BufferInputSource.hh>
|
||||
#include <qpdf/Pl_Buffer.hh>
|
||||
#include <qpdf/Pl_QPDFTokenizer.hh>
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFExc.hh>
|
||||
#include <qpdf/QPDFLogger.hh>
|
||||
@ -1617,10 +1618,33 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect)
|
||||
} else if (!dereference()) {
|
||||
throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
|
||||
} else {
|
||||
return obj->getJSON(json_version);
|
||||
Pl_Buffer p{"json"};
|
||||
JSON::Writer jw{&p, 0};
|
||||
writeJSON(json_version, jw, dereference_indirect);
|
||||
p.finish();
|
||||
return JSON::parse(p.getString());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect)
|
||||
{
|
||||
if (!dereference_indirect && isIndirect()) {
|
||||
p << "\"" << getObjGen().unparse(' ') << " R\"";
|
||||
} else if (!dereference()) {
|
||||
throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
|
||||
} else {
|
||||
obj->writeJSON(json_version, p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDFObjectHandle::writeJSON(int json_version, Pipeline* p, bool dereference_indirect, size_t depth)
|
||||
{
|
||||
JSON::Writer jw{p, depth};
|
||||
writeJSON(json_version, jw, dereference_indirect);
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDFObjectHandle::getStreamJSON(
|
||||
int json_version,
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <qpdf/QPDF_Array.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QPDFObjectHandle.hh>
|
||||
#include <qpdf/QPDFObject_private.hh>
|
||||
#include <qpdf/QTC.hh>
|
||||
@ -148,36 +149,41 @@ QPDF_Array::unparse()
|
||||
return result;
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Array::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Array::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
static const JSON j_null = JSON::makeNull();
|
||||
JSON j_array = JSON::makeArray();
|
||||
p.writeStart('[');
|
||||
if (sp) {
|
||||
int next = 0;
|
||||
for (auto& item: sp->elements) {
|
||||
int key = item.first;
|
||||
for (int j = next; j < key; ++j) {
|
||||
j_array.addArrayElement(j_null);
|
||||
p.writeNext() << "null";
|
||||
}
|
||||
p.writeNext();
|
||||
auto og = item.second->getObjGen();
|
||||
j_array.addArrayElement(
|
||||
og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R")
|
||||
: item.second->getJSON(json_version));
|
||||
if (og.isIndirect()) {
|
||||
p << "\"" << og.unparse(' ') << " R\"";
|
||||
} else {
|
||||
item.second->writeJSON(json_version, p);
|
||||
}
|
||||
next = ++key;
|
||||
}
|
||||
for (int j = next; j < sp->size; ++j) {
|
||||
j_array.addArrayElement(j_null);
|
||||
p.writeNext() << "null";
|
||||
}
|
||||
} else {
|
||||
for (auto const& item: elements) {
|
||||
p.writeNext();
|
||||
auto og = item->getObjGen();
|
||||
j_array.addArrayElement(
|
||||
og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R")
|
||||
: item->getJSON(json_version));
|
||||
if (og.isIndirect()) {
|
||||
p << "\"" << og.unparse(' ') << " R\"";
|
||||
} else {
|
||||
item->writeJSON(json_version, p);
|
||||
}
|
||||
}
|
||||
return j_array;
|
||||
}
|
||||
p.writeEnd(']');
|
||||
}
|
||||
|
||||
QPDFObjectHandle
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <qpdf/QPDF_Bool.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
|
||||
QPDF_Bool::QPDF_Bool(bool val) :
|
||||
QPDFValue(::ot_boolean, "boolean"),
|
||||
val(val)
|
||||
@ -24,10 +26,10 @@ QPDF_Bool::unparse()
|
||||
return (val ? "true" : "false");
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Bool::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Bool::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
return JSON::makeBool(this->val);
|
||||
p << val;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -28,9 +28,8 @@ QPDF_Destroyed::unparse()
|
||||
return "";
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Destroyed::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Destroyed::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF");
|
||||
return JSON::makeNull();
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <qpdf/QPDF_Dictionary.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QPDFObject_private.hh>
|
||||
#include <qpdf/QPDF_Name.hh>
|
||||
#include <qpdf/QPDF_Null.hh>
|
||||
@ -67,28 +68,30 @@ QPDF_Dictionary::unparse()
|
||||
return result;
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Dictionary::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Dictionary::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
JSON j = JSON::makeDictionary();
|
||||
p.writeStart('{');
|
||||
for (auto& iter: this->items) {
|
||||
if (!iter.second.isNull()) {
|
||||
p.writeNext();
|
||||
if (json_version == 1) {
|
||||
j.addDictionaryMember(
|
||||
QPDF_Name::normalizeName(iter.first), iter.second.getJSON(json_version));
|
||||
p << "\"" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first))
|
||||
<< "\": ";
|
||||
} else if (auto res = QPDF_Name::analyzeJSONEncoding(iter.first); res.first) {
|
||||
if (res.second) {
|
||||
p << "\"" << iter.first << "\": ";
|
||||
} else {
|
||||
bool has_8bit_chars;
|
||||
bool is_valid_utf8;
|
||||
bool is_utf16;
|
||||
QUtil::analyze_encoding(iter.first, has_8bit_chars, is_valid_utf8, is_utf16);
|
||||
std::string key = !has_8bit_chars || is_valid_utf8
|
||||
? iter.first
|
||||
: "n:" + QPDF_Name::normalizeName(iter.first);
|
||||
j.addDictionaryMember(key, iter.second.getJSON(json_version));
|
||||
p << "\"" << JSON::Writer::encode_string(iter.first) << "\": ";
|
||||
}
|
||||
} else {
|
||||
p << "\"n:" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first))
|
||||
<< "\": ";
|
||||
}
|
||||
iter.second.writeJSON(json_version, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return j;
|
||||
p.writeEnd('}');
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <qpdf/QPDF_InlineImage.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
|
||||
QPDF_InlineImage::QPDF_InlineImage(std::string const& val) :
|
||||
QPDFValue(::ot_inlineimage, "inline-image"),
|
||||
val(val)
|
||||
@ -24,8 +26,8 @@ QPDF_InlineImage::unparse()
|
||||
return this->val;
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_InlineImage::getJSON(int json_version)
|
||||
void
|
||||
QPDF_InlineImage::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
return JSON::makeNull();
|
||||
p << "null";
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <qpdf/QPDF_Integer.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
|
||||
QPDF_Integer::QPDF_Integer(long long val) :
|
||||
@ -26,10 +27,10 @@ QPDF_Integer::unparse()
|
||||
return std::to_string(this->val);
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Integer::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Integer::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
return JSON::makeInt(this->val);
|
||||
p << std::to_string(this->val);
|
||||
}
|
||||
|
||||
long long
|
||||
|
@ -1,7 +1,10 @@
|
||||
#include <qpdf/QPDF_Name.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
QPDF_Name::QPDF_Name(std::string const& name) :
|
||||
QPDFValue(::ot_name, "name"),
|
||||
name(name)
|
||||
@ -51,20 +54,71 @@ QPDF_Name::unparse()
|
||||
return normalizeName(this->name);
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Name::getJSON(int json_version)
|
||||
std::pair<bool, bool>
|
||||
QPDF_Name::analyzeJSONEncoding(const std::string& name)
|
||||
{
|
||||
std::basic_string_view<unsigned char> view{
|
||||
reinterpret_cast<const unsigned char*>(name.data()), name.size()};
|
||||
|
||||
int tail = 0; // Number of continuation characters expected.
|
||||
bool tail2 = false; // Potential overlong 3 octet utf-8.
|
||||
bool tail3 = false; // potential overlong 4 octet
|
||||
bool needs_escaping = false;
|
||||
for (auto const& c: view) {
|
||||
if (tail) {
|
||||
if ((c & 0xc0) != 0x80) {
|
||||
return {false, false};
|
||||
}
|
||||
if (tail2) {
|
||||
if ((c & 0xe0) == 0x80) {
|
||||
return {false, false};
|
||||
}
|
||||
tail2 = false;
|
||||
} else if (tail3) {
|
||||
if ((c & 0xf0) == 0x80) {
|
||||
return {false, false};
|
||||
}
|
||||
tail3 = false;
|
||||
}
|
||||
tail--;
|
||||
} else if (c < 0x80) {
|
||||
if (!needs_escaping) {
|
||||
needs_escaping = !((c > 34 && c != '\\') || c == ' ' || c == 33);
|
||||
}
|
||||
} else if ((c & 0xe0) == 0xc0) {
|
||||
if ((c & 0xfe) == 0xc0) {
|
||||
return {false, false};
|
||||
}
|
||||
tail = 1;
|
||||
} else if ((c & 0xf0) == 0xe0) {
|
||||
tail2 = (c == 0xe0);
|
||||
tail = 2;
|
||||
} else if ((c & 0xf8) == 0xf0) {
|
||||
tail3 = (c == 0xf0);
|
||||
tail = 3;
|
||||
} else {
|
||||
return {false, false};
|
||||
}
|
||||
}
|
||||
return {tail == 0, !needs_escaping};
|
||||
}
|
||||
|
||||
void
|
||||
QPDF_Name::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
// For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When updating
|
||||
// this method make sure QPDF_Dictionary is also update.
|
||||
if (json_version == 1) {
|
||||
return JSON::makeString(normalizeName(this->name));
|
||||
p << "\"" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
|
||||
} else {
|
||||
bool has_8bit_chars;
|
||||
bool is_valid_utf8;
|
||||
bool is_utf16;
|
||||
QUtil::analyze_encoding(this->name, has_8bit_chars, is_valid_utf8, is_utf16);
|
||||
if (!has_8bit_chars || is_valid_utf8) {
|
||||
return JSON::makeString(this->name);
|
||||
if (auto res = analyzeJSONEncoding(name); res.first) {
|
||||
if (res.second) {
|
||||
p << "\"" << name << "\"";
|
||||
} else {
|
||||
return JSON::makeString("n:" + normalizeName(this->name));
|
||||
p << "\"" << JSON::Writer::encode_string(name) << "\"";
|
||||
}
|
||||
} else {
|
||||
p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <qpdf/QPDF_Null.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QPDFObject_private.hh>
|
||||
|
||||
QPDF_Null::QPDF_Null() :
|
||||
@ -43,9 +44,8 @@ QPDF_Null::unparse()
|
||||
return "null";
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Null::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Null::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
// If this is updated, QPDF_Array::getJSON must also be updated.
|
||||
return JSON::makeNull();
|
||||
p << "null";
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <qpdf/QPDF_Operator.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
|
||||
QPDF_Operator::QPDF_Operator(std::string const& val) :
|
||||
QPDFValue(::ot_operator, "operator"),
|
||||
val(val)
|
||||
@ -24,8 +26,8 @@ QPDF_Operator::unparse()
|
||||
return val;
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Operator::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Operator::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
return JSON::makeNull();
|
||||
p << "null";
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <qpdf/QPDF_Real.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
|
||||
QPDF_Real::QPDF_Real(std::string const& val) :
|
||||
@ -38,21 +39,17 @@ QPDF_Real::unparse()
|
||||
return this->val;
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Real::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Real::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
// While PDF allows .x or -.x, JSON does not. Rather than converting from string to double and
|
||||
// back, just handle this as a special case for JSON.
|
||||
std::string result;
|
||||
if (this->val.length() == 0) {
|
||||
// Can't really happen...
|
||||
result = "0";
|
||||
p << "0";
|
||||
} else if (this->val.at(0) == '.') {
|
||||
result = "0" + this->val;
|
||||
} else if ((this->val.length() >= 2) && (this->val.at(0) == '-') && (this->val.at(1) == '.')) {
|
||||
result = "-0." + this->val.substr(2);
|
||||
p << "0" << this->val;
|
||||
} else if (this->val.length() >= 2 && this->val.at(0) == '-' && this->val.at(1) == '.') {
|
||||
p << "-0." << this->val.substr(2);
|
||||
} else {
|
||||
result = this->val;
|
||||
p << this->val;
|
||||
}
|
||||
return JSON::makeNumber(result);
|
||||
}
|
||||
|
@ -26,9 +26,8 @@ QPDF_Reserved::unparse()
|
||||
return "";
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Reserved::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Reserved::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object");
|
||||
return JSON::makeNull();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <qpdf/QPDF_Stream.hh>
|
||||
|
||||
#include <qpdf/ContentNormalizer.hh>
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/Pipeline.hh>
|
||||
#include <qpdf/Pl_Base64.hh>
|
||||
#include <qpdf/Pl_Buffer.hh>
|
||||
@ -176,13 +177,10 @@ QPDF_Stream::unparse()
|
||||
return og.unparse(' ') + " R";
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Stream::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Stream::writeJSON(int json_version, JSON::Writer& jw)
|
||||
{
|
||||
if (json_version == 1) {
|
||||
return this->stream_dict.getJSON(json_version);
|
||||
}
|
||||
return getStreamJSON(json_version, qpdf_sj_none, qpdf_dl_none, nullptr, "");
|
||||
stream_dict.writeJSON(json_version, jw);
|
||||
}
|
||||
|
||||
JSON
|
||||
@ -192,42 +190,70 @@ QPDF_Stream::getStreamJSON(
|
||||
qpdf_stream_decode_level_e decode_level,
|
||||
Pipeline* p,
|
||||
std::string const& data_filename)
|
||||
{
|
||||
Pl_Buffer pb{"streamjson"};
|
||||
JSON::Writer jw{&pb, 0};
|
||||
decode_level =
|
||||
writeStreamJSON(json_version, jw, json_data, decode_level, p, data_filename, true);
|
||||
pb.finish();
|
||||
auto result = JSON::parse(pb.getString());
|
||||
if (json_data == qpdf_sj_inline) {
|
||||
result.addDictionaryMember("data", JSON::makeBlob(StreamBlobProvider(this, decode_level)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
qpdf_stream_decode_level_e
|
||||
QPDF_Stream::writeStreamJSON(
|
||||
int json_version,
|
||||
JSON::Writer& jw,
|
||||
qpdf_json_stream_data_e json_data,
|
||||
qpdf_stream_decode_level_e decode_level,
|
||||
Pipeline* p,
|
||||
std::string const& data_filename,
|
||||
bool no_data_key)
|
||||
{
|
||||
switch (json_data) {
|
||||
case qpdf_sj_none:
|
||||
case qpdf_sj_inline:
|
||||
if (p != nullptr) {
|
||||
throw std::logic_error("QPDF_Stream::getStreamJSON: pipeline should only be supplied "
|
||||
throw std::logic_error("QPDF_Stream::writeStreamJSON: pipeline should only be supplied "
|
||||
"when json_data is file");
|
||||
}
|
||||
break;
|
||||
case qpdf_sj_file:
|
||||
if (p == nullptr) {
|
||||
throw std::logic_error(
|
||||
"QPDF_Stream::getStreamJSON: pipeline must be supplied when json_data is file");
|
||||
"QPDF_Stream::writeStreamJSON: pipeline must be supplied when json_data is file");
|
||||
}
|
||||
if (data_filename.empty()) {
|
||||
throw std::logic_error("QPDF_Stream::getStreamJSON: data_filename must be supplied "
|
||||
throw std::logic_error("QPDF_Stream::writeStreamJSON: data_filename must be supplied "
|
||||
"when json_data is file");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
auto dict = this->stream_dict;
|
||||
JSON result = JSON::makeDictionary();
|
||||
if (json_data != qpdf_sj_none) {
|
||||
jw.writeStart('{');
|
||||
|
||||
if (json_data == qpdf_sj_none) {
|
||||
jw.writeNext();
|
||||
jw << R"("dict": )";
|
||||
stream_dict.writeJSON(json_version, jw);
|
||||
jw.writeEnd('}');
|
||||
return decode_level;
|
||||
}
|
||||
|
||||
Pl_Discard discard;
|
||||
Pl_Buffer buf_pl{"stream data"};
|
||||
// buf_pl contains valid data and is ready for retrieval of the data.
|
||||
Pipeline* data_pipeline = &buf_pl;
|
||||
if (no_data_key && json_data == qpdf_sj_inline) {
|
||||
data_pipeline = &discard;
|
||||
}
|
||||
// pipeStreamData produced valid data.
|
||||
bool buf_pl_ready = false;
|
||||
bool filtered = false;
|
||||
bool filter = (decode_level != qpdf_dl_none);
|
||||
for (int attempt = 1; attempt <= 2; ++attempt) {
|
||||
Pipeline* data_pipeline = &discard;
|
||||
if (json_data == qpdf_sj_file) {
|
||||
// We need to capture the data to write
|
||||
data_pipeline = &buf_pl;
|
||||
}
|
||||
bool succeeded =
|
||||
pipeStreamData(data_pipeline, &filtered, 0, decode_level, false, (attempt == 1));
|
||||
if (!succeeded || (filter && !filtered)) {
|
||||
@ -236,34 +262,37 @@ QPDF_Stream::getStreamJSON(
|
||||
decode_level = qpdf_dl_none;
|
||||
buf_pl.getString(); // reset buf_pl
|
||||
} else {
|
||||
if (json_data == qpdf_sj_file) {
|
||||
buf_pl_ready = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!buf_pl_ready) {
|
||||
throw std::logic_error("QPDF_Stream: failed to get stream data");
|
||||
}
|
||||
// We can use unsafeShallowCopy because we are only touching top-level keys.
|
||||
dict = this->stream_dict.unsafeShallowCopy();
|
||||
auto dict = stream_dict.unsafeShallowCopy();
|
||||
dict.removeKey("/Length");
|
||||
if (filter && filtered) {
|
||||
dict.removeKey("/Filter");
|
||||
dict.removeKey("/DecodeParms");
|
||||
}
|
||||
if (json_data == qpdf_sj_file) {
|
||||
result.addDictionaryMember("datafile", JSON::makeString(data_filename));
|
||||
if (!buf_pl_ready) {
|
||||
throw std::logic_error("QPDF_Stream: failed to get stream data in json file mode");
|
||||
}
|
||||
jw.writeNext() << R"("datafile": ")" << JSON::Writer::encode_string(data_filename) << "\"";
|
||||
p->writeString(buf_pl.getString());
|
||||
} else if (json_data == qpdf_sj_inline) {
|
||||
result.addDictionaryMember(
|
||||
"data", JSON::makeBlob(StreamBlobProvider(this, decode_level)));
|
||||
if (!no_data_key) {
|
||||
jw.writeNext() << R"("data": ")";
|
||||
jw.writeBase64(buf_pl.getString()) << "\"";
|
||||
}
|
||||
} else {
|
||||
throw std::logic_error("QPDF_Stream: unexpected value of json_data");
|
||||
throw std::logic_error("QPDF_Stream::writeStreamJSON : unexpected value of json_data");
|
||||
}
|
||||
}
|
||||
result.addDictionaryMember("dict", dict.getJSON(json_version));
|
||||
return result;
|
||||
|
||||
jw.writeNext() << R"("dict": )";
|
||||
dict.writeJSON(json_version, jw);
|
||||
jw.writeEnd('}');
|
||||
|
||||
return decode_level;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <qpdf/QPDF_String.hh>
|
||||
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
|
||||
// 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()
|
||||
return unparse(false);
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_String::getJSON(int json_version)
|
||||
void
|
||||
QPDF_String::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
auto candidate = getUTF8Val();
|
||||
if (json_version == 1) {
|
||||
return JSON::makeString(getUTF8Val());
|
||||
}
|
||||
|
||||
p << "\"" << JSON::Writer::encode_string(candidate) << "\"";
|
||||
} else {
|
||||
// See if we can unambiguously represent as Unicode.
|
||||
bool is_unicode = false;
|
||||
std::string result;
|
||||
std::string candidate = getUTF8Val();
|
||||
if (QUtil::is_utf16(this->val) || QUtil::is_explicit_utf8(this->val)) {
|
||||
is_unicode = true;
|
||||
result = candidate;
|
||||
p << "\"u:" << JSON::Writer::encode_string(candidate) <<"\"";
|
||||
return;
|
||||
} else if (!useHexString()) {
|
||||
std::string test;
|
||||
if (QUtil::utf8_to_pdf_doc(candidate, test, '?') && (test == this->val)) {
|
||||
// This is a PDF-doc string that can be losslessly encoded as Unicode.
|
||||
is_unicode = true;
|
||||
result = candidate;
|
||||
p << "\"u:" << JSON::Writer::encode_string(candidate) <<"\"";
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (is_unicode) {
|
||||
result = "u:" + result;
|
||||
} else {
|
||||
result = "b:" + QUtil::hex_encode(this->val);
|
||||
p << "\"b:" << QUtil::hex_encode(val) <<"\"";
|
||||
}
|
||||
return JSON::makeString(result);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -27,9 +27,8 @@ QPDF_Unresolved::unparse()
|
||||
return "";
|
||||
}
|
||||
|
||||
JSON
|
||||
QPDF_Unresolved::getJSON(int json_version)
|
||||
void
|
||||
QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle");
|
||||
return JSON::makeNull();
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
#include <qpdf/QPDF.hh>
|
||||
|
||||
#include <qpdf/FileInputSource.hh>
|
||||
#include <qpdf/JSON_writer.hh>
|
||||
#include <qpdf/Pl_Base64.hh>
|
||||
#include <qpdf/Pl_StdioFile.hh>
|
||||
#include <qpdf/QIntC.hh>
|
||||
#include <qpdf/QPDFObject_private.hh>
|
||||
#include <qpdf/QPDFValue.hh>
|
||||
#include <qpdf/QPDF_Null.hh>
|
||||
#include <qpdf/QPDF_Stream.hh>
|
||||
#include <qpdf/QTC.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
#include <algorithm>
|
||||
@ -442,7 +444,9 @@ void
|
||||
QPDF::JSONReactor::replaceObject(QPDFObjectHandle&& replacement, JSON const& value)
|
||||
{
|
||||
if (replacement.isIndirect()) {
|
||||
error(replacement.getParsedOffset(), "the value of an object may not be an indirect object reference");
|
||||
error(
|
||||
replacement.getParsedOffset(),
|
||||
"the value of an object may not be an indirect object reference");
|
||||
return;
|
||||
}
|
||||
auto& tos = stack.back();
|
||||
@ -828,45 +832,20 @@ QPDF::importJSON(std::shared_ptr<InputSource> is, bool must_be_complete)
|
||||
}
|
||||
|
||||
void
|
||||
QPDF::writeJSONStream(
|
||||
writeJSONStreamFile(
|
||||
int version,
|
||||
Pipeline* p,
|
||||
bool& first,
|
||||
std::string const& key,
|
||||
QPDFObjectHandle& obj,
|
||||
JSON::Writer& jw,
|
||||
QPDF_Stream& stream,
|
||||
int id,
|
||||
qpdf_stream_decode_level_e decode_level,
|
||||
qpdf_json_stream_data_e json_stream_data,
|
||||
std::string const& file_prefix)
|
||||
{
|
||||
Pipeline* stream_p = nullptr;
|
||||
FILE* f = nullptr;
|
||||
std::shared_ptr<Pl_StdioFile> f_pl;
|
||||
std::string filename;
|
||||
if (json_stream_data == qpdf_sj_file) {
|
||||
filename = file_prefix + "-" + std::to_string(obj.getObjectID());
|
||||
f = QUtil::safe_fopen(filename.c_str(), "wb");
|
||||
f_pl = std::make_shared<Pl_StdioFile>("stream data", f);
|
||||
stream_p = f_pl.get();
|
||||
}
|
||||
auto j = JSON::makeDictionary();
|
||||
j.addDictionaryMember(
|
||||
"stream", obj.getStreamJSON(version, json_stream_data, decode_level, stream_p, filename));
|
||||
|
||||
JSON::writeDictionaryItem(p, first, key, j, 3);
|
||||
if (f) {
|
||||
f_pl->finish();
|
||||
f_pl = nullptr;
|
||||
auto filename = file_prefix + "-" + std::to_string(id);
|
||||
auto* f = QUtil::safe_fopen(filename.c_str(), "wb");
|
||||
Pl_StdioFile f_pl{"stream data", f};
|
||||
stream.writeStreamJSON(version, jw, qpdf_sj_file, decode_level, &f_pl, filename);
|
||||
f_pl.finish();
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDF::writeJSONObject(
|
||||
int version, Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle& obj)
|
||||
{
|
||||
auto j = JSON::makeDictionary();
|
||||
j.addDictionaryMember("value", obj.getJSON(version, true));
|
||||
JSON::writeDictionaryItem(p, first, key, j, 3);
|
||||
}
|
||||
|
||||
void
|
||||
@ -893,80 +872,75 @@ QPDF::writeJSON(
|
||||
std::string const& file_prefix,
|
||||
std::set<std::string> wanted_objects)
|
||||
{
|
||||
int const depth_outer = 1;
|
||||
int const depth_top = 1;
|
||||
int const depth_qpdf = 2;
|
||||
int const depth_qpdf_inner = 3;
|
||||
|
||||
if (version != 2) {
|
||||
throw std::runtime_error("QPDF::writeJSON: only version 2 is supported");
|
||||
}
|
||||
bool first = true;
|
||||
JSON::Writer jw{p, 4};
|
||||
if (complete) {
|
||||
JSON::writeDictionaryOpen(p, first, depth_outer);
|
||||
} else {
|
||||
first = first_key;
|
||||
jw << "{";
|
||||
} else if (!first_key) {
|
||||
jw << ",";
|
||||
}
|
||||
JSON::writeDictionaryKey(p, first, "qpdf", depth_top);
|
||||
bool first_qpdf = true;
|
||||
JSON::writeArrayOpen(p, first_qpdf, depth_top);
|
||||
JSON::writeNext(p, first_qpdf, depth_qpdf);
|
||||
bool first_qpdf_inner = true;
|
||||
JSON::writeDictionaryOpen(p, first_qpdf_inner, depth_qpdf);
|
||||
JSON::writeDictionaryItem(
|
||||
p, first_qpdf_inner, "jsonversion", JSON::makeInt(version), depth_qpdf_inner);
|
||||
JSON::writeDictionaryItem(
|
||||
p, first_qpdf_inner, "pdfversion", JSON::makeString(getPDFVersion()), depth_qpdf_inner);
|
||||
JSON::writeDictionaryItem(
|
||||
p,
|
||||
first_qpdf_inner,
|
||||
"pushedinheritedpageresources",
|
||||
JSON::makeBool(everPushedInheritedAttributesToPages()),
|
||||
depth_qpdf_inner);
|
||||
JSON::writeDictionaryItem(
|
||||
p,
|
||||
first_qpdf_inner,
|
||||
"calledgetallpages",
|
||||
JSON::makeBool(everCalledGetAllPages()),
|
||||
depth_qpdf_inner);
|
||||
JSON::writeDictionaryItem(
|
||||
p,
|
||||
first_qpdf_inner,
|
||||
"maxobjectid",
|
||||
JSON::makeInt(QIntC::to_longlong(getObjectCount())),
|
||||
depth_qpdf_inner);
|
||||
JSON::writeDictionaryClose(p, first_qpdf_inner, depth_qpdf);
|
||||
JSON::writeNext(p, first_qpdf, depth_qpdf);
|
||||
JSON::writeDictionaryOpen(p, first_qpdf_inner, depth_qpdf);
|
||||
first_key = false;
|
||||
|
||||
/* clang-format off */
|
||||
jw << "\n"
|
||||
" \"qpdf\": [\n"
|
||||
" {\n"
|
||||
" \"jsonversion\": " << std::to_string(version) << ",\n"
|
||||
" \"pdfversion\": \"" << getPDFVersion() << "\",\n"
|
||||
" \"pushedinheritedpageresources\": " << (everPushedInheritedAttributesToPages() ? "true" : "false") << ",\n"
|
||||
" \"calledgetallpages\": " << (everCalledGetAllPages() ? "true" : "false") << ",\n"
|
||||
" \"maxobjectid\": " << std::to_string(getObjectCount()) << "\n"
|
||||
" },\n"
|
||||
" {";
|
||||
/* clang-format on */
|
||||
|
||||
bool all_objects = wanted_objects.empty();
|
||||
bool first = true;
|
||||
for (auto& obj: getAllObjects()) {
|
||||
std::string key = "obj:" + obj.unparse();
|
||||
auto const og = obj.getObjGen();
|
||||
std::string key = "obj:" + og.unparse(' ') + " R";
|
||||
if (all_objects || wanted_objects.count(key)) {
|
||||
if (obj.isStream()) {
|
||||
writeJSONStream(
|
||||
version,
|
||||
p,
|
||||
first_qpdf_inner,
|
||||
key,
|
||||
obj,
|
||||
decode_level,
|
||||
json_stream_data,
|
||||
file_prefix);
|
||||
if (first) {
|
||||
jw << "\n \"" << key;
|
||||
first = false;
|
||||
} else {
|
||||
writeJSONObject(version, p, first_qpdf_inner, key, obj);
|
||||
jw << "\n },\n \"" << key;
|
||||
}
|
||||
if (auto* stream = obj.getObjectPtr()->as<QPDF_Stream>()) {
|
||||
jw << "\": {\n \"stream\": ";
|
||||
if (json_stream_data == qpdf_sj_file) {
|
||||
writeJSONStreamFile(
|
||||
version, jw, *stream, og.getObj(), decode_level, file_prefix);
|
||||
} else {
|
||||
stream->writeStreamJSON(
|
||||
version, jw, json_stream_data, decode_level, nullptr, "");
|
||||
}
|
||||
} else {
|
||||
jw << "\": {\n \"value\": ";
|
||||
obj.writeJSON(version, jw, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (all_objects || wanted_objects.count("trailer")) {
|
||||
auto trailer = getTrailer();
|
||||
writeJSONObject(version, p, first_qpdf_inner, "trailer", trailer);
|
||||
if (!first) {
|
||||
jw << "\n },";
|
||||
}
|
||||
JSON::writeDictionaryClose(p, first_qpdf_inner, depth_qpdf);
|
||||
JSON::writeArrayClose(p, first_qpdf, depth_top);
|
||||
jw << "\n \"trailer\": {\n \"value\": ";
|
||||
getTrailer().writeJSON(version, jw, true);
|
||||
first = false;
|
||||
}
|
||||
if (!first) {
|
||||
jw << "\n }";
|
||||
}
|
||||
/* clang-format off */
|
||||
jw << "\n"
|
||||
" }\n"
|
||||
" ]";
|
||||
/* clang-format on */
|
||||
if (complete) {
|
||||
JSON::writeDictionaryClose(p, first, 0);
|
||||
*p << "\n";
|
||||
jw << "\n}\n";
|
||||
p->finish();
|
||||
}
|
||||
first_key = false;
|
||||
}
|
||||
|
137
libqpdf/qpdf/JSON_writer.hh
Normal file
137
libqpdf/qpdf/JSON_writer.hh
Normal file
@ -0,0 +1,137 @@
|
||||
#ifndef JSON_WRITER_HH
|
||||
#define JSON_WRITER_HH
|
||||
|
||||
#include <qpdf/JSON.hh>
|
||||
#include <qpdf/Pipeline.hh>
|
||||
#include <qpdf/Pl_Base64.hh>
|
||||
#include <qpdf/Pl_Concatenate.hh>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
// Writer is a small utility class to aid writing JSON to a pipeline. Methods are designed to allow
|
||||
// chaining of calls.
|
||||
//
|
||||
// Some uses of the class have a significant performance impact. The class is intended purely for
|
||||
// internal use to allow it to be adapted as needed to maintain performance.
|
||||
class JSON::Writer
|
||||
{
|
||||
public:
|
||||
Writer(Pipeline* p, size_t depth) :
|
||||
p(p),
|
||||
indent(2 * depth)
|
||||
{
|
||||
}
|
||||
|
||||
Writer&
|
||||
write(char const* data, size_t len)
|
||||
{
|
||||
p->write(reinterpret_cast<unsigned char const*>(data), len);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
writeBase64(std::string_view sv)
|
||||
{
|
||||
Pl_Concatenate cat{"writer concat", p};
|
||||
Pl_Base64 base{"writer base64", &cat, Pl_Base64::a_encode};
|
||||
base.write(reinterpret_cast<unsigned char const*>(sv.data()), sv.size());
|
||||
base.finish();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
writeNext()
|
||||
{
|
||||
auto n = indent;
|
||||
if (first) {
|
||||
first = false;
|
||||
write(&spaces[1], n % n_spaces + 1);
|
||||
} else {
|
||||
write(&spaces[0], n % n_spaces + 2);
|
||||
}
|
||||
while (n >= n_spaces) {
|
||||
write(&spaces[2], n_spaces);
|
||||
n -= n_spaces;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
writeStart(char const& c)
|
||||
{
|
||||
write(&c, 1);
|
||||
first = true;
|
||||
indent += 2;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
writeEnd(char const& c)
|
||||
{
|
||||
if (indent > 1) {
|
||||
indent -= 2;
|
||||
}
|
||||
if (!first) {
|
||||
first = true;
|
||||
writeNext();
|
||||
}
|
||||
first = false;
|
||||
write(&c, 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
operator<<(std::string_view sv)
|
||||
{
|
||||
p->write(reinterpret_cast<unsigned char const*>(sv.data()), sv.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
operator<<(char const* s)
|
||||
{
|
||||
*this << std::string_view{s};
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
operator<<(bool val)
|
||||
{
|
||||
*this << (val ? "true" : "false");
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
operator<<(int val)
|
||||
{
|
||||
*this << std::to_string(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
operator<<(size_t val)
|
||||
{
|
||||
*this << std::to_string(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer&
|
||||
operator<<(JSON&& j)
|
||||
{
|
||||
j.write(p, indent / 2);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static std::string encode_string(std::string const& utf8);
|
||||
|
||||
private:
|
||||
Pipeline* p;
|
||||
bool first{true};
|
||||
size_t indent;
|
||||
|
||||
static constexpr std::string_view spaces =
|
||||
",\n ";
|
||||
static constexpr auto n_spaces = spaces.size() - 2;
|
||||
};
|
||||
|
||||
#endif // JSON_WRITER_HH
|
@ -33,10 +33,10 @@ class QPDFObject
|
||||
{
|
||||
return value->unparse();
|
||||
}
|
||||
JSON
|
||||
getJSON(int json_version)
|
||||
void
|
||||
writeJSON(int json_version, JSON::Writer& p)
|
||||
{
|
||||
return value->getJSON(json_version);
|
||||
return value->writeJSON(json_version, p);
|
||||
}
|
||||
std::string
|
||||
getStringValue() const
|
||||
|
@ -24,7 +24,7 @@ class QPDFValue: public std::enable_shared_from_this<QPDFValue>
|
||||
|
||||
virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0;
|
||||
virtual std::string unparse() = 0;
|
||||
virtual JSON getJSON(int json_version) = 0;
|
||||
virtual void writeJSON(int json_version, JSON::Writer& p) = 0;
|
||||
|
||||
struct JSON_Descr
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ class QPDF_Array: public QPDFValue
|
||||
create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
void disconnect() override;
|
||||
|
||||
int
|
||||
|
@ -10,7 +10,8 @@ class QPDF_Bool: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(bool val);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
|
||||
bool getVal() const;
|
||||
|
||||
private:
|
||||
|
@ -9,7 +9,7 @@ class QPDF_Destroyed: public QPDFValue
|
||||
~QPDF_Destroyed() override = default;
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
static std::shared_ptr<QPDFValue> getInstance();
|
||||
|
||||
private:
|
||||
|
@ -16,7 +16,7 @@ class QPDF_Dictionary: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(std::map<std::string, QPDFObjectHandle>&& items);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
void disconnect() override;
|
||||
|
||||
// hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns
|
||||
|
@ -10,7 +10,7 @@ class QPDF_InlineImage: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(std::string const& val);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
std::string
|
||||
getStringValue() const override
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ class QPDF_Integer: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(long long value);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
long long getVal() const;
|
||||
|
||||
private:
|
||||
|
@ -10,10 +10,15 @@ class QPDF_Name: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(std::string const& name);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
|
||||
// Put # into strings with characters unsuitable for name token
|
||||
static std::string normalizeName(std::string const& name);
|
||||
|
||||
// Check whether name is valid utf-8 and whether it contains characters that require escaping.
|
||||
// Return {false, false} if the name is not valid utf-8, otherwise return {true, true} if no
|
||||
// characters require or {true, false} if escaping is required.
|
||||
static std::pair<bool, bool> analyzeJSONEncoding(std::string const& name);
|
||||
std::string
|
||||
getStringValue() const override
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ class QPDF_Null: public QPDFValue
|
||||
std::string var_descr);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
|
||||
private:
|
||||
QPDF_Null();
|
||||
|
@ -10,7 +10,7 @@ class QPDF_Operator: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(std::string const& val);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
std::string
|
||||
getStringValue() const override
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ class QPDF_Real: public QPDFValue
|
||||
create(double value, int decimal_places, bool trim_trailing_zeroes);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
std::string
|
||||
getStringValue() const override
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ class QPDF_Reserved: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create();
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
|
||||
private:
|
||||
QPDF_Reserved();
|
||||
|
@ -25,7 +25,7 @@ class QPDF_Stream: public QPDFValue
|
||||
size_t length);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
void setDescription(
|
||||
QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) override;
|
||||
void disconnect() override;
|
||||
@ -64,6 +64,14 @@ class QPDF_Stream: public QPDFValue
|
||||
qpdf_stream_decode_level_e decode_level,
|
||||
Pipeline* p,
|
||||
std::string const& data_filename);
|
||||
qpdf_stream_decode_level_e writeStreamJSON(
|
||||
int json_version,
|
||||
JSON::Writer& jw,
|
||||
qpdf_json_stream_data_e json_data,
|
||||
qpdf_stream_decode_level_e decode_level,
|
||||
Pipeline* p,
|
||||
std::string const& data_filename,
|
||||
bool no_data_key = false);
|
||||
|
||||
void replaceDict(QPDFObjectHandle const& new_dict);
|
||||
|
||||
|
@ -16,7 +16,7 @@ class QPDF_String: public QPDFValue
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
std::string unparse(bool force_binary);
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
std::string getUTF8Val() const;
|
||||
std::string
|
||||
getStringValue() const override
|
||||
|
@ -10,7 +10,7 @@ class QPDF_Unresolved: public QPDFValue
|
||||
static std::shared_ptr<QPDFObject> create(QPDF* qpdf, QPDFObjGen const& og);
|
||||
std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
|
||||
std::string unparse() override;
|
||||
JSON getJSON(int json_version) override;
|
||||
void writeJSON(int json_version, JSON::Writer& p) override;
|
||||
|
||||
private:
|
||||
QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og);
|
||||
|
@ -33,5 +33,22 @@ $td->runtest("copy sparse array",
|
||||
{$td->COMMAND => "test_driver 97 many-nulls.pdf"},
|
||||
{$td->STRING => "test 97 done\n", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("copy file with many nulls",
|
||||
{$td->COMMAND =>
|
||||
"qpdf minimal-nulls.pdf --qdf --static-id --no-original-object-ids a.pdf"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("compare files",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "minimal-nulls.pdf"});
|
||||
$td->runtest("file with many nulls to JSON v1",
|
||||
{$td->COMMAND => "qpdf minimal-nulls.pdf --json=1 -"},
|
||||
{$td->FILE => "minimal-nulls-1.json", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("file with many nulls to JSON v2",
|
||||
{$td->COMMAND => "qpdf minimal-nulls.pdf --json=2 -"},
|
||||
{$td->FILE => "minimal-nulls-2.json", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
cleanup();
|
||||
$td->report(4);
|
||||
$td->report(8);
|
||||
|
@ -350,7 +350,7 @@ $td->runtest("check C API write to JSON stream",
|
||||
# (using #xx) would generate invalid JSON, even though qpdf's own JSON
|
||||
# parser would accept it. Also, the JSON spec allows real numbers in
|
||||
# scientific notation, but the PDF spec does not.
|
||||
$n_tests += 4;
|
||||
$n_tests += 7;
|
||||
$td->runtest("handle binary names",
|
||||
{$td->COMMAND =>
|
||||
"qpdf --json-output weird-tokens.pdf a.json"},
|
||||
@ -371,6 +371,17 @@ $td->runtest("weird tokens with scientific notation",
|
||||
"qpdf --json-input --json-output weird-tokens-alt.json -"},
|
||||
{$td->FILE => "weird-tokens.json", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
$td->runtest("handle binary names (JSON v1)",
|
||||
{$td->COMMAND =>
|
||||
"qpdf --json=1 weird-tokens.pdf a.json"},
|
||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||
$td->runtest("check json",
|
||||
{$td->FILE => "a.json"},
|
||||
{$td->FILE => "weird-tokens-v1.json"},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("write JSON to pipeline",
|
||||
{$td->COMMAND => "test_driver 98 minimal.pdf ''"},
|
||||
{$td->STRING => "test 98 done\n", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
cleanup();
|
||||
$td->report($n_tests);
|
||||
|
453
qpdf/qtest/qpdf/minimal-nulls-1.json
Normal file
453
qpdf/qtest/qpdf/minimal-nulls-1.json
Normal file
@ -0,0 +1,453 @@
|
||||
{
|
||||
"version": 1,
|
||||
"parameters": {
|
||||
"decodelevel": "generalized"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"contents": [
|
||||
"4 0 R"
|
||||
],
|
||||
"images": [],
|
||||
"label": null,
|
||||
"object": "3 0 R",
|
||||
"outlines": [],
|
||||
"pageposfrom1": 1
|
||||
}
|
||||
],
|
||||
"pagelabels": [],
|
||||
"acroform": {
|
||||
"fields": [],
|
||||
"hasacroform": false,
|
||||
"needappearances": false
|
||||
},
|
||||
"attachments": {},
|
||||
"encrypt": {
|
||||
"capabilities": {
|
||||
"accessibility": true,
|
||||
"extract": true,
|
||||
"moddifyannotations": true,
|
||||
"modify": true,
|
||||
"modifyassembly": true,
|
||||
"modifyforms": true,
|
||||
"modifyother": true,
|
||||
"printhigh": true,
|
||||
"printlow": true
|
||||
},
|
||||
"encrypted": false,
|
||||
"ownerpasswordmatched": false,
|
||||
"parameters": {
|
||||
"P": 0,
|
||||
"R": 0,
|
||||
"V": 0,
|
||||
"bits": 0,
|
||||
"filemethod": "none",
|
||||
"key": null,
|
||||
"method": "none",
|
||||
"streammethod": "none",
|
||||
"stringmethod": "none"
|
||||
},
|
||||
"recovereduserpassword": null,
|
||||
"userpasswordmatched": false
|
||||
},
|
||||
"outlines": [],
|
||||
"objects": {
|
||||
"1 0 R": {
|
||||
"/Pages": "2 0 R",
|
||||
"/Type": "/Catalog"
|
||||
},
|
||||
"2 0 R": {
|
||||
"/Count": 1,
|
||||
"/Kids": [
|
||||
"3 0 R"
|
||||
],
|
||||
"/Type": "/Pages"
|
||||
},
|
||||
"3 0 R": {
|
||||
"/Contents": "4 0 R",
|
||||
"/MediaBox": [
|
||||
0,
|
||||
0,
|
||||
612,
|
||||
792
|
||||
],
|
||||
"/Nulls": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"6 0 R",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"/Parent": "2 0 R",
|
||||
"/Resources": {
|
||||
"/Font": {
|
||||
"/F1": "7 0 R"
|
||||
},
|
||||
"/ProcSet": "8 0 R"
|
||||
},
|
||||
"/Type": "/Page"
|
||||
},
|
||||
"4 0 R": {
|
||||
"/Length": "5 0 R"
|
||||
},
|
||||
"5 0 R": 44,
|
||||
"6 0 R": null,
|
||||
"7 0 R": {
|
||||
"/BaseFont": "/Helvetica",
|
||||
"/Encoding": "/WinAnsiEncoding",
|
||||
"/Name": "/F1",
|
||||
"/Subtype": "/Type1",
|
||||
"/Type": "/Font"
|
||||
},
|
||||
"8 0 R": [
|
||||
"/PDF",
|
||||
"/Text"
|
||||
],
|
||||
"trailer": {
|
||||
"/ID": [
|
||||
"ÏîgE<67>EMÛ‹Êߢ$²\u0005#",
|
||||
"1AY&SXŠfi#—bd3…'Ł"
|
||||
],
|
||||
"/Root": "1 0 R",
|
||||
"/Size": 9
|
||||
}
|
||||
},
|
||||
"objectinfo": {
|
||||
"1 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"2 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"3 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"4 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": true,
|
||||
"length": 44
|
||||
}
|
||||
},
|
||||
"5 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"6 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"7 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"8 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
424
qpdf/qtest/qpdf/minimal-nulls-2.json
Normal file
424
qpdf/qtest/qpdf/minimal-nulls-2.json
Normal file
@ -0,0 +1,424 @@
|
||||
{
|
||||
"version": 2,
|
||||
"parameters": {
|
||||
"decodelevel": "generalized"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"contents": [
|
||||
"4 0 R"
|
||||
],
|
||||
"images": [],
|
||||
"label": null,
|
||||
"object": "3 0 R",
|
||||
"outlines": [],
|
||||
"pageposfrom1": 1
|
||||
}
|
||||
],
|
||||
"pagelabels": [],
|
||||
"acroform": {
|
||||
"fields": [],
|
||||
"hasacroform": false,
|
||||
"needappearances": false
|
||||
},
|
||||
"attachments": {},
|
||||
"encrypt": {
|
||||
"capabilities": {
|
||||
"accessibility": true,
|
||||
"extract": true,
|
||||
"modify": true,
|
||||
"modifyannotations": true,
|
||||
"modifyassembly": true,
|
||||
"modifyforms": true,
|
||||
"modifyother": true,
|
||||
"printhigh": true,
|
||||
"printlow": true
|
||||
},
|
||||
"encrypted": false,
|
||||
"ownerpasswordmatched": false,
|
||||
"parameters": {
|
||||
"P": 0,
|
||||
"R": 0,
|
||||
"V": 0,
|
||||
"bits": 0,
|
||||
"filemethod": "none",
|
||||
"key": null,
|
||||
"method": "none",
|
||||
"streammethod": "none",
|
||||
"stringmethod": "none"
|
||||
},
|
||||
"recovereduserpassword": null,
|
||||
"userpasswordmatched": false
|
||||
},
|
||||
"outlines": [],
|
||||
"qpdf": [
|
||||
{
|
||||
"jsonversion": 2,
|
||||
"pdfversion": "1.3",
|
||||
"pushedinheritedpageresources": false,
|
||||
"calledgetallpages": true,
|
||||
"maxobjectid": 8
|
||||
},
|
||||
{
|
||||
"obj:1 0 R": {
|
||||
"value": {
|
||||
"/Pages": "2 0 R",
|
||||
"/Type": "/Catalog"
|
||||
}
|
||||
},
|
||||
"obj:2 0 R": {
|
||||
"value": {
|
||||
"/Count": 1,
|
||||
"/Kids": [
|
||||
"3 0 R"
|
||||
],
|
||||
"/Type": "/Pages"
|
||||
}
|
||||
},
|
||||
"obj:3 0 R": {
|
||||
"value": {
|
||||
"/Contents": "4 0 R",
|
||||
"/MediaBox": [
|
||||
0,
|
||||
0,
|
||||
612,
|
||||
792
|
||||
],
|
||||
"/Nulls": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"6 0 R",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
10,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"/Parent": "2 0 R",
|
||||
"/Resources": {
|
||||
"/Font": {
|
||||
"/F1": "7 0 R"
|
||||
},
|
||||
"/ProcSet": "8 0 R"
|
||||
},
|
||||
"/Type": "/Page"
|
||||
}
|
||||
},
|
||||
"obj:4 0 R": {
|
||||
"stream": {
|
||||
"dict": {
|
||||
"/Length": "5 0 R"
|
||||
}
|
||||
}
|
||||
},
|
||||
"obj:5 0 R": {
|
||||
"value": 44
|
||||
},
|
||||
"obj:6 0 R": {
|
||||
"value": null
|
||||
},
|
||||
"obj:7 0 R": {
|
||||
"value": {
|
||||
"/BaseFont": "/Helvetica",
|
||||
"/Encoding": "/WinAnsiEncoding",
|
||||
"/Name": "/F1",
|
||||
"/Subtype": "/Type1",
|
||||
"/Type": "/Font"
|
||||
}
|
||||
},
|
||||
"obj:8 0 R": {
|
||||
"value": [
|
||||
"/PDF",
|
||||
"/Text"
|
||||
]
|
||||
},
|
||||
"trailer": {
|
||||
"value": {
|
||||
"/ID": [
|
||||
"b:cfee6745ad454ddb88cadfa224b20523",
|
||||
"b:31415926535897932384626433832795"
|
||||
],
|
||||
"/Root": "1 0 R",
|
||||
"/Size": 9
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
387
qpdf/qtest/qpdf/minimal-nulls.pdf
Normal file
387
qpdf/qtest/qpdf/minimal-nulls.pdf
Normal file
@ -0,0 +1,387 @@
|
||||
%PDF-1.3
|
||||
%¿÷¢þ
|
||||
%QDF-1.0
|
||||
|
||||
1 0 obj
|
||||
<<
|
||||
/Pages 2 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<<
|
||||
/Count 1
|
||||
/Kids [
|
||||
3 0 R
|
||||
]
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 1
|
||||
3 0 obj
|
||||
<<
|
||||
/Contents 4 0 R
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Nulls [
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
6 0 R
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
10
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
null
|
||||
]
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 7 0 R
|
||||
>>
|
||||
/ProcSet 8 0 R
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
4 0 obj
|
||||
<<
|
||||
/Length 5 0 R
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
72 720 Td
|
||||
(Potato) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
44
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
null
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
8 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 9
|
||||
0000000000 65535 f
|
||||
0000000025 00000 n
|
||||
0000000079 00000 n
|
||||
0000000161 00000 n
|
||||
0000002909 00000 n
|
||||
0000003008 00000 n
|
||||
0000003027 00000 n
|
||||
0000003048 00000 n
|
||||
0000003166 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 9
|
||||
/ID [<cfee6745ad454ddb88cadfa224b20523><31415926535897932384626433832795>]
|
||||
>>
|
||||
startxref
|
||||
3201
|
||||
%%EOF
|
@ -10,21 +10,155 @@
|
||||
{
|
||||
"obj:1 0 R": {
|
||||
"value": {
|
||||
"/Escape\\Key": 42,
|
||||
"/Extra": [
|
||||
"u:Names with binary data",
|
||||
"n:/ABCDEF+#ba#da#cc#e5",
|
||||
"n:/OVERLONG+#c0#81",
|
||||
"n:/OVERLONG+#c1#ff",
|
||||
"/Ok+",
|
||||
"n:/OVERLONG+#e0#81#82",
|
||||
"n:/OVERLONG+#e0#9f#ff",
|
||||
"/Ok+ࠀ",
|
||||
"n:/OVERLONG+#f0#81#82#83",
|
||||
"n:/OVERLONG+#f0#8f#ff#ff",
|
||||
"/Ok+𐀀",
|
||||
"n:/range+#01",
|
||||
"n:/low+#18",
|
||||
"/ABCEDEF+π",
|
||||
"n:/one+#a0two",
|
||||
"n:/text#2fplain",
|
||||
"u:Names requiring escaping in JSON",
|
||||
"/Back\\shlash",
|
||||
"/Low\u0022",
|
||||
"/Low\u001f",
|
||||
"/ExceptSpace ",
|
||||
"/Except!",
|
||||
"u:Very small/large reals",
|
||||
1e-05,
|
||||
1e12
|
||||
],
|
||||
"/Nested": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/Pages": "2 0 R",
|
||||
"/Type": "/Catalog",
|
||||
"n:/WeirdKey+#ba#da#cc#e5": 42
|
||||
@ -78,7 +212,7 @@
|
||||
"value": {
|
||||
"/ID": [
|
||||
"b:42841c13bbf709d79a200fa1691836f8",
|
||||
"b:728c020f464c3cf7e02c12605fa7d88b"
|
||||
"b:31415926535897932384626433832795"
|
||||
],
|
||||
"/Root": "1 0 R",
|
||||
"/Size": 7
|
||||
|
295
qpdf/qtest/qpdf/weird-tokens-v1.json
Normal file
295
qpdf/qtest/qpdf/weird-tokens-v1.json
Normal file
@ -0,0 +1,295 @@
|
||||
{
|
||||
"version": 1,
|
||||
"parameters": {
|
||||
"decodelevel": "generalized"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"contents": [
|
||||
"4 0 R"
|
||||
],
|
||||
"images": [],
|
||||
"label": null,
|
||||
"object": "3 0 R",
|
||||
"outlines": [],
|
||||
"pageposfrom1": 1
|
||||
}
|
||||
],
|
||||
"pagelabels": [],
|
||||
"acroform": {
|
||||
"fields": [],
|
||||
"hasacroform": false,
|
||||
"needappearances": false
|
||||
},
|
||||
"attachments": {},
|
||||
"encrypt": {
|
||||
"capabilities": {
|
||||
"accessibility": true,
|
||||
"extract": true,
|
||||
"moddifyannotations": true,
|
||||
"modify": true,
|
||||
"modifyassembly": true,
|
||||
"modifyforms": true,
|
||||
"modifyother": true,
|
||||
"printhigh": true,
|
||||
"printlow": true
|
||||
},
|
||||
"encrypted": false,
|
||||
"ownerpasswordmatched": false,
|
||||
"parameters": {
|
||||
"P": 0,
|
||||
"R": 0,
|
||||
"V": 0,
|
||||
"bits": 0,
|
||||
"filemethod": "none",
|
||||
"key": null,
|
||||
"method": "none",
|
||||
"streammethod": "none",
|
||||
"stringmethod": "none"
|
||||
},
|
||||
"recovereduserpassword": null,
|
||||
"userpasswordmatched": false
|
||||
},
|
||||
"outlines": [],
|
||||
"objects": {
|
||||
"1 0 R": {
|
||||
"/Escape\\Key": 42,
|
||||
"/Extra": [
|
||||
"Names with binary data",
|
||||
"/ABCDEF+#ba#da#cc#e5",
|
||||
"/OVERLONG+#c0#81",
|
||||
"/OVERLONG+#c1#ff",
|
||||
"/Ok+#c2#80",
|
||||
"/OVERLONG+#e0#81#82",
|
||||
"/OVERLONG+#e0#9f#ff",
|
||||
"/Ok+#e0#a0#80",
|
||||
"/OVERLONG+#f0#81#82#83",
|
||||
"/OVERLONG+#f0#8f#ff#ff",
|
||||
"/Ok+#f0#90#80#80",
|
||||
"/range+#01",
|
||||
"/low+#18",
|
||||
"/ABCEDEF+#cf#80",
|
||||
"/one+#a0two",
|
||||
"/text#2fplain",
|
||||
"Names requiring escaping in JSON",
|
||||
"/Back\\shlash",
|
||||
"/Low\"",
|
||||
"/Low#1f",
|
||||
"/ExceptSpace#20",
|
||||
"/Except!",
|
||||
"Very small/large reals",
|
||||
0.00001,
|
||||
1000000000000
|
||||
],
|
||||
"/Nested": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/Pages": "2 0 R",
|
||||
"/Type": "/Catalog",
|
||||
"/WeirdKey+#ba#da#cc#e5": 42
|
||||
},
|
||||
"2 0 R": {
|
||||
"/Count": 1,
|
||||
"/Kids": [
|
||||
"3 0 R"
|
||||
],
|
||||
"/Type": "/Pages"
|
||||
},
|
||||
"3 0 R": {
|
||||
"/Contents": "4 0 R",
|
||||
"/MediaBox": [
|
||||
0,
|
||||
0,
|
||||
612,
|
||||
792
|
||||
],
|
||||
"/Parent": "2 0 R",
|
||||
"/Resources": {
|
||||
"/Font": {
|
||||
"/F1": "6 0 R"
|
||||
}
|
||||
},
|
||||
"/Type": "/Page"
|
||||
},
|
||||
"4 0 R": {
|
||||
"/Length": "5 0 R"
|
||||
},
|
||||
"5 0 R": 44,
|
||||
"6 0 R": {
|
||||
"/BaseFont": "/Helvetica",
|
||||
"/Encoding": "/WinAnsiEncoding",
|
||||
"/Subtype": "/Type1",
|
||||
"/Type": "/Font"
|
||||
},
|
||||
"trailer": {
|
||||
"/ID": [
|
||||
"B—˝\u0013»÷\t×ı \u000f¡i˘6ø",
|
||||
"1AY&SXŠfi#—bd3…'Ł"
|
||||
],
|
||||
"/Root": "1 0 R",
|
||||
"/Size": 7
|
||||
}
|
||||
},
|
||||
"objectinfo": {
|
||||
"1 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"2 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"3 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"4 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": true,
|
||||
"length": 44
|
||||
}
|
||||
},
|
||||
"5 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
},
|
||||
"6 0 R": {
|
||||
"stream": {
|
||||
"filter": null,
|
||||
"is": false,
|
||||
"length": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,21 +10,155 @@
|
||||
{
|
||||
"obj:1 0 R": {
|
||||
"value": {
|
||||
"/Escape\\Key": 42,
|
||||
"/Extra": [
|
||||
"u:Names with binary data",
|
||||
"n:/ABCDEF+#ba#da#cc#e5",
|
||||
"n:/OVERLONG+#c0#81",
|
||||
"n:/OVERLONG+#c1#ff",
|
||||
"/Ok+",
|
||||
"n:/OVERLONG+#e0#81#82",
|
||||
"n:/OVERLONG+#e0#9f#ff",
|
||||
"/Ok+ࠀ",
|
||||
"n:/OVERLONG+#f0#81#82#83",
|
||||
"n:/OVERLONG+#f0#8f#ff#ff",
|
||||
"/Ok+𐀀",
|
||||
"/range+\u0001",
|
||||
"/low+\u0018",
|
||||
"/ABCEDEF+π",
|
||||
"n:/one+#a0two",
|
||||
"/text/plain",
|
||||
"u:Names requiring escaping in JSON",
|
||||
"/Back\\shlash",
|
||||
"/Low\"",
|
||||
"/Low\u001f",
|
||||
"/ExceptSpace ",
|
||||
"/Except!",
|
||||
"u:Very small/large reals",
|
||||
0.00001,
|
||||
1000000000000
|
||||
],
|
||||
"/Nested": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": {
|
||||
"/1": {
|
||||
"/2": {
|
||||
"/3": {
|
||||
"/4": {
|
||||
"/5": {
|
||||
"/6": {
|
||||
"/7": {
|
||||
"/8": {
|
||||
"/9": {
|
||||
"/10": 42
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/Pages": "2 0 R",
|
||||
"/Type": "/Catalog",
|
||||
"n:/WeirdKey+#ba#da#cc#e5": 42
|
||||
@ -78,7 +212,7 @@
|
||||
"value": {
|
||||
"/ID": [
|
||||
"b:42841c13bbf709d79a200fa1691836f8",
|
||||
"b:728c020f464c3cf7e02c12605fa7d88b"
|
||||
"b:31415926535897932384626433832795"
|
||||
],
|
||||
"/Root": "1 0 R",
|
||||
"/Size": 7
|
||||
|
@ -4,21 +4,155 @@
|
||||
|
||||
1 0 obj
|
||||
<<
|
||||
/Escape\Key 42
|
||||
/Extra [
|
||||
(Names with binary data)
|
||||
/ABCDEF+#ba#da#cc#e5
|
||||
/OVERLONG+#c0#81
|
||||
/OVERLONG+#c1#ff
|
||||
/Ok+#c2#80
|
||||
/OVERLONG+#e0#81#82
|
||||
/OVERLONG+#e0#9f#ff
|
||||
/Ok+#e0#a0#80
|
||||
/OVERLONG+#f0#81#82#83
|
||||
/OVERLONG+#f0#8f#ff#ff
|
||||
/Ok+#f0#90#80#80
|
||||
/range+#01
|
||||
/low+#18
|
||||
/ABCEDEF+#cf#80
|
||||
/one+#a0two
|
||||
/text#2fplain
|
||||
(Names requiring escaping in JSON)
|
||||
/Back\shlash
|
||||
/Low"
|
||||
/Low#1f
|
||||
/ExceptSpace#20
|
||||
/Except!
|
||||
(Very small/large reals)
|
||||
0.00001
|
||||
1000000000000
|
||||
]
|
||||
/Nested <<
|
||||
/1 <<
|
||||
/2 <<
|
||||
/3 <<
|
||||
/4 <<
|
||||
/5 <<
|
||||
/6 <<
|
||||
/7 <<
|
||||
/8 <<
|
||||
/9 <<
|
||||
/10 <<
|
||||
/1 <<
|
||||
/2 <<
|
||||
/3 <<
|
||||
/4 <<
|
||||
/5 <<
|
||||
/6 <<
|
||||
/7 <<
|
||||
/8 <<
|
||||
/9 <<
|
||||
/10 <<
|
||||
/1 <<
|
||||
/2 <<
|
||||
/3 <<
|
||||
/4 <<
|
||||
/5 <<
|
||||
/6 <<
|
||||
/7 <<
|
||||
/8 <<
|
||||
/9 <<
|
||||
/10 <<
|
||||
/1 <<
|
||||
/2 <<
|
||||
/3 <<
|
||||
/4 <<
|
||||
/5 <<
|
||||
/6 <<
|
||||
/7 <<
|
||||
/8 <<
|
||||
/9 <<
|
||||
/10 <<
|
||||
/1 <<
|
||||
/2 <<
|
||||
/3 <<
|
||||
/4 <<
|
||||
/5 <<
|
||||
/6 <<
|
||||
/7 <<
|
||||
/8 <<
|
||||
/9 <<
|
||||
/10 <<
|
||||
/1 <<
|
||||
/2 <<
|
||||
/3 <<
|
||||
/4 <<
|
||||
/5 <<
|
||||
/6 <<
|
||||
/7 <<
|
||||
/8 <<
|
||||
/9 <<
|
||||
/10 42
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
>>
|
||||
/Pages 2 0 R
|
||||
/Type /Catalog
|
||||
/WeirdKey+#ba#da#cc#e5 42
|
||||
@ -86,16 +220,16 @@ xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000025 00000 n
|
||||
0000000389 00000 n
|
||||
0000000471 00000 n
|
||||
0000000667 00000 n
|
||||
0000000766 00000 n
|
||||
0000000785 00000 n
|
||||
0000008642 00000 n
|
||||
0000008724 00000 n
|
||||
0000008920 00000 n
|
||||
0000009019 00000 n
|
||||
0000009038 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 7
|
||||
/ID [<42841c13bbf709d79a200fa1691836f8><728c020f464c3cf7e02c12605fa7d88b>]
|
||||
/ID [<42841c13bbf709d79a200fa1691836f8><31415926535897932384626433832795>]
|
||||
>>
|
||||
startxref
|
||||
891
|
||||
9144
|
||||
%%EOF
|
||||
|
@ -3382,6 +3382,22 @@ test_97(QPDF& pdf, char const* arg2)
|
||||
assert(nulls.unparse() == nulls2.unparse());
|
||||
}
|
||||
|
||||
static void
|
||||
test_98(QPDF& pdf, char const* arg2)
|
||||
{
|
||||
// Test QPDFObjectHandle::writeJSON. This test is built for minimal.pdf.
|
||||
for (int i = 1; i < 7; ++i) {
|
||||
auto oh = pdf.getObject(i, 0);
|
||||
Pl_Buffer bf1{"write", nullptr};
|
||||
Pl_Buffer bf2{"get", nullptr};
|
||||
oh.writeJSON(JSON::LATEST, &bf1, true, 7);
|
||||
bf1.finish();
|
||||
oh.getJSON(JSON::LATEST, true).write(&bf2, 7);
|
||||
bf2.finish();
|
||||
assert(bf1.getString() == bf2.getString());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
runtest(int n, char const* filename1, char const* arg2)
|
||||
{
|
||||
@ -3483,7 +3499,7 @@ runtest(int n, char const* filename1, char const* arg2)
|
||||
{78, test_78}, {79, test_79}, {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83},
|
||||
{84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, {88, test_88}, {89, test_89},
|
||||
{90, test_90}, {91, test_91}, {92, test_92}, {93, test_93}, {94, test_94}, {95, test_95},
|
||||
{96, test_96}, {97, test_97}};
|
||||
{96, test_96}, {97, test_97}, {98, test_98}};
|
||||
|
||||
auto fn = test_functions.find(n);
|
||||
if (fn == test_functions.end()) {
|
||||
|
Loading…
Reference in New Issue
Block a user