2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-31 01:10:51 +00:00

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

Also add stubs for top-level QPDF methods (createFromJSON,
updateFromJSON)
This commit is contained in:
Jay Berkenbilt 2022-05-08 13:42:16 -04:00
parent ed6130036c
commit 4fe2e06b47
20 changed files with 186 additions and 19 deletions

View File

@ -215,6 +215,14 @@ CODING RULES
HOW TO ADD A COMMAND-LINE ARGUMENT HOW TO ADD A COMMAND-LINE ARGUMENT
Quick reminder:
* Add an entry to the top half of job.yml for the command-line
argument
* Add an entry to the bottom half of job.yml for the job JSON field
* Add documentation for the new option to cli.rst
* Implement the QPDFJob::Config method in QPDFJob_config.cc.
QPDFJob is documented in three places: QPDFJob is documented in three places:
* This section provides a quick reminder for how to add a command-line * This section provides a quick reminder for how to add a command-line

View File

@ -110,6 +110,28 @@ class QPDF
void void
processInputSource(std::shared_ptr<InputSource>, char const* password = 0); processInputSource(std::shared_ptr<InputSource>, char const* password = 0);
// Create a PDF from an input source that contains JSON as written
// by qpdf --json (version 2 or higher). The JSON must be a
// complete representation of a PDF. See "QPDF JSON Format" in the
// manual for details.
QPDF_DLL
void
createFromJSON(std::string const& json_file);
QPDF_DLL
void
createFromJSON(std::shared_ptr<InputSource>);
// Update a PDF from an input source that contains JSON in the
// same format as is written by qpdf --json (version 2 or higher).
// Objects in the PDF and not in the JSON are not modified. See
// "QPDF JSON Format" in the manual for details.
QPDF_DLL
void
updateFromJSON(std::string const& json_file);
QPDF_DLL
void
updateFromJSON(std::shared_ptr<InputSource>);
// Close or otherwise release the input source. Once this has been // Close or otherwise release the input source. Once this has been
// called, no other methods of qpdf can be called safely except // called, no other methods of qpdf can be called safely except
// for getWarnings and anyWarnings(). After this has been called, // for getWarnings and anyWarnings(). After this has been called,

View File

@ -323,6 +323,8 @@ class QPDFJob
Config* outputFile(std::string const& filename); Config* outputFile(std::string const& filename);
QPDF_DLL QPDF_DLL
Config* replaceInput(); Config* replaceInput();
QPDF_DLL
Config* createFromJson(std::string const& filename);
QPDF_DLL QPDF_DLL
std::shared_ptr<CopyAttConfig> copyAttachmentsFrom(); std::shared_ptr<CopyAttConfig> copyAttachmentsFrom();
@ -674,6 +676,8 @@ class QPDFJob
bool check_requires_password; bool check_requires_password;
std::shared_ptr<char> infilename; std::shared_ptr<char> infilename;
std::shared_ptr<char> outfilename; std::shared_ptr<char> outfilename;
std::string create_from_json;
std::string update_from_json;
}; };
std::shared_ptr<Members> m; std::shared_ptr<Members> m;
}; };

View File

@ -68,6 +68,7 @@ QPDF_DLL Config* rotate(std::string const& parameter);
QPDF_DLL Config* showAttachment(std::string const& parameter); QPDF_DLL Config* showAttachment(std::string const& parameter);
QPDF_DLL Config* showObject(std::string const& parameter); QPDF_DLL Config* showObject(std::string const& parameter);
QPDF_DLL Config* jsonStreamPrefix(std::string const& parameter); QPDF_DLL Config* jsonStreamPrefix(std::string const& parameter);
QPDF_DLL Config* updateFromJson(std::string const& parameter);
QPDF_DLL Config* collate(std::string const& parameter); QPDF_DLL Config* collate(std::string const& parameter);
QPDF_DLL Config* collate(); QPDF_DLL Config* collate();
QPDF_DLL Config* splitPages(std::string const& parameter); QPDF_DLL Config* splitPages(std::string const& parameter);

View File

@ -3,15 +3,15 @@ generate_auto_job 0514289f2deb3bf7c1a6e85ef7d99ad120321ef5a6fe49d76c5274c6a658d3
include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4
include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42
include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5
include/qpdf/auto_job_c_main.hh 50214d1583d0384e70ce7c91d6bb92c58f8cc125490a680681cfffe6455a1dce include/qpdf/auto_job_c_main.hh 178a0c98c80d53036910ec67165dbc3902aa8da857de8a0df52911f005918c54
include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c18911614fe8e568ec include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c18911614fe8e568ec
include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1 include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1
job.yml c046a750e0cf6889b920484ab937bcb999be55273d77b263cb227b82006fbb36 job.yml a95b2446066293f409b36032a0ee411dbe570a7a94f5fd295048d009215f993f
libqpdf/qpdf/auto_job_decl.hh 74df4d7fdbdf51ecd0d58ce1e9844bb5525b9adac5a45f7c9a787ecdda2868df libqpdf/qpdf/auto_job_decl.hh 833bde9c1f8fc17b914f16498e26d9d1315361645b4ac5c50c1f830a76618ca7
libqpdf/qpdf/auto_job_help.hh e9b37d33bfcbf165bfba21b6778df3f356b904a961bfae68f9638b85142a87e8 libqpdf/qpdf/auto_job_help.hh 38bf89067ca7dc244e4e697c598ba0ba8a827600a78b9195fc69f0ac1663f3a2
libqpdf/qpdf/auto_job_init.hh 423157a51fa470fb45d6e341cc3fc8f044b5344f06f86475b37302610c7d8afd libqpdf/qpdf/auto_job_init.hh f0ffab312430b232a7288b8443382ed859021e6ad6ed2c8c9a4dbbd2b33e2aa7
libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a9474319fb7c86d92634cc8297 libqpdf/qpdf/auto_job_json_decl.hh 81d09d4b82b2e042a64246ed1d7a187bdc83b671b45e7b8ee60ad37c0c11e9a7
libqpdf/qpdf/auto_job_json_init.hh eaed8624a1a394c75a3e298e1c31015146211e240d710509eb627fc711a387a6 libqpdf/qpdf/auto_job_json_init.hh 2fcdae08365abe351d2dfb6a823e2b3af27a6510632de23cabef6cefa5dfb199
libqpdf/qpdf/auto_job_schema.hh a9971c82c9821a5ec620ccc003bcb3383c054e45658b50fa559b5855e694ed1a libqpdf/qpdf/auto_job_schema.hh 1a80be3b8d97e9b5a55b8aa45a4b312668b1687eab6f038c4ee5f4662ab71997
manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
manual/cli.rst f1bbf59ce4fdb5a6d29fc2470788eee321423dd946984fc2e6f3a904fe5137c1 manual/cli.rst 4550dd1b459721d8ef9affa7c9c07f351a06379498ec1291f702f1a5994b6d84

View File

@ -86,6 +86,7 @@ options:
- underlay - underlay
- empty - empty
- replace-input - replace-input
- create-from-json
positional: true positional: true
bare: bare:
- add-attachment - add-attachment
@ -163,6 +164,8 @@ options:
show-attachment: attachment show-attachment: attachment
show-object: trailer show-object: trailer
json-stream-prefix: stream-file-prefix json-stream-prefix: stream-file-prefix
create-from-json: qpdf-json file
update-from-json: qpdf-json file
required_choices: required_choices:
compress-streams: yn compress-streams: yn
decode-level: decode_level decode-level: decode_level
@ -278,6 +281,7 @@ json:
main.password: main.password:
password-file: password-file:
empty: empty:
create-from-json:
# output # output
_outputFile: "output filename" _outputFile: "output filename"
replace-input: replace-input:
@ -360,6 +364,7 @@ json:
json-stream-prefix: json-stream-prefix:
to-json: to-json:
# other options # other options
update-from-json:
allow-weak-crypto: allow-weak-crypto:
keep-files-open: keep-files-open:
keep-files-open-threshold: keep-files-open-threshold:

View File

@ -97,6 +97,7 @@ set(libqpdf_SOURCES
QPDF_Stream.cc QPDF_Stream.cc
QPDF_String.cc QPDF_String.cc
QPDF_encryption.cc QPDF_encryption.cc
QPDF_json.cc
QPDF_linearization.cc QPDF_linearization.cc
QPDF_optimization.cc QPDF_optimization.cc
QPDF_pages.cc QPDF_pages.cc

View File

@ -553,6 +553,13 @@ QPDFJob::run()
if (m->check_is_encrypted || m->check_requires_password) { if (m->check_is_encrypted || m->check_requires_password) {
return; return;
} }
// If we are updating from JSON, this has to be done first before
// other options may cause transformations to the input.
if (!this->m->update_from_json.empty()) {
pdf.updateFromJSON(this->m->update_from_json);
}
bool other_warnings = false; bool other_warnings = false;
std::vector<std::shared_ptr<QPDF>> page_heap; std::vector<std::shared_ptr<QPDF>> page_heap;
if (!m->page_specs.empty()) { if (!m->page_specs.empty()) {
@ -1937,7 +1944,11 @@ QPDFJob::doProcessOnce(
auto pdf = std::make_shared<QPDF>(); auto pdf = std::make_shared<QPDF>();
setQPDFOptions(*pdf); setQPDFOptions(*pdf);
if (empty) { if (empty) {
pdf->emptyPDF(); if (!this->m->create_from_json.empty()) {
pdf->createFromJSON(this->m->create_from_json);
} else {
pdf->emptyPDF();
}
} else { } else {
fn(pdf.get(), password); fn(pdf.get(), password);
} }

View File

@ -100,6 +100,13 @@ ArgParser::argReplaceInput()
this->gave_output = true; this->gave_output = true;
} }
void
ArgParser::argCreateFromJson(std::string const& arg)
{
c_main->createFromJson(arg);
this->gave_input = true;
}
void void
ArgParser::argVersion() ArgParser::argVersion()
{ {

View File

@ -25,12 +25,14 @@ QPDFJob::Config::emptyInput()
{ {
if (o.m->infilename == 0) { if (o.m->infilename == 0) {
// Various places in QPDFJob.cc know that the empty string for // Various places in QPDFJob.cc know that the empty string for
// infile means empty. This means that passing "" as the // infile means empty. We set it to something other than a
// argument to inputFile, or equivalently using "" as a // null pointer as an indication that some input source has
// positional command-line argument would be the same as // been specified. The --create-from-json option also sets
// --empty. This probably isn't worth blocking or coding // infilename to empty. This approach means that passing "" as
// around, but it would be better if we had a tighter way of // the argument to inputFile in job JSON, or equivalently
// knowing that the input file is empty. // using "" as a positional command-line argument would be the
// same as --empty. This probably isn't worth blocking or
// coding around.
o.m->infilename = QUtil::make_shared_cstr(""); o.m->infilename = QUtil::make_shared_cstr("");
} else { } else {
usage("empty input can't be used" usage("empty input can't be used"
@ -293,6 +295,23 @@ QPDFJob::Config::toJson()
return this; return this;
} }
QPDFJob::Config*
QPDFJob::Config::createFromJson(std::string const& parameter)
{
// See comments in emptyInput() about setting infilename to the
// empty string.
o.m->infilename = QUtil::make_shared_cstr("");
o.m->create_from_json = parameter;
return this;
}
QPDFJob::Config*
QPDFJob::Config::updateFromJson(std::string const& parameter)
{
o.m->update_from_json = parameter;
return this;
}
QPDFJob::Config* QPDFJob::Config*
QPDFJob::Config::testJsonSchema() QPDFJob::Config::testJsonSchema()
{ {

View File

@ -250,6 +250,12 @@ Handlers::setupEmpty()
addBare([this]() { c_main->emptyInput(); }); addBare([this]() { c_main->emptyInput(); });
} }
void
Handlers::setupCreateFromJson()
{
addParameter([this](char const* p) { c_main->createFromJson(p); });
}
void void
Handlers::setupOutputFile() Handlers::setupOutputFile()
{ {

27
libqpdf/QPDF_json.cc Normal file
View File

@ -0,0 +1,27 @@
#include <qpdf/QPDF.hh>
#include <qpdf/FileInputSource.hh>
void
QPDF::createFromJSON(std::string const& json_file)
{
createFromJSON(std::make_shared<FileInputSource>(json_file.c_str()));
}
void
QPDF::createFromJSON(std::shared_ptr<InputSource>)
{
// QXXXQ
}
void
QPDF::updateFromJSON(std::string const& json_file)
{
updateFromJSON(std::make_shared<FileInputSource>(json_file.c_str()));
}
void
QPDF::updateFromJSON(std::shared_ptr<InputSource>)
{
// QXXXQ
}

View File

@ -28,6 +28,7 @@ void argOverlay();
void argPages(); void argPages();
void argReplaceInput(); void argReplaceInput();
void argUnderlay(); void argUnderlay();
void argCreateFromJson(std::string const&);
void argPagesPositional(std::string const&); void argPagesPositional(std::string const&);
void argPagesPassword(std::string const&); void argPagesPassword(std::string const&);
void argEndPages(); void argEndPages();

View File

@ -836,6 +836,21 @@ name as the prefix for stream data files. Whatever is given here
will be appended with -nnn to create the name of the file that will be appended with -nnn to create the name of the file that
will contain the data for the stream stream in object nnn. will contain the data for the stream stream in object nnn.
)"); )");
ap.addOptionHelp("--create-from-json", "json", "create PDF from qpdf JSON", R"(--create-from-json=qpdf-json-file
Create a PDF file from the prior output of qpdf --json. See the
"QPDF JSON Format" section of the manual for information about
how to use this option.
)");
ap.addOptionHelp("--update-from-json", "json", "update a PDF from qpdf JSON", R"(--update-from-json=qpdf-json-file
Update a PDF file from a JSON file. Please see the "QPDF JSON
Format" section of the manual for information about how to use
this option.
)");
}
static void add_help_8(QPDFArgParser& ap)
{
ap.addHelpTopic("testing", "options for testing or debugging", R"(The options below are useful when writing automated test code that ap.addHelpTopic("testing", "options for testing or debugging", R"(The options below are useful when writing automated test code that
includes files created by qpdf or when testing qpdf itself. includes files created by qpdf or when testing qpdf itself.
)"); )");
@ -843,9 +858,6 @@ ap.addOptionHelp("--static-id", "testing", "use a fixed document ID", R"(Use a f
testing only. Never use it for production files. See also testing only. Never use it for production files. See also
qpdf --help=--deterministic-id. qpdf --help=--deterministic-id.
)"); )");
}
static void add_help_8(QPDFArgParser& ap)
{
ap.addOptionHelp("--static-aes-iv", "testing", "use a fixed AES vector", R"(Use a static initialization vector for AES-CBC. This is intended ap.addOptionHelp("--static-aes-iv", "testing", "use a fixed AES vector", R"(Use a static initialization vector for AES-CBC. This is intended
for testing only so that output files can be reproducible. Never for testing only so that output files can be reproducible. Never
use it for production files. This option is not secure since it use it for production files. This option is not secure since it

View File

@ -104,6 +104,8 @@ this->ap.addRequiredParameter("rotate", [this](std::string const& x){c_main->rot
this->ap.addRequiredParameter("show-attachment", [this](std::string const& x){c_main->showAttachment(x);}, "attachment"); this->ap.addRequiredParameter("show-attachment", [this](std::string const& x){c_main->showAttachment(x);}, "attachment");
this->ap.addRequiredParameter("show-object", [this](std::string const& x){c_main->showObject(x);}, "trailer"); this->ap.addRequiredParameter("show-object", [this](std::string const& x){c_main->showObject(x);}, "trailer");
this->ap.addRequiredParameter("json-stream-prefix", [this](std::string const& x){c_main->jsonStreamPrefix(x);}, "stream-file-prefix"); this->ap.addRequiredParameter("json-stream-prefix", [this](std::string const& x){c_main->jsonStreamPrefix(x);}, "stream-file-prefix");
this->ap.addRequiredParameter("create-from-json", p(&ArgParser::argCreateFromJson), "qpdf-json file");
this->ap.addRequiredParameter("update-from-json", [this](std::string const& x){c_main->updateFromJson(x);}, "qpdf-json file");
this->ap.addOptionalParameter("collate", [this](std::string const& x){c_main->collate(x);}); this->ap.addOptionalParameter("collate", [this](std::string const& x){c_main->collate(x);});
this->ap.addOptionalParameter("split-pages", [this](std::string const& x){c_main->splitPages(x);}); this->ap.addOptionalParameter("split-pages", [this](std::string const& x){c_main->splitPages(x);});
this->ap.addChoices("compress-streams", [this](std::string const& x){c_main->compressStreams(x);}, true, yn_choices); this->ap.addChoices("compress-streams", [this](std::string const& x){c_main->compressStreams(x);}, true, yn_choices);

View File

@ -8,6 +8,7 @@
void setupInputFile(); void setupInputFile();
void setupPassword(); void setupPassword();
void setupEmpty(); void setupEmpty();
void setupCreateFromJson();
void setupOutputFile(); void setupOutputFile();
void setupReplaceInput(); void setupReplaceInput();
void beginEncrypt(JSON); void beginEncrypt(JSON);

View File

@ -30,6 +30,9 @@ popHandler(); // key: passwordFile
pushKey("empty"); pushKey("empty");
setupEmpty(); setupEmpty();
popHandler(); // key: empty popHandler(); // key: empty
pushKey("createFromJson");
setupCreateFromJson();
popHandler(); // key: createFromJson
pushKey("outputFile"); pushKey("outputFile");
setupOutputFile(); setupOutputFile();
popHandler(); // key: outputFile popHandler(); // key: outputFile
@ -262,6 +265,9 @@ popHandler(); // key: jsonStreamPrefix
pushKey("toJson"); pushKey("toJson");
addBare([this]() { c_main->toJson(); }); addBare([this]() { c_main->toJson(); });
popHandler(); // key: toJson popHandler(); // key: toJson
pushKey("updateFromJson");
addParameter([this](std::string const& p) { c_main->updateFromJson(p); });
popHandler(); // key: updateFromJson
pushKey("allowWeakCrypto"); pushKey("allowWeakCrypto");
addBare([this]() { c_main->allowWeakCrypto(); }); addBare([this]() { c_main->allowWeakCrypto(); });
popHandler(); // key: allowWeakCrypto popHandler(); // key: allowWeakCrypto

View File

@ -3,6 +3,7 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({
"password": "password for encrypted file", "password": "password for encrypted file",
"passwordFile": "read password from a file", "passwordFile": "read password from a file",
"empty": "use empty file as input", "empty": "use empty file as input",
"createFromJson": "create PDF from qpdf JSON",
"outputFile": "output filename", "outputFile": "output filename",
"replaceInput": "overwrite input with output", "replaceInput": "overwrite input with output",
"qdf": "enable viewing PDF code in a text editor", "qdf": "enable viewing PDF code in a text editor",
@ -87,6 +88,7 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({
"jsonStreamData": "how to handle streams in json output", "jsonStreamData": "how to handle streams in json output",
"jsonStreamPrefix": "prefix for json stream data files", "jsonStreamPrefix": "prefix for json stream data files",
"toJson": "serialize to JSON", "toJson": "serialize to JSON",
"updateFromJson": "update a PDF from qpdf JSON",
"allowWeakCrypto": "allow insecure cryptographic algorithms", "allowWeakCrypto": "allow insecure cryptographic algorithms",
"keepFilesOpen": "manage keeping multiple files open", "keepFilesOpen": "manage keeping multiple files open",
"keepFilesOpenThreshold": "set threshold for keepFilesOpen", "keepFilesOpenThreshold": "set threshold for keepFilesOpen",

View File

@ -3270,6 +3270,31 @@ Related Options
:samp:`-{nnn}` to create the name of the file that will contain the :samp:`-{nnn}` to create the name of the file that will contain the
data for the stream stream in object :samp:`{nnn}`. data for the stream stream in object :samp:`{nnn}`.
.. qpdf:option:: --create-from-json=qpdf-json-file
.. help: create PDF from qpdf JSON
Create a PDF file from the prior output of qpdf --json. See the
"QPDF JSON Format" section of the manual for information about
how to use this option.
This option creates a PDF file from the previous output of ``qpdf
--json`` that includes stream data and information about all
objects. For information about converting between PDF and JSON,
please see :ref:`qpdf-json`.
.. qpdf:option:: --update-from-json=qpdf-json-file
.. help: update a PDF from qpdf JSON
Update a PDF file from a JSON file. Please see the "QPDF JSON
Format" section of the manual for information about how to use
this option.
This option updates a PDF file from a qpdf JSON file. For a
information about how to use this option, please see
:ref:`qpdf-json`.
.. _test-options: .. _test-options:
Options for Testing or Debugging Options for Testing or Debugging

View File

@ -18,6 +18,13 @@ files programmatically from the command-line in languages that can't
call or link with the qpdf library directly. Note that stream data can call or link with the qpdf library directly. Note that stream data can
be extracted from PDF files using other qpdf command-line options. be extracted from PDF files using other qpdf command-line options.
.. _qpdf-json:
QPDF JSON Format
----------------
QXXXQ Write this.
.. _json-guarantees: .. _json-guarantees:
JSON Guarantees JSON Guarantees