From 69820847af93cce0e400638999fee4d2cbb68db6 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 30 Jul 2022 20:53:30 -0400 Subject: Change the output of --json to use "qpdf" instead of "objects" --- libqpdf/QPDFJob.cc | 80 +++++++++++++++++++++++++++----------- libqpdf/QPDFJob_config.cc | 7 ++-- libqpdf/QPDF_json.cc | 3 +- libqpdf/qpdf/auto_job_init.hh | 2 +- libqpdf/qpdf/auto_job_json_init.hh | 2 +- 5 files changed, 66 insertions(+), 28 deletions(-) (limited to 'libqpdf') diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index e09c0d74..e5914ddd 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -417,7 +417,7 @@ QPDFJob::Members::Members() : check_is_encrypted(false), check_requires_password(false), json_input(false), - json_output(0) + json_output(false) { } @@ -728,8 +728,15 @@ QPDFJob::checkConfiguration() " overwrite the input file"); } - if (m->json_keys.count("objectinfo")) { - usage("json key \"objectinfo\" is only valid for json version 1"); + if (m->json_version == 1) { + if (m->json_keys.count("qpdf")) { + usage("json key \"qpdf\" is only valid for json version > 1"); + } + } else { + if (m->json_keys.count("objectinfo") || m->json_keys.count("objects")) { + usage("json keys \"objects\" and \"objectinfo\" are only valid for" + " json version 1"); + } } } @@ -1568,12 +1575,12 @@ QPDFJob::json_schema(int json_version, std::set* keys) // The list of selectable top-level keys id duplicated in the // following places: job.yml, QPDFJob::json_schema, and // QPDFJob::doJSON. - if (all_keys || keys->count("objects")) { - schema.addDictionaryMember("objects", JSON::parse(R"({ + if (json_version == 1) { + if (all_keys || keys->count("objects")) { + schema.addDictionaryMember("objects", JSON::parse(R"({ "": "json representation of object" })")); - } - if (json_version == 1) { + } if (all_keys || keys->count("objectinfo")) { JSON objectinfo = schema.addDictionaryMember("objectinfo", JSON::parse(R"({ @@ -1586,6 +1593,19 @@ QPDFJob::json_schema(int json_version, std::set* keys) } })")); } + } else { + if (all_keys || keys->count("qpdf")) { + schema.addDictionaryMember("qpdf", JSON::parse(R"([{ + "jsonversion": "numeric JSON version", + "pdfversion": "PDF version as x.y", + "pushedinheritedpageresources": "whether inherited attributes were pushed to the page level", + "calledgetallpages": "whether getAllPages was called", + "maxobjectid": "highest object ID in output, ignored on input" +}, +{ + "": "json representation of object" +}])")); + } } if (all_keys || keys->count("pages")) { JSON page = schema.addDictionaryMember("pages", JSON::parse(R"([ @@ -1812,9 +1832,14 @@ QPDFJob::doJSON(QPDF& pdf, Pipeline* p) // We do objects last so their information is consistent with // repairing the page tree. To see the original file with any page // tree problems and the page tree not flattened, select - // objects/objectinfo without other keys. - if (all_keys || m->json_keys.count("objects")) { - doJSONObjects(p, first, pdf); + // 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); + } } if (this->m->json_version == 1) { // "objectinfo" is not needed for version >1 since you can @@ -2274,7 +2299,8 @@ QPDFJob::addAttachments(QPDF& pdf) } message = pdf.getFilename() + " already has attachments with the following keys: " + message + - "; use --replace to replace or --key to specify a different key"; + "; use --replace to replace or --key to specify a different " + "key"; throw std::runtime_error(message); } } @@ -2925,10 +2951,12 @@ QPDFJob::maybeFixWritePassword(int R, std::string& password) << this->m->message_prefix << ": WARNING: " << "supplied password looks like a Unicode" << " password with characters not allowed in" - << " passwords for 40-bit and 128-bit encryption;" + << " passwords for 40-bit and 128-bit " + "encryption;" << " most readers will not be able to open this" << " file with the supplied password." - << " (Use --password-mode=bytes to suppress this" + << " (Use --password-mode=bytes to suppress " + "this" << " warning and use the password anyway.)\n"; } } else if ((R >= 5) && (!is_valid_utf8)) { @@ -2978,13 +3006,15 @@ QPDFJob::setEncryptionOptions(QPDF& pdf, QPDFWriter& w) QTC::TC("qpdf", "QPDFJob weak crypto error"); *this->m->log->getError() << this->m->message_prefix - << ": refusing to write a file with RC4, a weak cryptographic " + << ": refusing to write a file with RC4, a weak " + "cryptographic " "algorithm\n" << "Please use 256-bit keys for better security.\n" << "Pass --allow-weak-crypto to enable writing insecure " "files.\n" << "See also " - "https://qpdf.readthedocs.io/en/stable/weak-crypto.html\n"; + "https://qpdf.readthedocs.io/en/stable/" + "weak-crypto.html\n"; throw std::runtime_error( "refusing to write a file with weak crypto"); } @@ -3293,7 +3323,8 @@ QPDFJob::writeOutfile(QPDF& pdf) m->outfilename = nullptr; } if (this->m->json_output) { - writeJSON(pdf); + bool unused = true; + writeJSON(nullptr, pdf, true, unused); } else { // QPDFWriter must have block scope so the output file will be // closed after write() finishes. @@ -3347,7 +3378,7 @@ QPDFJob::writeOutfile(QPDF& pdf) } void -QPDFJob::writeJSON(QPDF& pdf) +QPDFJob::writeJSON(Pipeline* p, QPDF& pdf, bool complete, bool& first_key) { // File pipeline must have block scope so it will be closed // after write. @@ -3369,7 +3400,12 @@ QPDFJob::writeJSON(QPDF& pdf) "name is unknown"); } else { QTC::TC("qpdf", "QPDFJob write json to stdout"); - fp = this->m->log->getInfo(); + if (p == nullptr) { + fp = this->m->log->getInfo(); + } + } + if (p == nullptr) { + p = fp.get(); } std::set json_objects; if (this->m->json_objects.count("trailer")) { @@ -3382,10 +3418,10 @@ QPDFJob::writeJSON(QPDF& pdf) json_objects.insert(s.str()); } pdf.writeJSON( - this->m->json_output, - fp.get(), - true, - true, + this->m->json_version, + p, + complete, + first_key, this->m->decode_level, this->m->json_stream_data, file_prefix, diff --git a/libqpdf/QPDFJob_config.cc b/libqpdf/QPDFJob_config.cc index 6a500b5c..9a2b3a84 100644 --- a/libqpdf/QPDFJob_config.cc +++ b/libqpdf/QPDFJob_config.cc @@ -296,12 +296,13 @@ QPDFJob::Config::jsonInput() QPDFJob::Config* QPDFJob::Config::jsonOutput(std::string const& parameter) { + o.m->json_output = true; if (parameter.empty() || (parameter == "latest")) { - o.m->json_output = JSON::LATEST; + o.m->json_version = JSON::LATEST; } else { - o.m->json_output = QUtil::string_to_int(parameter.c_str()); + o.m->json_version = QUtil::string_to_int(parameter.c_str()); } - if ((o.m->json_output < 2) || (o.m->json_output > JSON::LATEST)) { + if ((o.m->json_version < 2) || (o.m->json_version > JSON::LATEST)) { usage(std::string("unsupported json output version ") + parameter); } if (!o.m->json_stream_data_set) { diff --git a/libqpdf/QPDF_json.cc b/libqpdf/QPDF_json.cc index 9ecb1b31..f90096f5 100644 --- a/libqpdf/QPDF_json.cc +++ b/libqpdf/QPDF_json.cc @@ -802,7 +802,7 @@ QPDF::writeJSON( int version, Pipeline* p, bool complete, - bool first_key, + bool& first_key, qpdf_stream_decode_level_e decode_level, qpdf_json_stream_data_e json_stream_data, std::string const& file_prefix, @@ -892,4 +892,5 @@ QPDF::writeJSON( *p << "\n"; p->finish(); } + first_key = false; } diff --git a/libqpdf/qpdf/auto_job_init.hh b/libqpdf/qpdf/auto_job_init.hh index c76a0b6a..0c5302e1 100644 --- a/libqpdf/qpdf/auto_job_init.hh +++ b/libqpdf/qpdf/auto_job_init.hh @@ -19,7 +19,7 @@ static char const* decode_level_choices[] = {"none", "generalized", "specialized static char const* object_streams_choices[] = {"disable", "preserve", "generate", 0}; static char const* remove_unref_choices[] = {"auto", "yes", "no", 0}; static char const* flatten_choices[] = {"all", "print", "screen", 0}; -static char const* json_key_choices[] = {"acroform", "attachments", "encrypt", "objectinfo", "objects", "outlines", "pagelabels", "pages", 0}; +static char const* json_key_choices[] = {"acroform", "attachments", "encrypt", "objectinfo", "objects", "outlines", "pagelabels", "pages", "qpdf", 0}; static char const* json_output_choices[] = {"2", "latest", 0}; static char const* json_stream_data_choices[] = {"none", "inline", "file", 0}; static char const* json_version_choices[] = {"1", "2", "latest", 0}; diff --git a/libqpdf/qpdf/auto_job_json_init.hh b/libqpdf/qpdf/auto_job_json_init.hh index 168d5676..8f8fb987 100644 --- a/libqpdf/qpdf/auto_job_json_init.hh +++ b/libqpdf/qpdf/auto_job_json_init.hh @@ -12,7 +12,7 @@ static char const* decode_level_choices[] = {"none", "generalized", "specialized static char const* object_streams_choices[] = {"disable", "preserve", "generate", 0}; static char const* remove_unref_choices[] = {"auto", "yes", "no", 0}; static char const* flatten_choices[] = {"all", "print", "screen", 0}; -static char const* json_key_choices[] = {"acroform", "attachments", "encrypt", "objectinfo", "objects", "outlines", "pagelabels", "pages", 0}; +static char const* json_key_choices[] = {"acroform", "attachments", "encrypt", "objectinfo", "objects", "outlines", "pagelabels", "pages", "qpdf", 0}; static char const* json_output_choices[] = {"2", "latest", 0}; static char const* json_stream_data_choices[] = {"none", "inline", "file", 0}; static char const* json_version_choices[] = {"1", "2", "latest", 0}; -- cgit v1.2.3-54-g00ecf