Commit 0500d4347a6d31ef05fd860559e380c2e488c194

Authored by Jay Berkenbilt
1 parent 05fda4af

JSON: add blob type that generates base64-encoded binary data

ChangeLog
1 2022-05-04 Jay Berkenbilt <ejb@ql.org> 1 2022-05-04 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * JSON: add a new "blob" type that takes a function to write data
  4 + into. The blob is serialized as a base64-encoded representation of
  5 + whatever is written to the function.
  6 +
3 * FileInputSource has new constructors that eliminate the need to 7 * FileInputSource has new constructors that eliminate the need to
4 call setFilename or setFile in most cases. 8 call setFilename or setFile in most cases.
5 9
@@ -51,11 +51,6 @@ library, when context is available, to have a pipeline rather than a @@ -51,11 +51,6 @@ library, when context is available, to have a pipeline rather than a
51 FILE* or std::ostream. This makes it possible for people to capture 51 FILE* or std::ostream. This makes it possible for people to capture
52 output more flexibly. 52 output more flexibly.
53 53
54 -Have a json blob defined by a function that takes a pipeline and  
55 -writes data to the pipeline. It's writer should create a Pl_Base64 ->  
56 -Pl_Concatenate in front of the pipeline passed to write and call the  
57 -function with that.  
58 -  
59 For json output, do not unparse to string. Use the writers instead. 54 For json output, do not unparse to string. Use the writers instead.
60 Write incrementally. This changes ordering only, but we should be able 55 Write incrementally. This changes ordering only, but we should be able
61 manually update the test output for those cases. Objects should be 56 manually update the test output for those cases. Objects should be
cSpell.json
@@ -199,6 +199,7 @@ @@ -199,6 +199,7 @@
199 "itemizedlist", 199 "itemizedlist",
200 "jarr", 200 "jarr",
201 "jbig", 201 "jbig",
  202 + "jblob",
202 "jdimension", 203 "jdimension",
203 "jdouble", 204 "jdouble",
204 "jerr", 205 "jerr",
include/qpdf/JSON.hh
@@ -122,6 +122,13 @@ class JSON @@ -122,6 +122,13 @@ class JSON
122 QPDF_DLL 122 QPDF_DLL
123 static JSON makeNull(); 123 static JSON makeNull();
124 124
  125 + // A blob serializes as a string. The function will be called by
  126 + // JSON with a pipeline and should write binary data to the
  127 + // pipeline but not call finish(). JSON will call finish() at the
  128 + // right time.
  129 + QPDF_DLL
  130 + static JSON makeBlob(std::function<void(Pipeline*)>);
  131 +
125 QPDF_DLL 132 QPDF_DLL
126 bool isArray() const; 133 bool isArray() const;
127 134
@@ -323,6 +330,13 @@ class JSON @@ -323,6 +330,13 @@ class JSON
323 virtual ~JSON_null() = default; 330 virtual ~JSON_null() = default;
324 virtual void write(Pipeline*, size_t depth) const; 331 virtual void write(Pipeline*, size_t depth) const;
325 }; 332 };
  333 + struct JSON_blob: public JSON_value
  334 + {
  335 + JSON_blob(std::function<void(Pipeline*)> fn);
  336 + virtual ~JSON_blob() = default;
  337 + virtual void write(Pipeline*, size_t depth) const;
  338 + std::function<void(Pipeline*)> fn;
  339 + };
326 340
327 JSON(std::shared_ptr<JSON_value>); 341 JSON(std::shared_ptr<JSON_value>);
328 342
libqpdf/JSON.cc
1 #include <qpdf/JSON.hh> 1 #include <qpdf/JSON.hh>
2 2
3 #include <qpdf/BufferInputSource.hh> 3 #include <qpdf/BufferInputSource.hh>
  4 +#include <qpdf/Pl_Base64.hh>
  5 +#include <qpdf/Pl_Concatenate.hh>
4 #include <qpdf/Pl_String.hh> 6 #include <qpdf/Pl_String.hh>
5 #include <qpdf/QTC.hh> 7 #include <qpdf/QTC.hh>
6 #include <qpdf/QUtil.hh> 8 #include <qpdf/QUtil.hh>
@@ -168,6 +170,22 @@ JSON::JSON_null::write(Pipeline* p, size_t) const @@ -168,6 +170,22 @@ JSON::JSON_null::write(Pipeline* p, size_t) const
168 *p << "null"; 170 *p << "null";
169 } 171 }
170 172
  173 +JSON::JSON_blob::JSON_blob(std::function<void(Pipeline*)> fn) :
  174 + fn(fn)
  175 +{
  176 +}
  177 +
  178 +void
  179 +JSON::JSON_blob::write(Pipeline* p, size_t) const
  180 +{
  181 + *p << "\"";
  182 + Pl_Concatenate cat("blob concatenate", p);
  183 + Pl_Base64 base64("blob base64", &cat, Pl_Base64::a_encode);
  184 + fn(&base64);
  185 + base64.finish();
  186 + *p << "\"";
  187 +}
  188 +
171 void 189 void
172 JSON::write(Pipeline* p, size_t depth) const 190 JSON::write(Pipeline* p, size_t depth) const
173 { 191 {
@@ -306,6 +324,12 @@ JSON::makeNull() @@ -306,6 +324,12 @@ JSON::makeNull()
306 return JSON(std::make_shared<JSON_null>()); 324 return JSON(std::make_shared<JSON_null>());
307 } 325 }
308 326
  327 +JSON
  328 +JSON::makeBlob(std::function<void(Pipeline*)> fn)
  329 +{
  330 + return JSON(std::make_shared<JSON_blob>(fn));
  331 +}
  332 +
309 bool 333 bool
310 JSON::isArray() const 334 JSON::isArray() const
311 { 335 {
libtests/json.cc
1 #include <qpdf/assert_test.h> 1 #include <qpdf/assert_test.h>
2 2
3 #include <qpdf/JSON.hh> 3 #include <qpdf/JSON.hh>
  4 +#include <qpdf/Pipeline.hh>
4 #include <qpdf/QPDFObjectHandle.hh> 5 #include <qpdf/QPDFObjectHandle.hh>
5 #include <iostream> 6 #include <iostream>
6 7
@@ -113,6 +114,19 @@ test_main() @@ -113,6 +114,19 @@ test_main()
113 {"c", "[\n true\n]"}, 114 {"c", "[\n true\n]"},
114 }; 115 };
115 assert(dvalue == xdvalue); 116 assert(dvalue == xdvalue);
  117 + auto blob_data = [](Pipeline* p) {
  118 + *p << "\x01\x02\x03\x04\x05\xff\xfe\xfd\xfc\xfb";
  119 + };
  120 + JSON jblob = JSON::makeDictionary();
  121 + jblob.addDictionaryMember("normal", JSON::parse(R"("string")"));
  122 + jblob.addDictionaryMember("blob", JSON::makeBlob(blob_data));
  123 + // cSpell:ignore AQIDBAX
  124 + check(
  125 + jblob,
  126 + "{\n"
  127 + " \"blob\": \"AQIDBAX//v38+w==\",\n"
  128 + " \"normal\": \"string\"\n"
  129 + "}");
116 } 130 }
117 131
118 static void 132 static void