Commit 4fe2e06b4787ffb639f965ac840b51018308ec07

Authored by Jay Berkenbilt
1 parent ed613003

Add --create-from-json and --update-from-json arguments

Also add stubs for top-level QPDF methods (createFromJSON,
updateFromJSON)
README-maintainer
... ... @@ -215,6 +215,14 @@ CODING RULES
215 215  
216 216 HOW TO ADD A COMMAND-LINE ARGUMENT
217 217  
  218 +Quick reminder:
  219 +
  220 +* Add an entry to the top half of job.yml for the command-line
  221 + argument
  222 +* Add an entry to the bottom half of job.yml for the job JSON field
  223 +* Add documentation for the new option to cli.rst
  224 +* Implement the QPDFJob::Config method in QPDFJob_config.cc.
  225 +
218 226 QPDFJob is documented in three places:
219 227  
220 228 * This section provides a quick reminder for how to add a command-line
... ...
include/qpdf/QPDF.hh
... ... @@ -110,6 +110,28 @@ class QPDF
110 110 void
111 111 processInputSource(std::shared_ptr<InputSource>, char const* password = 0);
112 112  
  113 + // Create a PDF from an input source that contains JSON as written
  114 + // by qpdf --json (version 2 or higher). The JSON must be a
  115 + // complete representation of a PDF. See "QPDF JSON Format" in the
  116 + // manual for details.
  117 + QPDF_DLL
  118 + void
  119 + createFromJSON(std::string const& json_file);
  120 + QPDF_DLL
  121 + void
  122 + createFromJSON(std::shared_ptr<InputSource>);
  123 +
  124 + // Update a PDF from an input source that contains JSON in the
  125 + // same format as is written by qpdf --json (version 2 or higher).
  126 + // Objects in the PDF and not in the JSON are not modified. See
  127 + // "QPDF JSON Format" in the manual for details.
  128 + QPDF_DLL
  129 + void
  130 + updateFromJSON(std::string const& json_file);
  131 + QPDF_DLL
  132 + void
  133 + updateFromJSON(std::shared_ptr<InputSource>);
  134 +
113 135 // Close or otherwise release the input source. Once this has been
114 136 // called, no other methods of qpdf can be called safely except
115 137 // for getWarnings and anyWarnings(). After this has been called,
... ...
include/qpdf/QPDFJob.hh
... ... @@ -323,6 +323,8 @@ class QPDFJob
323 323 Config* outputFile(std::string const& filename);
324 324 QPDF_DLL
325 325 Config* replaceInput();
  326 + QPDF_DLL
  327 + Config* createFromJson(std::string const& filename);
326 328  
327 329 QPDF_DLL
328 330 std::shared_ptr<CopyAttConfig> copyAttachmentsFrom();
... ... @@ -674,6 +676,8 @@ class QPDFJob
674 676 bool check_requires_password;
675 677 std::shared_ptr<char> infilename;
676 678 std::shared_ptr<char> outfilename;
  679 + std::string create_from_json;
  680 + std::string update_from_json;
677 681 };
678 682 std::shared_ptr<Members> m;
679 683 };
... ...
include/qpdf/auto_job_c_main.hh
... ... @@ -68,6 +68,7 @@ QPDF_DLL Config* rotate(std::string const&amp; parameter);
68 68 QPDF_DLL Config* showAttachment(std::string const& parameter);
69 69 QPDF_DLL Config* showObject(std::string const& parameter);
70 70 QPDF_DLL Config* jsonStreamPrefix(std::string const& parameter);
  71 +QPDF_DLL Config* updateFromJson(std::string const& parameter);
71 72 QPDF_DLL Config* collate(std::string const& parameter);
72 73 QPDF_DLL Config* collate();
73 74 QPDF_DLL Config* splitPages(std::string const& parameter);
... ...
job.sums
... ... @@ -3,15 +3,15 @@ generate_auto_job 0514289f2deb3bf7c1a6e85ef7d99ad120321ef5a6fe49d76c5274c6a658d3
3 3 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4
4 4 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42
5 5 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5
6   -include/qpdf/auto_job_c_main.hh 50214d1583d0384e70ce7c91d6bb92c58f8cc125490a680681cfffe6455a1dce
  6 +include/qpdf/auto_job_c_main.hh 178a0c98c80d53036910ec67165dbc3902aa8da857de8a0df52911f005918c54
7 7 include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c18911614fe8e568ec
8 8 include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1
9   -job.yml c046a750e0cf6889b920484ab937bcb999be55273d77b263cb227b82006fbb36
10   -libqpdf/qpdf/auto_job_decl.hh 74df4d7fdbdf51ecd0d58ce1e9844bb5525b9adac5a45f7c9a787ecdda2868df
11   -libqpdf/qpdf/auto_job_help.hh e9b37d33bfcbf165bfba21b6778df3f356b904a961bfae68f9638b85142a87e8
12   -libqpdf/qpdf/auto_job_init.hh 423157a51fa470fb45d6e341cc3fc8f044b5344f06f86475b37302610c7d8afd
13   -libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a9474319fb7c86d92634cc8297
14   -libqpdf/qpdf/auto_job_json_init.hh eaed8624a1a394c75a3e298e1c31015146211e240d710509eb627fc711a387a6
15   -libqpdf/qpdf/auto_job_schema.hh a9971c82c9821a5ec620ccc003bcb3383c054e45658b50fa559b5855e694ed1a
  9 +job.yml a95b2446066293f409b36032a0ee411dbe570a7a94f5fd295048d009215f993f
  10 +libqpdf/qpdf/auto_job_decl.hh 833bde9c1f8fc17b914f16498e26d9d1315361645b4ac5c50c1f830a76618ca7
  11 +libqpdf/qpdf/auto_job_help.hh 38bf89067ca7dc244e4e697c598ba0ba8a827600a78b9195fc69f0ac1663f3a2
  12 +libqpdf/qpdf/auto_job_init.hh f0ffab312430b232a7288b8443382ed859021e6ad6ed2c8c9a4dbbd2b33e2aa7
  13 +libqpdf/qpdf/auto_job_json_decl.hh 81d09d4b82b2e042a64246ed1d7a187bdc83b671b45e7b8ee60ad37c0c11e9a7
  14 +libqpdf/qpdf/auto_job_json_init.hh 2fcdae08365abe351d2dfb6a823e2b3af27a6510632de23cabef6cefa5dfb199
  15 +libqpdf/qpdf/auto_job_schema.hh 1a80be3b8d97e9b5a55b8aa45a4b312668b1687eab6f038c4ee5f4662ab71997
16 16 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
17   -manual/cli.rst f1bbf59ce4fdb5a6d29fc2470788eee321423dd946984fc2e6f3a904fe5137c1
  17 +manual/cli.rst 4550dd1b459721d8ef9affa7c9c07f351a06379498ec1291f702f1a5994b6d84
... ...
... ... @@ -86,6 +86,7 @@ options:
86 86 - underlay
87 87 - empty
88 88 - replace-input
  89 + - create-from-json
89 90 positional: true
90 91 bare:
91 92 - add-attachment
... ... @@ -163,6 +164,8 @@ options:
163 164 show-attachment: attachment
164 165 show-object: trailer
165 166 json-stream-prefix: stream-file-prefix
  167 + create-from-json: qpdf-json file
  168 + update-from-json: qpdf-json file
166 169 required_choices:
167 170 compress-streams: yn
168 171 decode-level: decode_level
... ... @@ -278,6 +281,7 @@ json:
278 281 main.password:
279 282 password-file:
280 283 empty:
  284 + create-from-json:
281 285 # output
282 286 _outputFile: "output filename"
283 287 replace-input:
... ... @@ -360,6 +364,7 @@ json:
360 364 json-stream-prefix:
361 365 to-json:
362 366 # other options
  367 + update-from-json:
363 368 allow-weak-crypto:
364 369 keep-files-open:
365 370 keep-files-open-threshold:
... ...
libqpdf/CMakeLists.txt
... ... @@ -97,6 +97,7 @@ set(libqpdf_SOURCES
97 97 QPDF_Stream.cc
98 98 QPDF_String.cc
99 99 QPDF_encryption.cc
  100 + QPDF_json.cc
100 101 QPDF_linearization.cc
101 102 QPDF_optimization.cc
102 103 QPDF_pages.cc
... ...
libqpdf/QPDFJob.cc
... ... @@ -553,6 +553,13 @@ QPDFJob::run()
553 553 if (m->check_is_encrypted || m->check_requires_password) {
554 554 return;
555 555 }
  556 +
  557 + // If we are updating from JSON, this has to be done first before
  558 + // other options may cause transformations to the input.
  559 + if (!this->m->update_from_json.empty()) {
  560 + pdf.updateFromJSON(this->m->update_from_json);
  561 + }
  562 +
556 563 bool other_warnings = false;
557 564 std::vector<std::shared_ptr<QPDF>> page_heap;
558 565 if (!m->page_specs.empty()) {
... ... @@ -1937,7 +1944,11 @@ QPDFJob::doProcessOnce(
1937 1944 auto pdf = std::make_shared<QPDF>();
1938 1945 setQPDFOptions(*pdf);
1939 1946 if (empty) {
1940   - pdf->emptyPDF();
  1947 + if (!this->m->create_from_json.empty()) {
  1948 + pdf->createFromJSON(this->m->create_from_json);
  1949 + } else {
  1950 + pdf->emptyPDF();
  1951 + }
1941 1952 } else {
1942 1953 fn(pdf.get(), password);
1943 1954 }
... ...
libqpdf/QPDFJob_argv.cc
... ... @@ -101,6 +101,13 @@ ArgParser::argReplaceInput()
101 101 }
102 102  
103 103 void
  104 +ArgParser::argCreateFromJson(std::string const& arg)
  105 +{
  106 + c_main->createFromJson(arg);
  107 + this->gave_input = true;
  108 +}
  109 +
  110 +void
104 111 ArgParser::argVersion()
105 112 {
106 113 auto whoami = this->ap.getProgname();
... ...
libqpdf/QPDFJob_config.cc
... ... @@ -25,12 +25,14 @@ QPDFJob::Config::emptyInput()
25 25 {
26 26 if (o.m->infilename == 0) {
27 27 // Various places in QPDFJob.cc know that the empty string for
28   - // infile means empty. This means that passing "" as the
29   - // argument to inputFile, or equivalently using "" as a
30   - // positional command-line argument would be the same as
31   - // --empty. This probably isn't worth blocking or coding
32   - // around, but it would be better if we had a tighter way of
33   - // knowing that the input file is empty.
  28 + // infile means empty. We set it to something other than a
  29 + // null pointer as an indication that some input source has
  30 + // been specified. The --create-from-json option also sets
  31 + // infilename to empty. This approach means that passing "" as
  32 + // the argument to inputFile in job JSON, or equivalently
  33 + // using "" as a positional command-line argument would be the
  34 + // same as --empty. This probably isn't worth blocking or
  35 + // coding around.
34 36 o.m->infilename = QUtil::make_shared_cstr("");
35 37 } else {
36 38 usage("empty input can't be used"
... ... @@ -294,6 +296,23 @@ QPDFJob::Config::toJson()
294 296 }
295 297  
296 298 QPDFJob::Config*
  299 +QPDFJob::Config::createFromJson(std::string const& parameter)
  300 +{
  301 + // See comments in emptyInput() about setting infilename to the
  302 + // empty string.
  303 + o.m->infilename = QUtil::make_shared_cstr("");
  304 + o.m->create_from_json = parameter;
  305 + return this;
  306 +}
  307 +
  308 +QPDFJob::Config*
  309 +QPDFJob::Config::updateFromJson(std::string const& parameter)
  310 +{
  311 + o.m->update_from_json = parameter;
  312 + return this;
  313 +}
  314 +
  315 +QPDFJob::Config*
297 316 QPDFJob::Config::testJsonSchema()
298 317 {
299 318 o.m->test_json_schema = true;
... ...
libqpdf/QPDFJob_json.cc
... ... @@ -251,6 +251,12 @@ Handlers::setupEmpty()
251 251 }
252 252  
253 253 void
  254 +Handlers::setupCreateFromJson()
  255 +{
  256 + addParameter([this](char const* p) { c_main->createFromJson(p); });
  257 +}
  258 +
  259 +void
254 260 Handlers::setupOutputFile()
255 261 {
256 262 addParameter([this](char const* p) { c_main->outputFile(p); });
... ...
libqpdf/QPDF_json.cc 0 โ†’ 100644
  1 +#include <qpdf/QPDF.hh>
  2 +
  3 +#include <qpdf/FileInputSource.hh>
  4 +
  5 +void
  6 +QPDF::createFromJSON(std::string const& json_file)
  7 +{
  8 + createFromJSON(std::make_shared<FileInputSource>(json_file.c_str()));
  9 +}
  10 +
  11 +void
  12 +QPDF::createFromJSON(std::shared_ptr<InputSource>)
  13 +{
  14 + // QXXXQ
  15 +}
  16 +
  17 +void
  18 +QPDF::updateFromJSON(std::string const& json_file)
  19 +{
  20 + updateFromJSON(std::make_shared<FileInputSource>(json_file.c_str()));
  21 +}
  22 +
  23 +void
  24 +QPDF::updateFromJSON(std::shared_ptr<InputSource>)
  25 +{
  26 + // QXXXQ
  27 +}
... ...
libqpdf/qpdf/auto_job_decl.hh
... ... @@ -28,6 +28,7 @@ void argOverlay();
28 28 void argPages();
29 29 void argReplaceInput();
30 30 void argUnderlay();
  31 +void argCreateFromJson(std::string const&);
31 32 void argPagesPositional(std::string const&);
32 33 void argPagesPassword(std::string const&);
33 34 void argEndPages();
... ...
libqpdf/qpdf/auto_job_help.hh
... ... @@ -836,6 +836,21 @@ name as the prefix for stream data files. Whatever is given here
836 836 will be appended with -nnn to create the name of the file that
837 837 will contain the data for the stream stream in object nnn.
838 838 )");
  839 +ap.addOptionHelp("--create-from-json", "json", "create PDF from qpdf JSON", R"(--create-from-json=qpdf-json-file
  840 +
  841 +Create a PDF file from the prior output of qpdf --json. See the
  842 +"QPDF JSON Format" section of the manual for information about
  843 +how to use this option.
  844 +)");
  845 +ap.addOptionHelp("--update-from-json", "json", "update a PDF from qpdf JSON", R"(--update-from-json=qpdf-json-file
  846 +
  847 +Update a PDF file from a JSON file. Please see the "QPDF JSON
  848 +Format" section of the manual for information about how to use
  849 +this option.
  850 +)");
  851 +}
  852 +static void add_help_8(QPDFArgParser& ap)
  853 +{
839 854 ap.addHelpTopic("testing", "options for testing or debugging", R"(The options below are useful when writing automated test code that
840 855 includes files created by qpdf or when testing qpdf itself.
841 856 )");
... ... @@ -843,9 +858,6 @@ ap.addOptionHelp(&quot;--static-id&quot;, &quot;testing&quot;, &quot;use a fixed document ID&quot;, R&quot;(Use a f
843 858 testing only. Never use it for production files. See also
844 859 qpdf --help=--deterministic-id.
845 860 )");
846   -}
847   -static void add_help_8(QPDFArgParser& ap)
848   -{
849 861 ap.addOptionHelp("--static-aes-iv", "testing", "use a fixed AES vector", R"(Use a static initialization vector for AES-CBC. This is intended
850 862 for testing only so that output files can be reproducible. Never
851 863 use it for production files. This option is not secure since it
... ...
libqpdf/qpdf/auto_job_init.hh
... ... @@ -104,6 +104,8 @@ this-&gt;ap.addRequiredParameter(&quot;rotate&quot;, [this](std::string const&amp; x){c_main-&gt;rot
104 104 this->ap.addRequiredParameter("show-attachment", [this](std::string const& x){c_main->showAttachment(x);}, "attachment");
105 105 this->ap.addRequiredParameter("show-object", [this](std::string const& x){c_main->showObject(x);}, "trailer");
106 106 this->ap.addRequiredParameter("json-stream-prefix", [this](std::string const& x){c_main->jsonStreamPrefix(x);}, "stream-file-prefix");
  107 +this->ap.addRequiredParameter("create-from-json", p(&ArgParser::argCreateFromJson), "qpdf-json file");
  108 +this->ap.addRequiredParameter("update-from-json", [this](std::string const& x){c_main->updateFromJson(x);}, "qpdf-json file");
107 109 this->ap.addOptionalParameter("collate", [this](std::string const& x){c_main->collate(x);});
108 110 this->ap.addOptionalParameter("split-pages", [this](std::string const& x){c_main->splitPages(x);});
109 111 this->ap.addChoices("compress-streams", [this](std::string const& x){c_main->compressStreams(x);}, true, yn_choices);
... ...
libqpdf/qpdf/auto_job_json_decl.hh
... ... @@ -8,6 +8,7 @@
8 8 void setupInputFile();
9 9 void setupPassword();
10 10 void setupEmpty();
  11 +void setupCreateFromJson();
11 12 void setupOutputFile();
12 13 void setupReplaceInput();
13 14 void beginEncrypt(JSON);
... ...
libqpdf/qpdf/auto_job_json_init.hh
... ... @@ -30,6 +30,9 @@ popHandler(); // key: passwordFile
30 30 pushKey("empty");
31 31 setupEmpty();
32 32 popHandler(); // key: empty
  33 +pushKey("createFromJson");
  34 +setupCreateFromJson();
  35 +popHandler(); // key: createFromJson
33 36 pushKey("outputFile");
34 37 setupOutputFile();
35 38 popHandler(); // key: outputFile
... ... @@ -262,6 +265,9 @@ popHandler(); // key: jsonStreamPrefix
262 265 pushKey("toJson");
263 266 addBare([this]() { c_main->toJson(); });
264 267 popHandler(); // key: toJson
  268 +pushKey("updateFromJson");
  269 +addParameter([this](std::string const& p) { c_main->updateFromJson(p); });
  270 +popHandler(); // key: updateFromJson
265 271 pushKey("allowWeakCrypto");
266 272 addBare([this]() { c_main->allowWeakCrypto(); });
267 273 popHandler(); // key: allowWeakCrypto
... ...
libqpdf/qpdf/auto_job_schema.hh
... ... @@ -3,6 +3,7 @@ static constexpr char const* JOB_SCHEMA_DATA = R&quot;({
3 3 "password": "password for encrypted file",
4 4 "passwordFile": "read password from a file",
5 5 "empty": "use empty file as input",
  6 + "createFromJson": "create PDF from qpdf JSON",
6 7 "outputFile": "output filename",
7 8 "replaceInput": "overwrite input with output",
8 9 "qdf": "enable viewing PDF code in a text editor",
... ... @@ -87,6 +88,7 @@ static constexpr char const* JOB_SCHEMA_DATA = R&quot;({
87 88 "jsonStreamData": "how to handle streams in json output",
88 89 "jsonStreamPrefix": "prefix for json stream data files",
89 90 "toJson": "serialize to JSON",
  91 + "updateFromJson": "update a PDF from qpdf JSON",
90 92 "allowWeakCrypto": "allow insecure cryptographic algorithms",
91 93 "keepFilesOpen": "manage keeping multiple files open",
92 94 "keepFilesOpenThreshold": "set threshold for keepFilesOpen",
... ...
manual/cli.rst
... ... @@ -3270,6 +3270,31 @@ Related Options
3270 3270 :samp:`-{nnn}` to create the name of the file that will contain the
3271 3271 data for the stream stream in object :samp:`{nnn}`.
3272 3272  
  3273 +.. qpdf:option:: --create-from-json=qpdf-json-file
  3274 +
  3275 + .. help: create PDF from qpdf JSON
  3276 +
  3277 + Create a PDF file from the prior output of qpdf --json. See the
  3278 + "QPDF JSON Format" section of the manual for information about
  3279 + how to use this option.
  3280 +
  3281 + This option creates a PDF file from the previous output of ``qpdf
  3282 + --json`` that includes stream data and information about all
  3283 + objects. For information about converting between PDF and JSON,
  3284 + please see :ref:`qpdf-json`.
  3285 +
  3286 +.. qpdf:option:: --update-from-json=qpdf-json-file
  3287 +
  3288 + .. help: update a PDF from qpdf JSON
  3289 +
  3290 + Update a PDF file from a JSON file. Please see the "QPDF JSON
  3291 + Format" section of the manual for information about how to use
  3292 + this option.
  3293 +
  3294 + This option updates a PDF file from a qpdf JSON file. For a
  3295 + information about how to use this option, please see
  3296 + :ref:`qpdf-json`.
  3297 +
3273 3298 .. _test-options:
3274 3299  
3275 3300 Options for Testing or Debugging
... ...
manual/json.rst
... ... @@ -18,6 +18,13 @@ files programmatically from the command-line in languages that can&#39;t
18 18 call or link with the qpdf library directly. Note that stream data can
19 19 be extracted from PDF files using other qpdf command-line options.
20 20  
  21 +.. _qpdf-json:
  22 +
  23 +QPDF JSON Format
  24 +----------------
  25 +
  26 +QXXXQ Write this.
  27 +
21 28 .. _json-guarantees:
22 29  
23 30 JSON Guarantees
... ...