Skip to content

Commit

Permalink
Add new writeJSON methods
Browse files Browse the repository at this point in the history
Create an alternative to getJSON to allow an object handle to be written as JSON without the overhead of creating a JSON object.
  • Loading branch information
m-holger committed Feb 16, 2024
1 parent 9e90007 commit e2737ab
Show file tree
Hide file tree
Showing 33 changed files with 235 additions and 3 deletions.
2 changes: 2 additions & 0 deletions include/qpdf/QPDFObjectHandle.hh
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,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();
Expand Down
13 changes: 13 additions & 0 deletions libqpdf/QPDFObjectHandle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
QPDFObjectHandle::getStreamJSON(
int json_version,
Expand Down
38 changes: 38 additions & 0 deletions libqpdf/QPDF_Array.cc
Original file line number Diff line number Diff line change
@@ -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>
Expand Down Expand Up @@ -180,6 +181,43 @@ QPDF_Array::getJSON(int json_version)
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
QPDF_Array::at(int n) const noexcept
{
Expand Down
8 changes: 8 additions & 0 deletions libqpdf/QPDF_Bool.cc
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -30,6 +32,12 @@ QPDF_Bool::getJSON(int json_version)
return JSON::makeBool(this->val);
}

void
QPDF_Bool::writeJSON(int json_version, JSON::Writer& p)
{
p << val;
}

bool
QPDF_Bool::getVal() const
{
Expand Down
6 changes: 6 additions & 0 deletions libqpdf/QPDF_Destroyed.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");
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");
}
28 changes: 28 additions & 0 deletions libqpdf/QPDF_Dictionary.cc
Original file line number Diff line number Diff line change
@@ -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>
Expand Down Expand Up @@ -91,6 +92,33 @@ QPDF_Dictionary::getJSON(int json_version)
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
QPDF_Dictionary::hasKey(std::string const& key)
{
Expand Down
8 changes: 8 additions & 0 deletions libqpdf/QPDF_InlineImage.cc
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -29,3 +31,9 @@ QPDF_InlineImage::getJSON(int json_version)
{
return JSON::makeNull();
}

void
QPDF_InlineImage::writeJSON(int json_version, JSON::Writer& p)
{
p << "null";
}
7 changes: 7 additions & 0 deletions libqpdf/QPDF_Integer.cc
Original file line number Diff line number Diff line change
@@ -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) :
Expand Down Expand Up @@ -32,6 +33,12 @@ QPDF_Integer::getJSON(int json_version)
return JSON::makeInt(this->val);
}

void
QPDF_Integer::writeJSON(int json_version, JSON::Writer& p)
{
p << std::to_string(this->val);
}

long long
QPDF_Integer::getVal() const
{
Expand Down
19 changes: 19 additions & 0 deletions libqpdf/QPDF_Name.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <qpdf/QPDF_Name.hh>

#include <qpdf/JSON_writer.hh>
#include <qpdf/QUtil.hh>

QPDF_Name::QPDF_Name(std::string const& name) :
Expand Down Expand Up @@ -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)) << "\"";
}
}
}
7 changes: 7 additions & 0 deletions libqpdf/QPDF_Null.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <qpdf/QPDF_Null.hh>

#include <qpdf/JSON_writer.hh>
#include <qpdf/QPDFObject_private.hh>

QPDF_Null::QPDF_Null() :
Expand Down Expand Up @@ -49,3 +50,9 @@ QPDF_Null::getJSON(int json_version)
// If this is updated, QPDF_Array::getJSON must also be updated.
return JSON::makeNull();
}

void
QPDF_Null::writeJSON(int json_version, JSON::Writer& p)
{
p << "null";
}
8 changes: 8 additions & 0 deletions libqpdf/QPDF_Operator.cc
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -29,3 +31,9 @@ QPDF_Operator::getJSON(int json_version)
{
return JSON::makeNull();
}

void
QPDF_Operator::writeJSON(int json_version, JSON::Writer& p)
{
p << "null";
}
16 changes: 16 additions & 0 deletions libqpdf/QPDF_Real.cc
Original file line number Diff line number Diff line change
@@ -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) :
Expand Down Expand Up @@ -56,3 +57,18 @@ QPDF_Real::getJSON(int json_version)
}
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;
}
}
6 changes: 6 additions & 0 deletions libqpdf/QPDF_Reserved.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ QPDF_Reserved::getJSON(int json_version)
throw std::logic_error("QPDFObjectHandle: attempting to get JSON from a reserved object");
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");
}
7 changes: 7 additions & 0 deletions libqpdf/QPDF_Stream.cc
Original file line number Diff line number Diff line change
@@ -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>
Expand Down Expand Up @@ -185,6 +186,12 @@ QPDF_Stream::getJSON(int json_version)
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
QPDF_Stream::getStreamJSON(
int json_version,
Expand Down
25 changes: 25 additions & 0 deletions libqpdf/QPDF_String.cc
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -74,6 +75,30 @@ QPDF_String::getJSON(int json_version)
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
QPDF_String::useHexString() const
{
Expand Down
6 changes: 6 additions & 0 deletions libqpdf/QPDF_Unresolved.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ QPDF_Unresolved::getJSON(int json_version)
throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle");
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");
}
13 changes: 10 additions & 3 deletions libqpdf/QPDF_json.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#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>
Expand Down Expand Up @@ -864,9 +865,15 @@ 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);
if (first) {
*p << "\n \"" << key << "\": {\n \"value\": ";
first = false;
} else {
*p << ",\n \"" << key << "\": {\n \"value\": ";
}
auto w = JSON::Writer(p, 4);
obj.writeJSON(version, w, true);
*p << "\n }";
}

void
Expand Down
Loading

0 comments on commit e2737ab

Please sign in to comment.