Add new writeJSON methods

Create an alternative to getJSON to allow an object handle to be written as JSON without the overhead of creating a JSON object.
This commit is contained in:
m-holger 2024-02-09 13:09:08 +00:00
parent 9e90007a4a
commit e2737ab646
33 changed files with 235 additions and 3 deletions

View File

@ -1353,6 +1353,8 @@ class QPDFObjectHandle
return obj.get(); return obj.get();
} }
void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false);
private: private:
QPDF_Array* asArray(); QPDF_Array* asArray();
QPDF_Bool* asBool(); QPDF_Bool* asBool();

View File

@ -3,6 +3,7 @@
#include <qpdf/BufferInputSource.hh> #include <qpdf/BufferInputSource.hh>
#include <qpdf/Pl_Buffer.hh> #include <qpdf/Pl_Buffer.hh>
#include <qpdf/Pl_QPDFTokenizer.hh> #include <qpdf/Pl_QPDFTokenizer.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QPDF.hh> #include <qpdf/QPDF.hh>
#include <qpdf/QPDFExc.hh> #include <qpdf/QPDFExc.hh>
#include <qpdf/QPDFLogger.hh> #include <qpdf/QPDFLogger.hh>
@ -1621,6 +1622,18 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect)
} }
} }
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);
}
}
JSON JSON
QPDFObjectHandle::getStreamJSON( QPDFObjectHandle::getStreamJSON(
int json_version, int json_version,

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_Array.hh> #include <qpdf/QPDF_Array.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QPDFObjectHandle.hh> #include <qpdf/QPDFObjectHandle.hh>
#include <qpdf/QPDFObject_private.hh> #include <qpdf/QPDFObject_private.hh>
#include <qpdf/QTC.hh> #include <qpdf/QTC.hh>
@ -180,6 +181,43 @@ QPDF_Array::getJSON(int json_version)
return j_array; return j_array;
} }
void
QPDF_Array::writeJSON(int json_version, JSON::Writer& p)
{
p.writeStart('[');
if (sp) {
int next = 0;
for (auto& item: sp->elements) {
int key = item.first;
for (int j = next; j < key; ++j) {
p.writeNext() << "null";
}
p.writeNext();
auto og = item.second->getObjGen();
if (og.isIndirect()) {
p << "\"" << og.unparse(' ') << " R\"";
} else {
item.second->writeJSON(json_version, p);
}
next = ++key;
}
for (int j = next; j < sp->size; ++j) {
p.writeNext() << "null";
}
} else {
for (auto const& item: elements) {
p.writeNext();
auto og = item->getObjGen();
if (og.isIndirect()) {
p << "\"" << og.unparse(' ') << " R\"";
} else {
item->writeJSON(json_version, p);
}
}
}
p.writeEnd(']');
}
QPDFObjectHandle QPDFObjectHandle
QPDF_Array::at(int n) const noexcept QPDF_Array::at(int n) const noexcept
{ {

View File

@ -1,5 +1,7 @@
#include <qpdf/QPDF_Bool.hh> #include <qpdf/QPDF_Bool.hh>
#include <qpdf/JSON_writer.hh>
QPDF_Bool::QPDF_Bool(bool val) : QPDF_Bool::QPDF_Bool(bool val) :
QPDFValue(::ot_boolean, "boolean"), QPDFValue(::ot_boolean, "boolean"),
val(val) val(val)
@ -30,6 +32,12 @@ QPDF_Bool::getJSON(int json_version)
return JSON::makeBool(this->val); return JSON::makeBool(this->val);
} }
void
QPDF_Bool::writeJSON(int json_version, JSON::Writer& p)
{
p << val;
}
bool bool
QPDF_Bool::getVal() const QPDF_Bool::getVal() const
{ {

View File

@ -34,3 +34,9 @@ QPDF_Destroyed::getJSON(int json_version)
throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF"); throw std::logic_error("attempted to get JSON from a QPDFObjectHandle from a destroyed QPDF");
return JSON::makeNull(); return JSON::makeNull();
} }
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");
}

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_Dictionary.hh> #include <qpdf/QPDF_Dictionary.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QPDFObject_private.hh> #include <qpdf/QPDFObject_private.hh>
#include <qpdf/QPDF_Name.hh> #include <qpdf/QPDF_Name.hh>
#include <qpdf/QPDF_Null.hh> #include <qpdf/QPDF_Null.hh>
@ -91,6 +92,33 @@ QPDF_Dictionary::getJSON(int json_version)
return j; return j;
} }
void
QPDF_Dictionary::writeJSON(int json_version, JSON::Writer& p)
{
p.writeStart('{');
for (auto& iter: this->items) {
if (!iter.second.isNull()) {
p.writeNext();
if (json_version == 1) {
p << "\"" << JSON::Writer::encode_string(QPDF_Name::normalizeName(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);
if (!has_8bit_chars || is_valid_utf8) {
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);
}
}
p.writeEnd('}');
}
bool bool
QPDF_Dictionary::hasKey(std::string const& key) QPDF_Dictionary::hasKey(std::string const& key)
{ {

View File

@ -1,5 +1,7 @@
#include <qpdf/QPDF_InlineImage.hh> #include <qpdf/QPDF_InlineImage.hh>
#include <qpdf/JSON_writer.hh>
QPDF_InlineImage::QPDF_InlineImage(std::string const& val) : QPDF_InlineImage::QPDF_InlineImage(std::string const& val) :
QPDFValue(::ot_inlineimage, "inline-image"), QPDFValue(::ot_inlineimage, "inline-image"),
val(val) val(val)
@ -29,3 +31,9 @@ QPDF_InlineImage::getJSON(int json_version)
{ {
return JSON::makeNull(); return JSON::makeNull();
} }
void
QPDF_InlineImage::writeJSON(int json_version, JSON::Writer& p)
{
p << "null";
}

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_Integer.hh> #include <qpdf/QPDF_Integer.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QUtil.hh> #include <qpdf/QUtil.hh>
QPDF_Integer::QPDF_Integer(long long val) : QPDF_Integer::QPDF_Integer(long long val) :
@ -32,6 +33,12 @@ QPDF_Integer::getJSON(int json_version)
return JSON::makeInt(this->val); return JSON::makeInt(this->val);
} }
void
QPDF_Integer::writeJSON(int json_version, JSON::Writer& p)
{
p << std::to_string(this->val);
}
long long long long
QPDF_Integer::getVal() const QPDF_Integer::getVal() const
{ {

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_Name.hh> #include <qpdf/QPDF_Name.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QUtil.hh> #include <qpdf/QUtil.hh>
QPDF_Name::QPDF_Name(std::string const& name) : QPDF_Name::QPDF_Name(std::string const& name) :
@ -68,3 +69,21 @@ QPDF_Name::getJSON(int json_version)
} }
} }
} }
void
QPDF_Name::writeJSON(int json_version, JSON::Writer& p)
{
if (json_version == 1) {
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) {
p << "\"" << JSON::Writer::encode_string(name) << "\"";
} else {
p << "\"n:" << JSON::Writer::encode_string(normalizeName(name)) << "\"";
}
}
}

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_Null.hh> #include <qpdf/QPDF_Null.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QPDFObject_private.hh> #include <qpdf/QPDFObject_private.hh>
QPDF_Null::QPDF_Null() : QPDF_Null::QPDF_Null() :
@ -49,3 +50,9 @@ QPDF_Null::getJSON(int json_version)
// If this is updated, QPDF_Array::getJSON must also be updated. // If this is updated, QPDF_Array::getJSON must also be updated.
return JSON::makeNull(); return JSON::makeNull();
} }
void
QPDF_Null::writeJSON(int json_version, JSON::Writer& p)
{
p << "null";
}

View File

@ -1,5 +1,7 @@
#include <qpdf/QPDF_Operator.hh> #include <qpdf/QPDF_Operator.hh>
#include <qpdf/JSON_writer.hh>
QPDF_Operator::QPDF_Operator(std::string const& val) : QPDF_Operator::QPDF_Operator(std::string const& val) :
QPDFValue(::ot_operator, "operator"), QPDFValue(::ot_operator, "operator"),
val(val) val(val)
@ -29,3 +31,9 @@ QPDF_Operator::getJSON(int json_version)
{ {
return JSON::makeNull(); return JSON::makeNull();
} }
void
QPDF_Operator::writeJSON(int json_version, JSON::Writer& p)
{
p << "null";
}

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_Real.hh> #include <qpdf/QPDF_Real.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QUtil.hh> #include <qpdf/QUtil.hh>
QPDF_Real::QPDF_Real(std::string const& val) : QPDF_Real::QPDF_Real(std::string const& val) :
@ -56,3 +57,18 @@ QPDF_Real::getJSON(int json_version)
} }
return JSON::makeNumber(result); return JSON::makeNumber(result);
} }
void
QPDF_Real::writeJSON(int json_version, JSON::Writer& p)
{
if (this->val.length() == 0) {
// Can't really happen...
p << "0";
} else if (this->val.at(0) == '.') {
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 {
p << this->val;
}
}

View File

@ -32,3 +32,9 @@ QPDF_Reserved::getJSON(int json_version)
throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object"); throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object");
return JSON::makeNull(); return JSON::makeNull();
} }
void
QPDF_Reserved::writeJSON(int json_version, JSON::Writer& p)
{
throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object");
}

View File

@ -1,6 +1,7 @@
#include <qpdf/QPDF_Stream.hh> #include <qpdf/QPDF_Stream.hh>
#include <qpdf/ContentNormalizer.hh> #include <qpdf/ContentNormalizer.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/Pipeline.hh> #include <qpdf/Pipeline.hh>
#include <qpdf/Pl_Base64.hh> #include <qpdf/Pl_Base64.hh>
#include <qpdf/Pl_Buffer.hh> #include <qpdf/Pl_Buffer.hh>
@ -185,6 +186,12 @@ QPDF_Stream::getJSON(int json_version)
return getStreamJSON(json_version, qpdf_sj_none, qpdf_dl_none, nullptr, ""); return getStreamJSON(json_version, qpdf_sj_none, qpdf_dl_none, nullptr, "");
} }
void
QPDF_Stream::writeJSON(int json_version, JSON::Writer& p)
{
stream_dict.writeJSON(json_version, p);
}
JSON JSON
QPDF_Stream::getStreamJSON( QPDF_Stream::getStreamJSON(
int json_version, int json_version,

View File

@ -1,5 +1,6 @@
#include <qpdf/QPDF_String.hh> #include <qpdf/QPDF_String.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/QUtil.hh> #include <qpdf/QUtil.hh>
// DO NOT USE ctype -- it is locale dependent for some things, and it's not worth the risk of // DO NOT USE ctype -- it is locale dependent for some things, and it's not worth the risk of
@ -74,6 +75,30 @@ QPDF_String::getJSON(int json_version)
return JSON::makeString(result); return JSON::makeString(result);
} }
void
QPDF_String::writeJSON(int json_version, JSON::Writer& p)
{
auto candidate = getUTF8Val();
if (json_version == 1) {
p << "\"" << JSON::Writer::encode_string(candidate) << "\"";
} else {
// 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) <<"\"";
}
}
bool bool
QPDF_String::useHexString() const QPDF_String::useHexString() const
{ {

View File

@ -33,3 +33,9 @@ QPDF_Unresolved::getJSON(int json_version)
throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle"); throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle");
return JSON::makeNull(); return JSON::makeNull();
} }
void
QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p)
{
throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle");
}

View File

@ -1,6 +1,7 @@
#include <qpdf/QPDF.hh> #include <qpdf/QPDF.hh>
#include <qpdf/FileInputSource.hh> #include <qpdf/FileInputSource.hh>
#include <qpdf/JSON_writer.hh>
#include <qpdf/Pl_Base64.hh> #include <qpdf/Pl_Base64.hh>
#include <qpdf/Pl_StdioFile.hh> #include <qpdf/Pl_StdioFile.hh>
#include <qpdf/QIntC.hh> #include <qpdf/QIntC.hh>
@ -864,9 +865,15 @@ void
QPDF::writeJSONObject( QPDF::writeJSONObject(
int version, Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle& obj) int version, Pipeline* p, bool& first, std::string const& key, QPDFObjectHandle& obj)
{ {
auto j = JSON::makeDictionary(); if (first) {
j.addDictionaryMember("value", obj.getJSON(version, true)); *p << "\n \"" << key << "\": {\n \"value\": ";
JSON::writeDictionaryItem(p, first, key, j, 3); first = false;
} else {
*p << ",\n \"" << key << "\": {\n \"value\": ";
}
auto w = JSON::Writer(p, 4);
obj.writeJSON(version, w, true);
*p << "\n }";
} }
void void

View File

@ -38,6 +38,11 @@ class QPDFObject
{ {
return value->getJSON(json_version); return value->getJSON(json_version);
} }
void
writeJSON(int json_version, JSON::Writer& p)
{
return value->writeJSON(json_version, p);
}
std::string std::string
getStringValue() const getStringValue() const
{ {

View File

@ -25,6 +25,7 @@ class QPDFValue: public std::enable_shared_from_this<QPDFValue>
virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0; virtual std::shared_ptr<QPDFObject> copy(bool shallow = false) = 0;
virtual std::string unparse() = 0; virtual std::string unparse() = 0;
virtual JSON getJSON(int json_version) = 0; virtual JSON getJSON(int json_version) = 0;
virtual void writeJSON(int json_version, JSON::Writer& p) = 0;
struct JSON_Descr struct JSON_Descr
{ {

View File

@ -23,6 +23,7 @@ class QPDF_Array: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
void disconnect() override; void disconnect() override;
int int

View File

@ -11,6 +11,8 @@ class QPDF_Bool: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
bool getVal() const; bool getVal() const;
private: private:

View File

@ -10,6 +10,7 @@ class QPDF_Destroyed: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
static std::shared_ptr<QPDFValue> getInstance(); static std::shared_ptr<QPDFValue> getInstance();
private: private:

View File

@ -17,6 +17,7 @@ class QPDF_Dictionary: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
void disconnect() override; void disconnect() override;
// hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns // hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns

View File

@ -11,6 +11,7 @@ class QPDF_InlineImage: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
std::string std::string
getStringValue() const override getStringValue() const override
{ {

View File

@ -11,6 +11,7 @@ class QPDF_Integer: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
long long getVal() const; long long getVal() const;
private: private:

View File

@ -11,6 +11,7 @@ class QPDF_Name: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) 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 // Put # into strings with characters unsuitable for name token
static std::string normalizeName(std::string const& name); static std::string normalizeName(std::string const& name);

View File

@ -19,6 +19,7 @@ class QPDF_Null: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
private: private:
QPDF_Null(); QPDF_Null();

View File

@ -11,6 +11,7 @@ class QPDF_Operator: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
std::string std::string
getStringValue() const override getStringValue() const override
{ {

View File

@ -13,6 +13,7 @@ class QPDF_Real: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
std::string std::string
getStringValue() const override getStringValue() const override
{ {

View File

@ -11,6 +11,7 @@ class QPDF_Reserved: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
private: private:
QPDF_Reserved(); QPDF_Reserved();

View File

@ -26,6 +26,7 @@ class QPDF_Stream: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
void setDescription( void setDescription(
QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) override; QPDF*, std::shared_ptr<QPDFValue::Description>& description, qpdf_offset_t offset) override;
void disconnect() override; void disconnect() override;

View File

@ -17,6 +17,7 @@ class QPDF_String: public QPDFValue
std::string unparse() override; std::string unparse() override;
std::string unparse(bool force_binary); std::string unparse(bool force_binary);
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
std::string getUTF8Val() const; std::string getUTF8Val() const;
std::string std::string
getStringValue() const override getStringValue() const override

View File

@ -11,6 +11,7 @@ class QPDF_Unresolved: public QPDFValue
std::shared_ptr<QPDFObject> copy(bool shallow = false) override; std::shared_ptr<QPDFObject> copy(bool shallow = false) override;
std::string unparse() override; std::string unparse() override;
JSON getJSON(int json_version) override; JSON getJSON(int json_version) override;
void writeJSON(int json_version, JSON::Writer& p) override;
private: private:
QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og); QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og);