aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/qpdf/QPDFJob.hh5
-rw-r--r--job.sums10
-rw-r--r--job.yml3
-rw-r--r--libqpdf/QPDFJob_argv.cc120
-rw-r--r--libqpdf/qpdf/auto_job_decl.hh2
-rw-r--r--libqpdf/qpdf/auto_job_help.hh41
-rw-r--r--libqpdf/qpdf/auto_job_init.hh2
-rw-r--r--manual/cli.rst20
8 files changed, 181 insertions, 22 deletions
diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh
index de51dc57..a1c17435 100644
--- a/include/qpdf/QPDFJob.hh
+++ b/include/qpdf/QPDFJob.hh
@@ -35,6 +35,7 @@
#include <map>
#include <iostream>
#include <functional>
+#include <memory>
class QPDFWriter;
@@ -67,6 +68,10 @@ class QPDFJob
void initializeFromArgv(int argc, char* argv[],
char const* progname_env = nullptr);
+ // QXXXQ
+ QPDF_DLL
+ void initializeFromJson(std::string const& json);
+
// Set name that is used to prefix verbose messages, progress
// messages, and other things that the library writes to output
// and error streams on the caller's behalf. Defaults to "qpdf".
diff --git a/job.sums b/job.sums
index 07c08c43..8f8498d4 100644
--- a/job.sums
+++ b/job.sums
@@ -1,9 +1,9 @@
# Generated by generate_auto_job
generate_auto_job 0758b244fc4e2d3e440883072d2740bc4cdb26c5aa8de938f028afd7d83fad79
-job.yml 2856c2635d42f0a58717d3ffce3125816d8f98ff17245c4b7a0669d70cd68b84
-libqpdf/qpdf/auto_job_decl.hh 97395ecbe590b23ae04d6cce2080dbd0e998917ff5eeaa5c6aafa91041d3cd6a
-libqpdf/qpdf/auto_job_help.hh 2653faaf59415bec81c3a85d426239d52b609ac24faba34ec2d26f00710dd2c6
-libqpdf/qpdf/auto_job_init.hh 465bf46769559ceb77110d1b9d3293ba9b3595850b49848c31aeabd10aadb4ad
+job.yml 78d3b655abe70c0baaa31e51b74931f97084632bca5961fdbae89d7a57f34a67
+libqpdf/qpdf/auto_job_decl.hh 9fda0ebd93bce6e308a3f26181293ad7b0d88a3503d4955cbf8e1db9a884d8ee
+libqpdf/qpdf/auto_job_help.hh 383eea80e2c185ef5295fc126246457a7ceeffea759fdb90bb2e6727532ea538
+libqpdf/qpdf/auto_job_init.hh a16e89fc7be3ca200d47391d949628a92113533135758068b944b64d0d54793d
libqpdf/qpdf/auto_job_schema.hh c91a4e182e088797b70dda94af03ca32d360f3564890132da2a8bdc3c4432423
manual/_ext/qpdf.py 855fe12de5af7a10bb24be6ecc4d5dff4c84ac58cf388a13be6bbb394346a67d
-manual/cli.rst b136c7f33a538c580b081a7e802c27635aad2a4229efa0eb0736466116b7aa90
+manual/cli.rst 68122ff8179c10df3fe6d577adde4973c346f7866ba9a511bab5a6e6f292a6f1
diff --git a/job.yml b/job.yml
index dec43e90..105a33c6 100644
--- a/job.yml
+++ b/job.yml
@@ -58,6 +58,7 @@ options:
- copyright
- json-help
- show-crypto
+ - job-json-help
- table: main
positional: true
bare:
@@ -120,6 +121,7 @@ options:
encryption-file-password: password
force-version: version
ii-min-bytes: minimum
+ job-json-file: file
json-object: trailer
keep-files-open-threshold: count
linearize-pass1: filename
@@ -219,6 +221,7 @@ options:
password: password
no-json:
- preserve-unreferenced-resources
+ - job-json-file
json:
# The structure of this section defines what the json input to
# QPDFJob looks like. If a key starts with underscore or has a value
diff --git a/libqpdf/QPDFJob_argv.cc b/libqpdf/QPDFJob_argv.cc
index 8da5e836..f4a4219a 100644
--- a/libqpdf/QPDFJob_argv.cc
+++ b/libqpdf/QPDFJob_argv.cc
@@ -8,6 +8,7 @@
#include <cstdio>
#include <ctype.h>
#include <memory>
+#include <sstream>
#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>
@@ -15,6 +16,10 @@
#include <qpdf/QPDFArgParser.hh>
#include <qpdf/QPDFJob.hh>
#include <qpdf/QIntC.hh>
+#include <qpdf/JSONHandler.hh>
+
+#include <qpdf/auto_job_schema.hh>
+static JSON JOB_SCHEMA = JSON::parse(JOB_SCHEMA_DATA);
namespace
{
@@ -1329,6 +1334,31 @@ ArgParser::argEndCopyAttachment()
}
void
+ArgParser::argJobJsonFile(char* parameter)
+{
+ PointerHolder<char> file_buf;
+ size_t size;
+ QUtil::read_file_into_memory(parameter, file_buf, size);
+ try
+ {
+ o.initializeFromJson(std::string(file_buf.getPointer(), size));
+ }
+ catch (std::exception& e)
+ {
+ throw std::runtime_error(
+ "error with job-json file " + std::string(parameter) + " " +
+ e.what() + "\nRun " + this->ap.getProgname() +
+ "--job-json-help for information on the file format.");
+ }
+}
+
+void
+ArgParser::argJobJsonHelp()
+{
+ std::cout << JOB_SCHEMA_DATA << std::endl;
+}
+
+void
ArgParser::usage(std::string const& message)
{
this->ap.usage(message);
@@ -1534,3 +1564,93 @@ QPDFJob::initializeFromArgv(int argc, char* argv[], char const* progname_env)
ArgParser ap(qap, *this);
ap.parseOptions();
}
+
+void
+QPDFJob::initializeFromJson(std::string const& json)
+{
+ std::list<std::string> errors;
+ JSON j = JSON::parse(json);
+ if (! j.checkSchema(JOB_SCHEMA, JSON::f_optional, errors))
+ {
+ std::ostringstream msg;
+ msg << this->m->message_prefix
+ << ": job json has errors:";
+ for (auto const& error: errors)
+ {
+ msg << std::endl << " " << error;
+ }
+ throw std::runtime_error(msg.str());
+ }
+
+ JSONHandler jh;
+ {
+ jh.addDictHandlers(
+ [](std::string const&){},
+ [](std::string const&){});
+
+ auto input = std::make_shared<JSONHandler>();
+ auto input_file = std::make_shared<JSONHandler>();
+ auto input_file_name = std::make_shared<JSONHandler>();
+ auto output = std::make_shared<JSONHandler>();
+ auto output_file = std::make_shared<JSONHandler>();
+ auto output_file_name = std::make_shared<JSONHandler>();
+ auto output_options = std::make_shared<JSONHandler>();
+ auto output_options_qdf = std::make_shared<JSONHandler>();
+
+ input->addDictHandlers(
+ [](std::string const&){},
+ [](std::string const&){});
+ input_file->addDictHandlers(
+ [](std::string const&){},
+ [](std::string const&){});
+ output->addDictHandlers(
+ [](std::string const&){},
+ [](std::string const&){});
+ output_file->addDictHandlers(
+ [](std::string const&){},
+ [](std::string const&){});
+ output_options->addDictHandlers(
+ [](std::string const&){},
+ [](std::string const&){});
+
+ jh.addDictKeyHandler("input", input);
+ input->addDictKeyHandler("file", input_file);
+ input_file->addDictKeyHandler("name", input_file_name);
+ jh.addDictKeyHandler("output", output);
+ output->addDictKeyHandler("file", output_file);
+ output_file->addDictKeyHandler("name", output_file_name);
+ output->addDictKeyHandler("options", output_options);
+ output_options->addDictKeyHandler("qdf", output_options_qdf);
+
+ input_file_name->addStringHandler(
+ [this](std::string const&, std::string const& v) {
+ this->infilename = QUtil::make_shared_cstr(v);
+ });
+ output_file_name->addStringHandler(
+ [this](std::string const&, std::string const& v) {
+ this->outfilename = QUtil::make_shared_cstr(v);
+ });
+ output_options_qdf->addBoolHandler(
+ [this](std::string const&, bool v) {
+ this->qdf_mode = v;
+ });
+ }
+
+ // {
+ // "input": {
+ // "file": {
+ // "name": "/home/ejb/source/examples/pdf/minimal.pdf"
+ // }
+ // },
+ // "output": {
+ // "file": {
+ // "name": "/tmp/a.pdf"
+ // },
+ // "options": {
+ // "qdf": true
+ // }
+ // }
+ // }
+
+ jh.handle(".", j);
+}
diff --git a/libqpdf/qpdf/auto_job_decl.hh b/libqpdf/qpdf/auto_job_decl.hh
index 1f2b4263..6d5c8228 100644
--- a/libqpdf/qpdf/auto_job_decl.hh
+++ b/libqpdf/qpdf/auto_job_decl.hh
@@ -16,6 +16,7 @@ void argVersion();
void argCopyright();
void argJsonHelp();
void argShowCrypto();
+void argJobJsonHelp();
void argPositional(char*);
void argAddAttachment();
void argAllowWeakCrypto();
@@ -74,6 +75,7 @@ void argCopyEncryption(char *);
void argEncryptionFilePassword(char *);
void argForceVersion(char *);
void argIiMinBytes(char *);
+void argJobJsonFile(char *);
void argJsonObject(char *);
void argKeepFilesOpenThreshold(char *);
void argLinearizePass1(char *);
diff --git a/libqpdf/qpdf/auto_job_help.hh b/libqpdf/qpdf/auto_job_help.hh
index dd55fb0c..982509ee 100644
--- a/libqpdf/qpdf/auto_job_help.hh
+++ b/libqpdf/qpdf/auto_job_help.hh
@@ -27,6 +27,11 @@ with --pages.
)");
ap.addOptionHelp("--replace-input", "usage", "replace input with output", R"(Use in place of outfile to overwrite the input file with the output.
)");
+ap.addOptionHelp("--job-json-file", "usage", "job JSON file", R"(--job-json-file=file
+
+Specify the name of a file whose contents are expected to
+contain a QPDFJob json file.
+)");
ap.addHelpTopic("exit-status", "meanings of qpdf's exit codes", R"(Meaning of exit codes:
0: no errors or warnings
@@ -78,14 +83,14 @@ performed.
)");
ap.addOptionHelp("--progress", "general", "show progress when writing", R"(Indicate progress when writing files.
)");
+}
+static void add_help_2(QPDFArgParser& ap)
+{
ap.addOptionHelp("--no-warn", "general", "suppress printing warning messages", R"(Suppress printing warning messages. If warnings were
encountered, qpdf still exits with exit status 3.
Use --warning-exit-0 with --no-warn to completely ignore
warnings.
)");
-}
-static void add_help_2(QPDFArgParser& ap)
-{
ap.addOptionHelp("--deterministic-id", "general", "generate ID deterministically", R"(Generate a secure, random document ID only using static
information, such as the page contents. Does not use the file's
name or attributes or the current time.
@@ -164,15 +169,15 @@ chapter about it in the manual.
ap.addOptionHelp("--no-original-object-ids", "transformation", "omit original object ID in qdf", R"(Omit comments in a QDF file indicating the object ID an object
had in the original file.
)");
+}
+static void add_help_3(QPDFArgParser& ap)
+{
ap.addOptionHelp("--compress-streams", "transformation", "compress uncompressed streams", R"(--compress-streams=[yn]
Setting --compress-streams=n prevents qpdf from compressing
uncompressed streams. This can be useful if you are leaving some
streams uncompressed intentionally.
)");
-}
-static void add_help_3(QPDFArgParser& ap)
-{
ap.addOptionHelp("--decode-level", "transformation", "control which streams to uncompress", R"(--decode-level=parameter
When uncompressing streams, control which types of compression
@@ -280,15 +285,15 @@ ap.addOptionHelp("--pages", "modification", "begin page selection", R"(--pages f
Run qpdf --help=page-selection for details.
)");
+}
+static void add_help_4(QPDFArgParser& ap)
+{
ap.addOptionHelp("--collate", "modification", "collate with --pages", R"(--collate=n
Collate rather than concatenate pages specified with --pages.
With a numeric parameter, collate in groups of n. The default
is 1. Run qpdf --help=page-selection for additional details.
)");
-}
-static void add_help_4(QPDFArgParser& ap)
-{
ap.addOptionHelp("--split-pages", "modification", "write pages to separate files", R"(--split-pages=[n]
This option causes qpdf to create separate output files for each
@@ -451,15 +456,15 @@ ap.addOptionHelp("--extract", "encryption", "restrict text/graphic extraction",
Enable/disable text/graphic extraction for purposes other than
accessibility.
)");
+}
+static void add_help_5(QPDFArgParser& ap)
+{
ap.addOptionHelp("--form", "encryption", "restrict form filling", R"(--form=[yn]
Enable/disable whether filling form fields is allowed even if
modification of annotations is disabled. This option is not
available with 40-bit encryption.
)");
-}
-static void add_help_5(QPDFArgParser& ap)
-{
ap.addOptionHelp("--modify-other", "encryption", "restrict other modifications", R"(--modify-other=[yn]
Enable/disable modifications not controlled by --assemble,
@@ -625,15 +630,15 @@ ap.addOptionHelp("--remove-attachment", "attachments", "remove an embedded file"
Remove an embedded file using its key. Get the key with
--list-attachments.
)");
+}
+static void add_help_6(QPDFArgParser& ap)
+{
ap.addOptionHelp("--copy-attachments-from", "attachments", "start copy attachment options", R"(--copy-attachments-from file options --
The --copy-attachments-from flag and its options may be repeated
to copy attachments from multiple files. Run
qpdf --help=copy-attachments for details.
)");
-}
-static void add_help_6(QPDFArgParser& ap)
-{
ap.addHelpTopic("pdf-dates", "PDF date format", R"(When a date is required, the date should conform to the PDF date
format specification, which is "D:yyyymmddhhmmssz" where "z" is
either literally upper case "Z" for UTC or a timezone offset in
@@ -738,11 +743,11 @@ encryption key to be displayed.
ap.addOptionHelp("--check-linearization", "inspection", "check linearization tables", R"(Check to see whether a file is linearized and, if so, whether
the linearization hint tables are correct.
)");
-ap.addOptionHelp("--show-linearization", "inspection", "show linearization hint tables", R"(Check and display all data in the linearization hint tables.
-)");
}
static void add_help_7(QPDFArgParser& ap)
{
+ap.addOptionHelp("--show-linearization", "inspection", "show linearization hint tables", R"(Check and display all data in the linearization hint tables.
+)");
ap.addOptionHelp("--show-xref", "inspection", "show cross reference data", R"(Show the contents of the cross-reference table or stream (object
locations in the file) in a human-readable form. This is
especially useful for files with cross-reference streams, which
@@ -793,6 +798,8 @@ This option is repeatable. If given, only specified objects will
be shown in the "objects" key of the JSON output. Otherwise, all
objects will be shown.
)");
+ap.addOptionHelp("--job-json-help", "json", "show format of job json", R"(Describe the format of the QPDFJob JSON input.
+)");
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.
)");
diff --git a/libqpdf/qpdf/auto_job_init.hh b/libqpdf/qpdf/auto_job_init.hh
index 3d7cdd7b..f9de9f90 100644
--- a/libqpdf/qpdf/auto_job_init.hh
+++ b/libqpdf/qpdf/auto_job_init.hh
@@ -26,6 +26,7 @@ this->ap.addBare("version", b(&ArgParser::argVersion));
this->ap.addBare("copyright", b(&ArgParser::argCopyright));
this->ap.addBare("json-help", b(&ArgParser::argJsonHelp));
this->ap.addBare("show-crypto", b(&ArgParser::argShowCrypto));
+this->ap.addBare("job-json-help", b(&ArgParser::argJobJsonHelp));
this->ap.selectMainOptionTable();
this->ap.addPositional(p(&ArgParser::argPositional));
this->ap.addBare("add-attachment", b(&ArgParser::argAddAttachment));
@@ -85,6 +86,7 @@ this->ap.addRequiredParameter("copy-encryption", p(&ArgParser::argCopyEncryption
this->ap.addRequiredParameter("encryption-file-password", p(&ArgParser::argEncryptionFilePassword), "password");
this->ap.addRequiredParameter("force-version", p(&ArgParser::argForceVersion), "version");
this->ap.addRequiredParameter("ii-min-bytes", p(&ArgParser::argIiMinBytes), "minimum");
+this->ap.addRequiredParameter("job-json-file", p(&ArgParser::argJobJsonFile), "file");
this->ap.addRequiredParameter("json-object", p(&ArgParser::argJsonObject), "trailer");
this->ap.addRequiredParameter("keep-files-open-threshold", p(&ArgParser::argKeepFilesOpenThreshold), "count");
this->ap.addRequiredParameter("linearize-pass1", p(&ArgParser::argLinearizePass1), "filename");
diff --git a/manual/cli.rst b/manual/cli.rst
index 0238ba16..9b8a04d6 100644
--- a/manual/cli.rst
+++ b/manual/cli.rst
@@ -157,6 +157,18 @@ Related Options
:file:`{infilename}.~qpdf-orig`. If there are errors, the input
file is left untouched.
+.. qpdf:option:: --job-json-file=file
+
+ .. help: job JSON file
+
+ Specify the name of a file whose contents are expected to
+ contain a QPDFJob json file.
+
+ Specify the name of a file whose contents are expected to contain a
+ QPDFJob json file. QXXXQ ref. This file is read and treated as if
+ the equivalent command-line arguments were supplied. It can be
+ mixed freely with other options.
+
.. _exit-status:
Exit Status
@@ -3147,6 +3159,14 @@ Related Options
be shown in the "``objects``" key of the JSON output. Otherwise, all
objects will be shown.
+.. qpdf:option:: --job-json-help
+
+ .. help: show format of job json
+
+ Describe the format of the QPDFJob JSON input.
+
+ Describe the format of the QPDFJob JSON input. QXXXQ doc ref.
+
.. _test-options:
Options for Testing or Debugging