diff options
author | Jay Berkenbilt <ejb@ql.org> | 2022-07-31 16:34:05 +0200 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2022-07-31 22:23:17 +0200 |
commit | 5f4224f31a500452a4f97f36ed57351b41ca0114 (patch) | |
tree | be355db5a4fcc8334410baf76c7eb1330578faa8 /libqpdf | |
parent | 80acfc3826704064db8cc2f6af0c338b3aa557e7 (diff) | |
download | qpdf-5f4224f31a500452a4f97f36ed57351b41ca0114.tar.zst |
Simplify --json-output
Now --json-output just changes defaults. Allow output file with --json.
Diffstat (limited to 'libqpdf')
-rw-r--r-- | libqpdf/QPDFJob.cc | 173 | ||||
-rw-r--r-- | libqpdf/QPDFJob_config.cc | 14 | ||||
-rw-r--r-- | libqpdf/qpdf/auto_job_help.hh | 22 | ||||
-rw-r--r-- | libqpdf/qpdf/auto_job_schema.hh | 2 |
4 files changed, 104 insertions, 107 deletions
diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index bc8f64f3..f1b35f56 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -680,8 +680,15 @@ QPDFJob::checkConfiguration() " an output file is specified"); } else if (m->split_pages) { usage("--split-pages may not be used with --replace-input"); + } else if (m->json_version) { + usage("--json may not be used with --replace-input"); } } + if (m->json_version && (m->outfilename == nullptr)) { + // The output file is optional with --json for backward + // compatibility and defaults to standard output. + m->outfilename = QUtil::make_shared_cstr("-"); + } if (m->infilename == nullptr) { usage("an input file name is required"); } else if ( @@ -1116,25 +1123,47 @@ QPDFJob::doJSONObject( void QPDFJob::doJSONObjects(Pipeline* p, bool& first, QPDF& pdf) { - JSON::writeDictionaryKey(p, first, "objects", 1); - bool first_object = true; - JSON::writeDictionaryOpen(p, first_object, 1); - bool all_objects = m->json_objects.empty(); - std::set<QPDFObjGen> wanted_og = getWantedJSONObjects(); - for (auto& obj: pdf.getAllObjects()) { - std::string key = obj.unparse(); - if (this->m->json_version > 1) { - key = "obj:" + key; + if (m->json_version == 1) { + JSON::writeDictionaryKey(p, first, "objects", 1); + bool first_object = true; + JSON::writeDictionaryOpen(p, first_object, 1); + bool all_objects = m->json_objects.empty(); + std::set<QPDFObjGen> wanted_og = getWantedJSONObjects(); + for (auto& obj: pdf.getAllObjects()) { + std::string key = obj.unparse(); + if (this->m->json_version > 1) { + key = "obj:" + key; + } + if (all_objects || wanted_og.count(obj.getObjGen())) { + doJSONObject(p, first_object, key, obj); + } } - if (all_objects || wanted_og.count(obj.getObjGen())) { - doJSONObject(p, first_object, key, obj); + if (all_objects || m->json_objects.count("trailer")) { + auto trailer = pdf.getTrailer(); + doJSONObject(p, first_object, "trailer", trailer); } + JSON::writeDictionaryClose(p, first_object, 1); + } else { + std::set<std::string> json_objects; + if (this->m->json_objects.count("trailer")) { + json_objects.insert("trailer"); + } + auto wanted = getWantedJSONObjects(); + for (auto const& og: wanted) { + std::ostringstream s; + s << "obj:" << og.unparse(' ') << " R"; + json_objects.insert(s.str()); + } + pdf.writeJSON( + this->m->json_version, + p, + false, + first, + this->m->decode_level, + this->m->json_stream_data, + this->m->json_stream_prefix, + json_objects); } - if (all_objects || m->json_objects.count("trailer")) { - auto trailer = pdf.getTrailer(); - doJSONObject(p, first_object, "trailer", trailer); - } - JSON::writeDictionaryClose(p, first_object, 1); } void @@ -1777,7 +1806,7 @@ void QPDFJob::doJSON(QPDF& pdf, Pipeline* p) { // qpdf guarantees that no new top-level keys whose names start - // with "xdata" will be added. These are reserved for users. + // with "x-" will be added. These are reserved for users. std::string captured_json; std::shared_ptr<Pl_String> pl_str; @@ -1788,32 +1817,38 @@ QPDFJob::doJSON(QPDF& pdf, Pipeline* p) bool first = true; JSON::writeDictionaryOpen(p, first, 0); - // This version is updated every time a non-backward-compatible - // change is made to the JSON format. Clients of the JSON are to - // ignore unrecognized keys, so we only update the version of a - // key disappears or if its value changes meaning. - JSON::writeDictionaryItem( - p, first, "version", JSON::makeInt(this->m->json_version), 1); - JSON j_params = JSON::makeDictionary(); - std::string decode_level_str; - switch (m->decode_level) { - case qpdf_dl_none: - decode_level_str = "none"; - break; - case qpdf_dl_generalized: - decode_level_str = "generalized"; - break; - case qpdf_dl_specialized: - decode_level_str = "specialized"; - break; - case qpdf_dl_all: - decode_level_str = "all"; - break; - } - j_params.addDictionaryMember( - "decodelevel", JSON::makeString(decode_level_str)); - JSON::writeDictionaryItem(p, first, "parameters", j_params, 1); + if (m->json_output) { + // Exclude version and parameters to keep the output file + // minimal. The JSON version is inside the "qpdf" key for + // version 2. + } else { + // This version is updated every time a non-backward-compatible + // change is made to the JSON format. Clients of the JSON are to + // ignore unrecognized keys, so we only update the version of a + // key disappears or if its value changes meaning. + JSON::writeDictionaryItem( + p, first, "version", JSON::makeInt(this->m->json_version), 1); + JSON j_params = JSON::makeDictionary(); + std::string decode_level_str; + switch (m->decode_level) { + case qpdf_dl_none: + decode_level_str = "none"; + break; + case qpdf_dl_generalized: + decode_level_str = "generalized"; + break; + case qpdf_dl_specialized: + decode_level_str = "specialized"; + break; + case qpdf_dl_all: + decode_level_str = "all"; + break; + } + j_params.addDictionaryMember( + "decodelevel", JSON::makeString(decode_level_str)); + JSON::writeDictionaryItem(p, first, "parameters", j_params, 1); + } bool all_keys = m->json_keys.empty(); // The list of selectable top-level keys id duplicated in the // following places: job.yml, QPDFJob::json_schema, and @@ -1850,11 +1885,7 @@ QPDFJob::doJSON(QPDF& pdf, Pipeline* p) // qpdf/objects/objectinfo without other keys. if (all_keys || m->json_keys.count("objects") || m->json_keys.count("qpdf")) { - if (this->m->json_version == 1) { - doJSONObjects(p, first, pdf); - } else { - writeJSON(p, pdf, false, first); - } + doJSONObjects(p, first, pdf); } if (this->m->json_version == 1) { // "objectinfo" is not needed for version >1 since you can @@ -1889,9 +1920,6 @@ QPDFJob::doInspection(QPDF& pdf) if (m->check) { doCheck(pdf); } - if (m->json_version) { - doJSON(pdf, &cout); - } if (m->show_npages) { QTC::TC("qpdf", "QPDFJob npages"); cout << pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue() @@ -3337,9 +3365,8 @@ QPDFJob::writeOutfile(QPDF& pdf) } else if (strcmp(m->outfilename.get(), "-") == 0) { m->outfilename = nullptr; } - if (this->m->json_output) { - bool unused = true; - writeJSON(nullptr, pdf, true, unused); + if (this->m->json_version) { + writeJSON(pdf); } else { // QPDFWriter must have block scope so the output file will be // closed after write() finishes. @@ -3393,52 +3420,30 @@ QPDFJob::writeOutfile(QPDF& pdf) } void -QPDFJob::writeJSON(Pipeline* p, QPDF& pdf, bool complete, bool& first_key) +QPDFJob::writeJSON(QPDF& pdf) { // File pipeline must have block scope so it will be closed // after write. std::shared_ptr<QUtil::FileCloser> fc; std::shared_ptr<Pipeline> fp; - std::string file_prefix = this->m->json_stream_prefix; if (m->outfilename.get()) { QTC::TC("qpdf", "QPDFJob write json to file"); - if (file_prefix.empty()) { - file_prefix = this->m->outfilename.get(); + if (this->m->json_stream_prefix.empty()) { + this->m->json_stream_prefix = this->m->outfilename.get(); } fc = std::make_shared<QUtil::FileCloser>( QUtil::safe_fopen(this->m->outfilename.get(), "w")); fp = std::make_shared<Pl_StdioFile>("json output", fc->f); } else if ( - (this->m->json_stream_data == qpdf_sj_file) && file_prefix.empty()) { + (this->m->json_stream_data == qpdf_sj_file) && + this->m->json_stream_prefix.empty()) { QTC::TC("qpdf", "QPDFJob need json-stream-prefix for stdout"); usage("please specify --json-stream-prefix since the input file " "name is unknown"); } else { QTC::TC("qpdf", "QPDFJob write json to stdout"); - if (p == nullptr) { - fp = this->m->log->getInfo(); - } - } - if (p == nullptr) { - p = fp.get(); - } - std::set<std::string> json_objects; - if (this->m->json_objects.count("trailer")) { - json_objects.insert("trailer"); - } - auto wanted = getWantedJSONObjects(); - for (auto const& og: wanted) { - std::ostringstream s; - s << "obj:" << og.unparse(' ') << " R"; - json_objects.insert(s.str()); - } - pdf.writeJSON( - this->m->json_version, - p, - complete, - first_key, - this->m->decode_level, - this->m->json_stream_data, - file_prefix, - json_objects); + this->m->log->saveToStandardOutput(true); + fp = this->m->log->getSave(); + } + doJSON(pdf, fp.get()); } diff --git a/libqpdf/QPDFJob_config.cc b/libqpdf/QPDFJob_config.cc index 9a2b3a84..8a9c1470 100644 --- a/libqpdf/QPDFJob_config.cc +++ b/libqpdf/QPDFJob_config.cc @@ -244,7 +244,6 @@ QPDFJob::Config::json(std::string const& parameter) if ((o.m->json_version < 1) || (o.m->json_version > JSON::LATEST)) { usage(std::string("unsupported json version ") + parameter); } - o.m->require_outfile = false; return this; } @@ -297,14 +296,7 @@ QPDFJob::Config* QPDFJob::Config::jsonOutput(std::string const& parameter) { o.m->json_output = true; - if (parameter.empty() || (parameter == "latest")) { - o.m->json_version = JSON::LATEST; - } else { - o.m->json_version = QUtil::string_to_int(parameter.c_str()); - } - if ((o.m->json_version < 2) || (o.m->json_version > JSON::LATEST)) { - usage(std::string("unsupported json output version ") + parameter); - } + json(parameter); if (!o.m->json_stream_data_set) { // No need to set json_stream_data_set -- that indicates // explicit use of --json-stream-data. @@ -313,9 +305,7 @@ QPDFJob::Config::jsonOutput(std::string const& parameter) if (!o.m->decode_level_set) { o.m->decode_level = qpdf_dl_none; } - if (o.m->json_keys.empty()) { - o.m->json_keys.insert("qpdf"); - } + o.m->json_keys.insert("qpdf"); return this; } diff --git a/libqpdf/qpdf/auto_job_help.hh b/libqpdf/qpdf/auto_job_help.hh index 6ade99f5..3551cf3d 100644 --- a/libqpdf/qpdf/auto_job_help.hh +++ b/libqpdf/qpdf/auto_job_help.hh @@ -803,7 +803,9 @@ depth in the JSON section of the manual. "version" may be a specific version or "latest" (the default). Run qpdf --json-help for a description of the generated JSON object. )"); -ap.addOptionHelp("--json-help", "json", "show format of JSON output", R"(Describe the format of the JSON output by writing to standard +ap.addOptionHelp("--json-help", "json", "show format of JSON output", R"(--json-help[=version] + +Describe the format of the JSON output by writing to standard output a JSON object with the same keys and with values containing descriptive text. )"); @@ -838,17 +840,17 @@ which is to use the output file name. Whatever is given here will be appended with -nnn to create the name of the file that will contain the data for the stream stream in object nnn. )"); -ap.addOptionHelp("--json-output", "json", "serialize to JSON", R"(--json-output[=version] +ap.addOptionHelp("--json-output", "json", "apply defaults for JSON serialization", R"(--json-output[=version] -The output file will be qpdf JSON format at the given version. -"version" may be a specific version or "latest" (the default). -The only supported version is 2. See also --json-stream-data, ---json-stream-prefix, and --decode-level. +Implies --json=version. Changes default values for certain +options so that the JSON output written is the most faithful +representation of the original PDF and contains no additional +JSON keys. See also --json-stream-data, --json-stream-prefix, +and --decode-level. )"); -ap.addOptionHelp("--json-input", "json", "input file is qpdf JSON", R"(Treat the input file as a JSON file in qpdf JSON format as -written by qpdf --json-output. See the "qpdf JSON Format" -section of the manual for information about how to use this -option. +ap.addOptionHelp("--json-input", "json", "input file is qpdf JSON", R"(Treat the input file as a JSON file in qpdf JSON format. 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 diff --git a/libqpdf/qpdf/auto_job_schema.hh b/libqpdf/qpdf/auto_job_schema.hh index 0fc187fe..aa69c192 100644 --- a/libqpdf/qpdf/auto_job_schema.hh +++ b/libqpdf/qpdf/auto_job_schema.hh @@ -28,7 +28,7 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({ "forceVersion": "set output PDF version", "progress": "show progress when writing", "splitPages": "write pages to separate files", - "jsonOutput": "serialize to JSON", + "jsonOutput": "apply defaults for JSON serialization", "encrypt": { "userPassword": "user password", "ownerPassword": "owner password", |