aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2022-07-31 16:34:05 +0200
committerJay Berkenbilt <ejb@ql.org>2022-07-31 22:23:17 +0200
commit5f4224f31a500452a4f97f36ed57351b41ca0114 (patch)
treebe355db5a4fcc8334410baf76c7eb1330578faa8 /libqpdf
parent80acfc3826704064db8cc2f6af0c338b3aa557e7 (diff)
downloadqpdf-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.cc173
-rw-r--r--libqpdf/QPDFJob_config.cc14
-rw-r--r--libqpdf/qpdf/auto_job_help.hh22
-rw-r--r--libqpdf/qpdf/auto_job_schema.hh2
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",