aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/JSON.cc8
-rw-r--r--libqpdf/QPDFJob.cc37
-rw-r--r--libqpdf/QPDFObjectHandle.cc26
-rw-r--r--libqpdf/QPDF_Array.cc32
-rw-r--r--libqpdf/QPDF_Bool.cc8
-rw-r--r--libqpdf/QPDF_Destroyed.cc7
-rw-r--r--libqpdf/QPDF_Dictionary.cc31
-rw-r--r--libqpdf/QPDF_InlineImage.cc8
-rw-r--r--libqpdf/QPDF_Integer.cc7
-rw-r--r--libqpdf/QPDF_Name.cc74
-rw-r--r--libqpdf/QPDF_Null.cc8
-rw-r--r--libqpdf/QPDF_Operator.cc8
-rw-r--r--libqpdf/QPDF_Real.cc19
-rw-r--r--libqpdf/QPDF_Reserved.cc5
-rw-r--r--libqpdf/QPDF_Stream.cc143
-rw-r--r--libqpdf/QPDF_String.cc42
-rw-r--r--libqpdf/QPDF_Unresolved.cc5
-rw-r--r--libqpdf/QPDF_json.cc168
-rw-r--r--libqpdf/qpdf/JSON_writer.hh137
-rw-r--r--libqpdf/qpdf/QPDFObject_private.hh6
-rw-r--r--libqpdf/qpdf/QPDFValue.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Array.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Bool.hh3
-rw-r--r--libqpdf/qpdf/QPDF_Destroyed.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Dictionary.hh2
-rw-r--r--libqpdf/qpdf/QPDF_InlineImage.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Integer.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Name.hh7
-rw-r--r--libqpdf/qpdf/QPDF_Null.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Operator.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Real.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Reserved.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Stream.hh10
-rw-r--r--libqpdf/qpdf/QPDF_String.hh2
-rw-r--r--libqpdf/qpdf/QPDF_Unresolved.hh2
35 files changed, 524 insertions, 299 deletions
diff --git a/libqpdf/JSON.cc b/libqpdf/JSON.cc
index 27405df7..bc28f236 100644
--- a/libqpdf/JSON.cc
+++ b/libqpdf/JSON.cc
@@ -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");
}
diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc
index a699e38d..a85ff0a2 100644
--- a/libqpdf/QPDFJob.cc
+++ b/libqpdf/QPDFJob.cc
@@ -955,23 +955,6 @@ QPDFJob::getWantedJSONObjects()
}
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)
{
if (m->json_version == 1) {
@@ -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,9 +3081,10 @@ 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
- << ", but the input was successfully replaced\n";
+ *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";
}
}
}
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index d543f98e..9b9849a3 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -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,
diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc
index 789acc35..ab2a4c8c 100644
--- a/libqpdf/QPDF_Array.cc
+++ b/libqpdf/QPDF_Array.cc
@@ -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
diff --git a/libqpdf/QPDF_Bool.cc b/libqpdf/QPDF_Bool.cc
index 05c52f22..97f47881 100644
--- a/libqpdf/QPDF_Bool.cc
+++ b/libqpdf/QPDF_Bool.cc
@@ -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
diff --git a/libqpdf/QPDF_Destroyed.cc b/libqpdf/QPDF_Destroyed.cc
index 4e34b508..5e04566a 100644
--- a/libqpdf/QPDF_Destroyed.cc
+++ b/libqpdf/QPDF_Destroyed.cc
@@ -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();
-}
+} \ No newline at end of file
diff --git a/libqpdf/QPDF_Dictionary.cc b/libqpdf/QPDF_Dictionary.cc
index f7e32fc9..9332b1d3 100644
--- a/libqpdf/QPDF_Dictionary.cc
+++ b/libqpdf/QPDF_Dictionary.cc
@@ -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 {
+ p << "\"" << JSON::Writer::encode_string(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 << "\"n:" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first))
+ << "\": ";
}
+ iter.second.writeJSON(json_version, p);
}
}
- return j;
+ p.writeEnd('}');
}
bool
diff --git a/libqpdf/QPDF_InlineImage.cc b/libqpdf/QPDF_InlineImage.cc
index 18f2fed6..2d62071d 100644
--- a/libqpdf/QPDF_InlineImage.cc
+++ b/libqpdf/QPDF_InlineImage.cc
@@ -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";
}
diff --git a/libqpdf/QPDF_Integer.cc b/libqpdf/QPDF_Integer.cc
index 716a11e0..aa0437a1 100644
--- a/libqpdf/QPDF_Integer.cc
+++ b/libqpdf/QPDF_Integer.cc
@@ -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
diff --git a/libqpdf/QPDF_Name.cc b/libqpdf/QPDF_Name.cc
index 5fde9c65..0e7f441a 100644
--- a/libqpdf/QPDF_Name.cc
+++ b/libqpdf/QPDF_Name.cc
@@ -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 {
+ p << "\"" << JSON::Writer::encode_string(name) << "\"";
+ }
} else {
- return JSON::makeString("n:" + normalizeName(this->name));
+ p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
}
}
}
diff --git a/libqpdf/QPDF_Null.cc b/libqpdf/QPDF_Null.cc
index fdabdfa7..fa4b6cab 100644
--- a/libqpdf/QPDF_Null.cc
+++ b/libqpdf/QPDF_Null.cc
@@ -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";
}
diff --git a/libqpdf/QPDF_Operator.cc b/libqpdf/QPDF_Operator.cc
index d1b2969d..c35a8391 100644
--- a/libqpdf/QPDF_Operator.cc
+++ b/libqpdf/QPDF_Operator.cc
@@ -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";
}
diff --git a/libqpdf/QPDF_Real.cc b/libqpdf/QPDF_Real.cc
index 1d954dcd..f4304397 100644
--- a/libqpdf/QPDF_Real.cc
+++ b/libqpdf/QPDF_Real.cc
@@ -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);
}
diff --git a/libqpdf/QPDF_Reserved.cc b/libqpdf/QPDF_Reserved.cc
index 845d6ebc..da112acc 100644
--- a/libqpdf/QPDF_Reserved.cc
+++ b/libqpdf/QPDF_Reserved.cc
@@ -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();
}
diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc
index a43d91ff..6cfcdd46 100644
--- a/libqpdf/QPDF_Stream.cc
+++ b/libqpdf/QPDF_Stream.cc
@@ -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
@@ -193,77 +191,108 @@ QPDF_Stream::getStreamJSON(
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) {
- Pl_Discard discard;
- Pl_Buffer buf_pl{"stream data"};
- // buf_pl contains valid data and is ready for retrieval of the 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)) {
- // Try again
- filter = false;
- decode_level = qpdf_dl_none;
- buf_pl.getString(); // reset buf_pl
- } else {
- if (json_data == qpdf_sj_file) {
- buf_pl_ready = true;
- }
- break;
- }
- }
- // We can use unsafeShallowCopy because we are only touching top-level keys.
- dict = this->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");
- }
- p->writeString(buf_pl.getString());
- } else if (json_data == qpdf_sj_inline) {
- result.addDictionaryMember(
- "data", JSON::makeBlob(StreamBlobProvider(this, decode_level)));
+ 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"};
+ 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) {
+ bool succeeded =
+ pipeStreamData(data_pipeline, &filtered, 0, decode_level, false, (attempt == 1));
+ if (!succeeded || (filter && !filtered)) {
+ // Try again
+ filter = false;
+ decode_level = qpdf_dl_none;
+ buf_pl.getString(); // reset buf_pl
} else {
- throw std::logic_error("QPDF_Stream: unexpected value of json_data");
+ buf_pl_ready = true;
+ break;
}
}
- result.addDictionaryMember("dict", dict.getJSON(json_version));
- return result;
+ 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.
+ auto dict = stream_dict.unsafeShallowCopy();
+ dict.removeKey("/Length");
+ if (filter && filtered) {
+ dict.removeKey("/Filter");
+ dict.removeKey("/DecodeParms");
+ }
+ if (json_data == qpdf_sj_file) {
+ jw.writeNext() << R"("datafile": ")" << JSON::Writer::encode_string(data_filename) << "\"";
+ p->writeString(buf_pl.getString());
+ } else if (json_data == qpdf_sj_inline) {
+ if (!no_data_key) {
+ jw.writeNext() << R"("data": ")";
+ jw.writeBase64(buf_pl.getString()) << "\"";
+ }
+ } else {
+ throw std::logic_error("QPDF_Stream::writeStreamJSON : unexpected value of json_data");
+ }
+
+ jw.writeNext() << R"("dict": )";
+ dict.writeJSON(json_version, jw);
+ jw.writeEnd('}');
+
+ return decode_level;
}
void
diff --git a/libqpdf/QPDF_String.cc b/libqpdf/QPDF_String.cc
index 3886b399..f425b313 100644
--- a/libqpdf/QPDF_String.cc
+++ b/libqpdf/QPDF_String.cc
@@ -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());
- }
- // 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;
- } 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;
- }
- }
- if (is_unicode) {
- result = "u:" + result;
+
+ p << "\"" << JSON::Writer::encode_string(candidate) << "\"";
} else {
- result = "b:" + QUtil::hex_encode(this->val);
+ // See if we can unambiguously represent as Unicode.
+ if (QUtil::is_utf16(this->val) || QUtil::is_explicit_utf8(this->val)) {
+ 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.
+ p << "\"u:" << JSON::Writer::encode_string(candidate) <<"\"";
+ return;
+ }
+ }
+ p << "\"b:" << QUtil::hex_encode(val) <<"\"";
}
- return JSON::makeString(result);
}
bool
diff --git a/libqpdf/QPDF_Unresolved.cc b/libqpdf/QPDF_Unresolved.cc
index fbf5e15f..dc14c2f2 100644
--- a/libqpdf/QPDF_Unresolved.cc
+++ b/libqpdf/QPDF_Unresolved.cc
@@ -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();
}
diff --git a/libqpdf/QPDF_json.cc b/libqpdf/QPDF_json.cc
index 8326e6a5..b06b70c9 100644
--- a/libqpdf/QPDF_json.cc
+++ b/libqpdf/QPDF_json.cc
@@ -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;
- 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);
+ 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
@@ -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;
- }
- 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);
+ jw << "{";
+ } else if (!first_key) {
+ jw << ",";
+ }
+ 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);
- }
- JSON::writeDictionaryClose(p, first_qpdf_inner, depth_qpdf);
- JSON::writeArrayClose(p, first_qpdf, depth_top);
+ if (!first) {
+ jw << "\n },";
+ }
+ 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;
}
diff --git a/libqpdf/qpdf/JSON_writer.hh b/libqpdf/qpdf/JSON_writer.hh
new file mode 100644
index 00000000..3f770c58
--- /dev/null
+++ b/libqpdf/qpdf/JSON_writer.hh
@@ -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
diff --git a/libqpdf/qpdf/QPDFObject_private.hh b/libqpdf/qpdf/QPDFObject_private.hh
index 5e87c215..1c3dadc9 100644
--- a/libqpdf/qpdf/QPDFObject_private.hh
+++ b/libqpdf/qpdf/QPDFObject_private.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
diff --git a/libqpdf/qpdf/QPDFValue.hh b/libqpdf/qpdf/QPDFValue.hh
index db8fb923..28abf147 100644
--- a/libqpdf/qpdf/QPDFValue.hh
+++ b/libqpdf/qpdf/QPDFValue.hh
@@ -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
{
diff --git a/libqpdf/qpdf/QPDF_Array.hh b/libqpdf/qpdf/QPDF_Array.hh
index 281e1d48..88b2a3d3 100644
--- a/libqpdf/qpdf/QPDF_Array.hh
+++ b/libqpdf/qpdf/QPDF_Array.hh
@@ -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
diff --git a/libqpdf/qpdf/QPDF_Bool.hh b/libqpdf/qpdf/QPDF_Bool.hh
index 0b4a71fd..1692bdc4 100644
--- a/libqpdf/qpdf/QPDF_Bool.hh
+++ b/libqpdf/qpdf/QPDF_Bool.hh
@@ -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:
diff --git a/libqpdf/qpdf/QPDF_Destroyed.hh b/libqpdf/qpdf/QPDF_Destroyed.hh
index 72e9130a..9259a2dc 100644
--- a/libqpdf/qpdf/QPDF_Destroyed.hh
+++ b/libqpdf/qpdf/QPDF_Destroyed.hh
@@ -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:
diff --git a/libqpdf/qpdf/QPDF_Dictionary.hh b/libqpdf/qpdf/QPDF_Dictionary.hh
index 0fb6636e..8713a450 100644
--- a/libqpdf/qpdf/QPDF_Dictionary.hh
+++ b/libqpdf/qpdf/QPDF_Dictionary.hh
@@ -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
diff --git a/libqpdf/qpdf/QPDF_InlineImage.hh b/libqpdf/qpdf/QPDF_InlineImage.hh
index bee12354..c06662d7 100644
--- a/libqpdf/qpdf/QPDF_InlineImage.hh
+++ b/libqpdf/qpdf/QPDF_InlineImage.hh
@@ -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
{
diff --git a/libqpdf/qpdf/QPDF_Integer.hh b/libqpdf/qpdf/QPDF_Integer.hh
index 2c2cf2f9..ae7f7893 100644
--- a/libqpdf/qpdf/QPDF_Integer.hh
+++ b/libqpdf/qpdf/QPDF_Integer.hh
@@ -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:
diff --git a/libqpdf/qpdf/QPDF_Name.hh b/libqpdf/qpdf/QPDF_Name.hh
index c14d8659..b5d3c318 100644
--- a/libqpdf/qpdf/QPDF_Name.hh
+++ b/libqpdf/qpdf/QPDF_Name.hh
@@ -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
{
diff --git a/libqpdf/qpdf/QPDF_Null.hh b/libqpdf/qpdf/QPDF_Null.hh
index a59b7509..fc6e0b5f 100644
--- a/libqpdf/qpdf/QPDF_Null.hh
+++ b/libqpdf/qpdf/QPDF_Null.hh
@@ -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();
diff --git a/libqpdf/qpdf/QPDF_Operator.hh b/libqpdf/qpdf/QPDF_Operator.hh
index 77aa5a17..b9b040d6 100644
--- a/libqpdf/qpdf/QPDF_Operator.hh
+++ b/libqpdf/qpdf/QPDF_Operator.hh
@@ -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
{
diff --git a/libqpdf/qpdf/QPDF_Real.hh b/libqpdf/qpdf/QPDF_Real.hh
index db5e0940..aa9baa5f 100644
--- a/libqpdf/qpdf/QPDF_Real.hh
+++ b/libqpdf/qpdf/QPDF_Real.hh
@@ -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
{
diff --git a/libqpdf/qpdf/QPDF_Reserved.hh b/libqpdf/qpdf/QPDF_Reserved.hh
index 9ba855b7..801987e4 100644
--- a/libqpdf/qpdf/QPDF_Reserved.hh
+++ b/libqpdf/qpdf/QPDF_Reserved.hh
@@ -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();
diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh
index 8488e157..3fcbc428 100644
--- a/libqpdf/qpdf/QPDF_Stream.hh
+++ b/libqpdf/qpdf/QPDF_Stream.hh
@@ -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);
diff --git a/libqpdf/qpdf/QPDF_String.hh b/libqpdf/qpdf/QPDF_String.hh
index c34cafef..967b2d30 100644
--- a/libqpdf/qpdf/QPDF_String.hh
+++ b/libqpdf/qpdf/QPDF_String.hh
@@ -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
diff --git a/libqpdf/qpdf/QPDF_Unresolved.hh b/libqpdf/qpdf/QPDF_Unresolved.hh
index 0a1fa9a5..b4c1d9e4 100644
--- a/libqpdf/qpdf/QPDF_Unresolved.hh
+++ b/libqpdf/qpdf/QPDF_Unresolved.hh
@@ -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);