From 1d099ab7439104759a421bc20c809e64fef29f03 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 22 Jan 2022 16:29:13 -0500 Subject: [PATCH] QPDFJob: placeholder for initializeFromJson --- include/qpdf/QPDFJob.hh | 5 ++ job.sums | 10 +-- job.yml | 3 + libqpdf/QPDFJob_argv.cc | 120 ++++++++++++++++++++++++++++++++++ libqpdf/qpdf/auto_job_decl.hh | 2 + libqpdf/qpdf/auto_job_help.hh | 41 +++++++----- libqpdf/qpdf/auto_job_init.hh | 2 + manual/cli.rst | 20 ++++++ 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 #include #include +#include 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 #include #include +#include #include #include @@ -15,6 +16,10 @@ #include #include #include +#include + +#include +static JSON JOB_SCHEMA = JSON::parse(JOB_SCHEMA_DATA); namespace { @@ -1328,6 +1333,31 @@ ArgParser::argEndCopyAttachment() } } +void +ArgParser::argJobJsonFile(char* parameter) +{ + PointerHolder 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) { @@ -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 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(); + auto input_file = std::make_shared(); + auto input_file_name = std::make_shared(); + auto output = std::make_shared(); + auto output_file = std::make_shared(); + auto output_file_name = std::make_shared(); + auto output_options = std::make_shared(); + auto output_options_qdf = std::make_shared(); + + 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