aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/qpdf/QPDFJob.hh25
-rw-r--r--libqpdf/QPDFJob.cc447
-rw-r--r--qpdf/qpdf.cc19
-rw-r--r--qpdf/qtest/qpdf/append-page-content-damaged-check.out1
-rw-r--r--qpdf/qtest/qpdf/bad-jpeg-check.out1
-rw-r--r--qpdf/qtest/qpdf/bad-xref-entry-corrected.out1
-rw-r--r--qpdf/qtest/qpdf/content-stream-errors.out1
-rw-r--r--qpdf/qtest/qpdf/damaged-stream.out1
-rw-r--r--qpdf/qtest/qpdf/eof-reading-token.out1
-rw-r--r--qpdf/qtest/qpdf/indirect-r-arg.out1
-rw-r--r--qpdf/qtest/qpdf/invalid-id-xref.out1
-rw-r--r--qpdf/qtest/qpdf/linearization-bounds-1.out1
-rw-r--r--qpdf/qtest/qpdf/linearization-bounds-2.out1
-rw-r--r--qpdf/qtest/qpdf/linearization-large-vector-alloc.out1
-rw-r--r--qpdf/qtest/qpdf/name-pound-images.out1
-rw-r--r--qpdf/qtest/qpdf/no-pages-types.out1
-rw-r--r--qpdf/qtest/qpdf/obj0-check.out1
-rw-r--r--qpdf/qtest/qpdf/pages-loop.out1
-rw-r--r--qpdf/qtest/qpdf/show-unfilterable.out3
-rw-r--r--qpdf/qtest/qpdf/split-content-stream-errors.out1
-rw-r--r--qpdf/qtest/qpdf/xref-errors.out1
-rw-r--r--qpdf/qtest/qpdf/zero-offset.out1
22 files changed, 288 insertions, 224 deletions
diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh
index 03b6aa21..89be7c2c 100644
--- a/include/qpdf/QPDFJob.hh
+++ b/include/qpdf/QPDFJob.hh
@@ -33,6 +33,7 @@
#include <set>
#include <map>
#include <iostream>
+#include <functional>
class QPDFWriter;
@@ -42,9 +43,23 @@ class QPDFJob
QPDF_DLL
QPDFJob();
+ // 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".
+ QPDF_DLL
+ void setMessagePrefix(std::string const&);
+
+ // Override streams that errors and output go to. Defaults are
+ // std::cout and std::cerr.
QPDF_DLL
void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
+ // If in verbose mode, call the given function, passing in the
+ // output stream and message prefix.
+ QPDF_DLL
+ void doIfVerbose(
+ std::function<void(std::ostream&, std::string const& prefix)> fn);
+
QPDF_DLL
void run();
@@ -63,14 +78,13 @@ class QPDFJob
QPDF_DLL
bool checkIsEncrypted();
-
// Return value is bitwise OR of values from qpdf_encryption_status_e
QPDF_DLL
unsigned long getEncryptionStatus();
- // QXXXQ From here to END-PUBLIC should all be private
+ // QXXXQ From here to END-PUBLIC should all be private or
+ // different somehow
public:
-
QPDF_DLL
static JSON json_schema(std::set<std::string>* keys = 0);
QPDF_DLL
@@ -165,6 +179,10 @@ class QPDFJob
void setWriterOptions(QPDF& pdf, QPDFWriter& w);
void doSplitPages(QPDF& pdf, bool& warnings);
void writeOutfile(QPDF& pdf);
+ void doJSON(QPDF& pdf);
+ void doInspection(QPDF& pdf);
+ void showEncryption(QPDF& pdf);
+ void doCheck(QPDF& pdf);
enum remove_unref_e { re_auto, re_yes, re_no };
@@ -295,6 +313,7 @@ class QPDFJob
Members();
Members(Members const&) = delete;
+ std::string whoami;
bool warnings;
bool creates_output;
std::ostream* out_stream;
diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc
index e5121bdd..b3379812 100644
--- a/libqpdf/QPDFJob.cc
+++ b/libqpdf/QPDFJob.cc
@@ -35,8 +35,6 @@
#include <qpdf/QIntC.hh>
// QXXXQ temporary for compilation
-static int constexpr EXIT_ERROR = 2;
-static int EXIT_WARNING = 3; // may be changed to 0 at runtime
static char const* whoami = "qpdf";
// /QXXXQ
@@ -82,7 +80,8 @@ namespace
class ProgressReporter: public QPDFWriter::ProgressReporter
{
public:
- ProgressReporter(char const* filename) :
+ ProgressReporter(std::string const& whoami, char const* filename) :
+ whoami(whoami),
filename(filename)
{
}
@@ -92,6 +91,7 @@ namespace
virtual void reportProgress(int);
private:
+ std::string whoami;
std::string filename;
};
}
@@ -133,6 +133,7 @@ ProgressReporter::reportProgress(int percentage)
QPDFJob::Members::Members() :
+ whoami("qpdf"),
warnings(false),
creates_output(false),
out_stream(&std::cout),
@@ -245,12 +246,28 @@ QPDFJob::QPDFJob() :
}
void
+QPDFJob::setMessagePrefix(std::string const& whoami)
+{
+ this->m->whoami = whoami;
+}
+
+void
QPDFJob::setOutputStreams(std::ostream* out, std::ostream* err)
{
this->m->out_stream = out ? out : &std::cout;
this->m->err_stream = err ? err : &std::cerr;
}
+void
+QPDFJob::doIfVerbose(
+ std::function<void(std::ostream&, std::string const& prefix)> fn)
+{
+ if (this->verbose && (this->m->out_stream != nullptr))
+ {
+ fn(*(this->m->out_stream), this->m->whoami);
+ }
+}
+
static void parse_version(std::string const& full_version_string,
std::string& version, int& extension_level)
{
@@ -316,8 +333,10 @@ static std::string show_encryption_method(QPDF::encryption_method_e method)
return result;
}
-static void show_encryption(QPDF& pdf, QPDFJob& o)
+void
+QPDFJob::showEncryption(QPDF& pdf)
{
+ QPDFJob& o = *this; // QXXXQ
// Extract /P from /Encrypt
int R = 0;
int P = 0;
@@ -380,8 +399,10 @@ static void show_encryption(QPDF& pdf, QPDFJob& o)
}
}
-static void do_check(QPDF& pdf, QPDFJob& o, int& exit_code)
+void
+QPDFJob::doCheck(QPDF& pdf)
{
+ QPDFJob& o = *this; // QXXXQ
// Code below may set okay to false but not to true.
// We assume okay until we prove otherwise but may
// continue to perform additional checks after finding
@@ -399,7 +420,7 @@ static void do_check(QPDF& pdf, QPDFJob& o, int& exit_code)
<< pdf.getExtensionLevel();
}
std::cout << std::endl;
- show_encryption(pdf, o);
+ showEncryption(pdf);
if (pdf.isLinearized())
{
std::cout << "File is linearized\n";
@@ -443,8 +464,9 @@ static void do_check(QPDF& pdf, QPDFJob& o, int& exit_code)
catch (QPDFExc& e)
{
okay = false;
- std::cerr << "ERROR: page " << pageno << ": "
- << e.what() << std::endl;
+ *(this->m->err_stream)
+ << "ERROR: page " << pageno << ": "
+ << e.what() << std::endl;
}
}
}
@@ -453,28 +475,27 @@ static void do_check(QPDF& pdf, QPDFJob& o, int& exit_code)
std::cerr << "ERROR: " << e.what() << std::endl;
okay = false;
}
- if (okay)
+ if (! okay)
{
- if ((! pdf.getWarnings().empty()) || warnings)
- {
- exit_code = EXIT_WARNING;
- }
- else
- {
- std::cout << "No syntax or stream encoding errors"
- << " found; the file may still contain"
- << std::endl
- << "errors that qpdf cannot detect"
- << std::endl;
- }
+ throw std::runtime_error("errors detected");
+ }
+
+ if ((! pdf.getWarnings().empty()) || warnings)
+ {
+ this->m->warnings = TRUE;
}
else
{
- exit_code = EXIT_ERROR;
+ *(this->m->out_stream)
+ << "No syntax or stream encoding errors"
+ << " found; the file may still contain"
+ << std::endl
+ << "errors that qpdf cannot detect"
+ << std::endl;
}
}
-static void do_show_obj(QPDF& pdf, QPDFJob& o, int& exit_code)
+static void do_show_obj(QPDF& pdf, QPDFJob& o)
{
QPDFObjectHandle obj;
if (o.show_trailer)
@@ -485,6 +506,7 @@ static void do_show_obj(QPDF& pdf, QPDFJob& o, int& exit_code)
{
obj = pdf.getObjectByID(o.show_obj, o.show_gen);
}
+ bool error = false;
if (obj.isStream())
{
if (o.show_raw_stream_data || o.show_filtered_stream_data)
@@ -494,9 +516,8 @@ static void do_show_obj(QPDF& pdf, QPDFJob& o, int& exit_code)
(! obj.pipeStreamData(0, 0, qpdf_dl_all)))
{
QTC::TC("qpdf", "qpdf unable to filter");
- std::cerr << "Unable to filter stream data."
- << std::endl;
- exit_code = EXIT_ERROR;
+ obj.warnIfPossible("unable to filter stream data");
+ error = true;
}
else
{
@@ -519,6 +540,11 @@ static void do_show_obj(QPDF& pdf, QPDFJob& o, int& exit_code)
{
std::cout << obj.unparseResolved() << std::endl;
}
+ if (error)
+ {
+ throw std::runtime_error(
+ "unable to get object " + obj.getObjGen().unparse());
+ }
}
static void do_show_pages(QPDF& pdf, QPDFJob& o)
@@ -581,29 +607,28 @@ static void do_list_attachments(QPDF& pdf, QPDFJob& o)
std::cout << key << " -> "
<< efoh->getEmbeddedFileStream().getObjGen()
<< std::endl;
- if (o.verbose)
- {
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
auto desc = efoh->getDescription();
if (! desc.empty())
{
- std::cout << " description: " << desc << std::endl;
+ cout << " description: " << desc << std::endl;
}
- std::cout << " preferred name: " << efoh->getFilename()
+ cout << " preferred name: " << efoh->getFilename()
<< std::endl;
- std::cout << " all names:" << std::endl;
+ cout << " all names:" << std::endl;
for (auto const& i2: efoh->getFilenames())
{
- std::cout << " " << i2.first << " -> " << i2.second
+ cout << " " << i2.first << " -> " << i2.second
<< std::endl;
}
- std::cout << " all data streams:" << std::endl;
+ cout << " all data streams:" << std::endl;
for (auto i2: efoh->getEmbeddedFileStreams().ditems())
{
- std::cout << " " << i2.first << " -> "
+ cout << " " << i2.first << " -> "
<< i2.second.getObjGen()
<< std::endl;
}
- }
+ });
}
}
else
@@ -612,16 +637,14 @@ static void do_list_attachments(QPDF& pdf, QPDFJob& o)
}
}
-static void do_show_attachment(QPDF& pdf, QPDFJob& o, int& exit_code)
+static void do_show_attachment(QPDF& pdf, QPDFJob& o)
{
QPDFEmbeddedFileDocumentHelper efdh(pdf);
auto fs = efdh.getEmbeddedFile(o.attachment_to_show);
if (! fs)
{
- std::cerr << whoami << ": attachment " << o.attachment_to_show
- << " not found" << std::endl;
- exit_code = EXIT_ERROR;
- return;
+ throw std::runtime_error(
+ "attachment " + o.attachment_to_show + " not found");
}
auto efs = fs->getEmbeddedFileStream();
QUtil::binary_stdout();
@@ -1149,8 +1172,9 @@ QPDFJob::json_schema(std::set<std::string>* keys)
bool all_keys = ((keys == 0) || keys->empty());
+ // QXXXQ
// The list of selectable top-level keys id duplicated in three
- // places: json_schema, do_json, and initOptionTable.
+ // places: json_schema, doJSON, and initOptionTable.
if (all_keys || keys->count("objects"))
{
schema.addDictionaryMember(
@@ -1456,8 +1480,10 @@ QPDFJob::json_schema(std::set<std::string>* keys)
return schema;
}
-static void do_json(QPDF& pdf, QPDFJob& o)
+void
+QPDFJob::doJSON(QPDF& pdf)
{
+ QPDFJob& o = *this; // QXXXQ
JSON j = JSON::makeDictionary();
// This version is updated every time a non-backward-compatible
// change is made to the JSON format. Clients of the JSON are to
@@ -1486,8 +1512,9 @@ static void do_json(QPDF& pdf, QPDFJob& o)
"decodelevel", JSON::makeString(decode_level_str));
bool all_keys = o.json_keys.empty();
+ // QXXXQ
// The list of selectable top-level keys id duplicated in three
- // places: json_schema, do_json, and initOptionTable.
+ // places: json_schema, doJSON, and initOptionTable.
if (all_keys || o.json_keys.count("objects"))
{
do_json_objects(pdf, o, j);
@@ -1527,9 +1554,9 @@ static void do_json(QPDF& pdf, QPDFJob& o)
std::list<std::string> errors;
if (! j.checkSchema(schema, errors))
{
- std::cerr
- << whoami << " didn't create JSON that complies with its own\n\
-rules. Please report this as a bug at\n\
+ *(this->m->err_stream)
+ << "QPDFJob didn't create JSON that complies with its own rules.\n\
+Please report this as a bug at\n\
https://github.com/qpdf/qpdf/issues/new\n\
ideally with the file that caused the error and the output below. Thanks!\n\
\n";
@@ -1543,16 +1570,17 @@ ideally with the file that caused the error and the output below. Thanks!\n\
std::cout << j.unparse() << std::endl;
}
-static void do_inspection(QPDF& pdf, QPDFJob& o)
+void
+QPDFJob::doInspection(QPDF& pdf)
{
- int exit_code = 0;
+ QPDFJob& o = *this; // QXXXQ
if (o.check)
{
- do_check(pdf, o, exit_code);
+ doCheck(pdf);
}
if (o.json)
{
- do_json(pdf, o);
+ doJSON(pdf);
}
if (o.show_npages)
{
@@ -1562,7 +1590,7 @@ static void do_inspection(QPDF& pdf, QPDFJob& o)
}
if (o.show_encryption)
{
- show_encryption(pdf, o);
+ showEncryption(pdf);
}
if (o.check_linearization)
{
@@ -1571,9 +1599,9 @@ static void do_inspection(QPDF& pdf, QPDFJob& o)
std::cout << o.infilename << ": no linearization errors"
<< std::endl;
}
- else if (exit_code != EXIT_ERROR)
+ else
{
- exit_code = EXIT_WARNING;
+ this->m->warnings = true;
}
}
if (o.show_linearization)
@@ -1594,7 +1622,7 @@ static void do_inspection(QPDF& pdf, QPDFJob& o)
}
if ((o.show_obj > 0) || o.show_trailer)
{
- do_show_obj(pdf, o, exit_code);
+ do_show_obj(pdf, o);
}
if (o.show_pages)
{
@@ -1606,17 +1634,11 @@ static void do_inspection(QPDF& pdf, QPDFJob& o)
}
if (! o.attachment_to_show.empty())
{
- do_show_attachment(pdf, o, exit_code);
- }
- if ((! pdf.getWarnings().empty()) && (exit_code != EXIT_ERROR))
- {
- std::cerr << whoami
- << ": operation succeeded with warnings" << std::endl;
- exit_code = EXIT_WARNING;
+ do_show_attachment(pdf, o);
}
- if (exit_code)
+ if (! pdf.getWarnings().empty())
{
- exit(exit_code); // QXXXQ
+ this->m->warnings = true;
}
}
@@ -1637,11 +1659,13 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next)
QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace");
if (! (w_obj.isNumber() && h_obj.isNumber()))
{
- if (o.verbose && (! description.empty()))
+ if (! description.empty())
{
- std::cout << whoami << ": " << description
- << ": not optimizing because image dictionary"
- << " is missing required keys" << std::endl;
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": not optimizing because image dictionary"
+ << " is missing required keys" << std::endl;
+ });
}
return result;
}
@@ -1649,11 +1673,13 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next)
if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8)))
{
QTC::TC("qpdf", "qpdf image optimize bits per component");
- if (o.verbose && (! description.empty()))
+ if (! description.empty())
{
- std::cout << whoami << ": " << description
- << ": not optimizing because image has other than"
- << " 8 bits per component" << std::endl;
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": not optimizing because image has other than"
+ << " 8 bits per component" << std::endl;
+ });
}
return result;
}
@@ -1700,11 +1726,13 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next)
else
{
QTC::TC("qpdf", "qpdf image optimize colorspace");
- if (o.verbose && (! description.empty()))
+ if (! description.empty())
{
- std::cout << whoami << ": " << description
- << ": not optimizing because qpdf can't optimize"
- << " images with this colorspace" << std::endl;
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": not optimizing because qpdf can't optimize"
+ << " images with this colorspace" << std::endl;
+ });
}
return result;
}
@@ -1713,12 +1741,14 @@ ImageOptimizer::makePipeline(std::string const& description, Pipeline* next)
((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area)))
{
QTC::TC("qpdf", "qpdf image optimize too small");
- if (o.verbose && (! description.empty()))
+ if (! description.empty())
{
- std::cout << whoami << ": " << description
- << ": not optimizing because image"
- << " is smaller than requested minimum dimensions"
- << std::endl;
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": not optimizing because image"
+ << " is smaller than requested minimum dimensions"
+ << std::endl;
+ });
}
return result;
}
@@ -1733,13 +1763,12 @@ ImageOptimizer::evaluate(std::string const& description)
if (! image.pipeStreamData(0, 0, qpdf_dl_specialized, true))
{
QTC::TC("qpdf", "qpdf image optimize no pipeline");
- if (o.verbose)
- {
- std::cout << whoami << ": " << description
- << ": not optimizing because unable to decode data"
- << " or data already uses DCT"
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": not optimizing because unable to decode data"
+ << " or data already uses DCT"
+ << std::endl;
+ });
return false;
}
Pl_Discard d;
@@ -1758,21 +1787,19 @@ ImageOptimizer::evaluate(std::string const& description)
if (c.getCount() >= orig_length)
{
QTC::TC("qpdf", "qpdf image optimize no shrink");
- if (o.verbose)
- {
- std::cout << whoami << ": " << description
- << ": not optimizing because DCT compression does not"
- << " reduce image size" << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": not optimizing because DCT compression does not"
+ << " reduce image size" << std::endl;
+ });
return false;
}
- if (o.verbose)
- {
- std::cout << whoami << ": " << description
- << ": optimizing image reduces size from "
- << orig_length << " to " << c.getCount()
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << description
+ << ": optimizing image reduces size from "
+ << orig_length << " to " << c.getCount()
+ << std::endl;
+ });
return true;
}
@@ -1889,13 +1916,15 @@ static PointerHolder<QPDF> do_process(
throw e;
}
}
- if ((! warned) && o.verbose)
+ if (! warned)
{
warned = true;
- std::cout << whoami << ": supplied password didn't work;"
- << " trying other passwords based on interpreting"
- << " password with different string encodings"
- << std::endl;
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": supplied password didn't work;"
+ << " trying other passwords based on interpreting"
+ << " password with different string encodings"
+ << std::endl;
+ });
}
}
// Should not be reachable
@@ -2032,10 +2061,9 @@ static void do_under_overlay_for_page(
iter != pagenos[pageno].end(); ++iter)
{
int from_pageno = *iter;
- if (o.verbose)
- {
- std::cout << " " << uo.which << " " << from_pageno << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << " " << uo.which << " " << from_pageno << std::endl;
+ });
auto from_page = pages.at(QIntC::to_size(from_pageno - 1));
if (0 == fo.count(from_pageno))
{
@@ -2116,16 +2144,14 @@ QPDFJob::handleUnderOverlay(QPDF& pdf)
QPDFPageDocumentHelper main_pdh(pdf);
std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages();
size_t main_npages = main_pages.size();
- if (o.verbose)
- {
- std::cout << whoami << ": processing underlay/overlay" << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": processing underlay/overlay" << std::endl;
+ });
for (size_t i = 0; i < main_npages; ++i)
{
- if (o.verbose)
- {
- std::cout << " page " << 1+i << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << " page " << 1+i << std::endl;
+ });
do_under_overlay_for_page(pdf, o, o.underlay, underlay_pagenos, i,
underlay_fo, upages, main_pages.at(i),
true);
@@ -2174,12 +2200,11 @@ QPDFJob::addAttachments(QPDF& pdf)
}
efdh.replaceEmbeddedFile(to_add.key, fs);
- if (o.verbose)
- {
- std::cout << whoami << ": attached " << to_add.path
- << " as " << to_add.filename
- << " with key " << to_add.key << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": attached " << to_add.path
+ << " as " << to_add.filename
+ << " with key " << to_add.key << std::endl;
+ });
}
if (! duplicated_keys.empty())
@@ -2210,11 +2235,10 @@ QPDFJob::copyAttachments(QPDF& pdf)
std::vector<std::string> duplicates;
for (auto const& to_copy: o.attachments_to_copy)
{
- if (o.verbose)
- {
- std::cout << whoami << ": copying attachments from "
- << to_copy.path << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": copying attachments from "
+ << to_copy.path << std::endl;
+ });
auto other = processFile(
to_copy.path.c_str(), to_copy.password.c_str());
QPDFEmbeddedFileDocumentHelper other_efdh(*other);
@@ -2233,11 +2257,11 @@ QPDFJob::copyAttachments(QPDF& pdf)
iter.second->getObjectHandle());
efdh.replaceEmbeddedFile(
new_key, QPDFFileSpecObjectHelper(new_fs_oh));
- if (o.verbose)
- {
- std::cout << " " << iter.first << " -> " << new_key
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout,
+ std::string const& whoami) {
+ cout << " " << iter.first << " -> " << new_key
+ << std::endl;
+ });
}
}
@@ -2361,11 +2385,11 @@ QPDFJob::handleTransformations(QPDF& pdf)
{
if (efdh.removeEmbeddedFile(key))
{
- if (o.verbose)
- {
- std::cout << whoami <<
+ o.doIfVerbose([&](std::ostream& cout,
+ std::string const& whoami) {
+ cout << whoami <<
": removed attachment " << key << std::endl;
- }
+ });
}
else
{
@@ -2407,11 +2431,10 @@ static bool should_remove_unreferenced_resources(QPDF& pdf, QPDFJob& o)
std::set<QPDFObjGen> resources_seen; // shared resources detection
std::set<QPDFObjGen> nodes_seen; // loop detection
- if (o.verbose)
- {
- std::cout << whoami << ": " << pdf.getFilename()
- << ": checking for shared resources" << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": " << pdf.getFilename()
+ << ": checking for shared resources" << std::endl;
+ });
std::list<QPDFObjectHandle> queue;
queue.push_back(pdf.getRoot().getKey("/Pages"));
@@ -2433,12 +2456,12 @@ static bool should_remove_unreferenced_resources(QPDF& pdf, QPDFJob& o)
if (dict.hasKey("/Resources"))
{
QTC::TC("qpdf", "qpdf found resources in non-leaf");
- if (o.verbose)
- {
- std::cout << " found resources in non-leaf page node "
- << og.getObj() << " " << og.getGen()
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout,
+ std::string const& whoami) {
+ cout << " found resources in non-leaf page node "
+ << og.getObj() << " " << og.getGen()
+ << std::endl;
+ });
return true;
}
int n = kids.getArrayNItems();
@@ -2457,15 +2480,15 @@ static bool should_remove_unreferenced_resources(QPDF& pdf, QPDFJob& o)
if (resources_seen.count(resources_og))
{
QTC::TC("qpdf", "qpdf found shared resources in leaf");
- if (o.verbose)
- {
- std::cout << " found shared resources in leaf node "
- << og.getObj() << " " << og.getGen()
- << ": "
- << resources_og.getObj() << " "
- << resources_og.getGen()
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout,
+ std::string const& whoami) {
+ cout << " found shared resources in leaf node "
+ << og.getObj() << " " << og.getGen()
+ << ": "
+ << resources_og.getObj() << " "
+ << resources_og.getGen()
+ << std::endl;
+ });
return true;
}
resources_seen.insert(resources_og);
@@ -2479,15 +2502,15 @@ static bool should_remove_unreferenced_resources(QPDF& pdf, QPDFJob& o)
if (resources_seen.count(xobject_og))
{
QTC::TC("qpdf", "qpdf found shared xobject in leaf");
- if (o.verbose)
- {
- std::cout << " found shared xobject in leaf node "
- << og.getObj() << " " << og.getGen()
- << ": "
- << xobject_og.getObj() << " "
- << xobject_og.getGen()
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout,
+ std::string const& whoami) {
+ cout << " found shared xobject in leaf node "
+ << og.getObj() << " " << og.getGen()
+ << ": "
+ << xobject_og.getObj() << " "
+ << xobject_og.getGen()
+ << std::endl;
+ });
return true;
}
resources_seen.insert(xobject_og);
@@ -2512,10 +2535,9 @@ static bool should_remove_unreferenced_resources(QPDF& pdf, QPDFJob& o)
}
}
- if (o.verbose)
- {
- std::cout << whoami << ": no shared resources found" << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": no shared resources found" << std::endl;
+ });
return false;
}
@@ -2570,20 +2592,18 @@ static void handle_page_specs(
if (filenames.size() > o.keep_files_open_threshold)
{
QTC::TC("qpdf", "qpdf disable keep files open");
- if (o.verbose)
- {
- std::cout << whoami << ": selecting --keep-open-files=n"
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": selecting --keep-open-files=n"
+ << std::endl;
+ });
o.keep_files_open = false;
}
else
{
- if (o.verbose)
- {
- std::cout << whoami << ": selecting --keep-open-files=y"
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": selecting --keep-open-files=y"
+ << std::endl;
+ });
o.keep_files_open = true;
QTC::TC("qpdf", "qpdf don't disable keep files open");
}
@@ -2618,11 +2638,10 @@ static void handle_page_specs(
QTC::TC("qpdf", "qpdf pages encryption password");
password = o.encryption_file_password;
}
- if (o.verbose)
- {
- std::cout << whoami << ": processing "
- << page_spec.filename << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": processing "
+ << page_spec.filename << std::endl;
+ });
PointerHolder<InputSource> is;
ClosedFileInputSource* cis = 0;
if (! o.keep_files_open)
@@ -2690,12 +2709,11 @@ static void handle_page_specs(
// without changing their object numbers. This enables other
// things in the original file, such as outlines, to continue to
// work.
- if (o.verbose)
- {
- std::cout << whoami
- << ": removing unreferenced pages from primary input"
- << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami
+ << ": removing unreferenced pages from primary input"
+ << std::endl;
+ });
QPDFPageDocumentHelper dh(pdf);
std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages();
for (std::vector<QPDFPageObjectHelper>::iterator iter =
@@ -2765,11 +2783,10 @@ static void handle_page_specs(
{
any_page_labels = true;
}
- if (o.verbose)
- {
- std::cout << whoami << ": adding pages from "
- << page_data.filename << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": adding pages from "
+ << page_data.filename << std::endl;
+ });
for (std::vector<int>::iterator pageno_iter =
page_data.selected_pages.begin();
pageno_iter != page_data.selected_pages.end();
@@ -3002,15 +3019,15 @@ static void maybe_fix_write_password(int R, QPDFJob& o, std::string& password)
if (QUtil::utf8_to_pdf_doc(password, encoded))
{
QTC::TC("qpdf", "qpdf auto-encode password");
- if (o.verbose)
- {
- std::cout
+ o.doIfVerbose([&](std::ostream& cout,
+ std::string const& whoami) {
+ cout
<< whoami
<< ": automatically converting Unicode"
<< " password to single-byte encoding as"
<< " required for 40-bit or 128-bit"
<< " encryption" << std::endl;
- }
+ });
password = encoded;
}
else
@@ -3088,9 +3105,8 @@ static void set_encryption_options(QPDF& pdf, QPDFJob& o, QPDFWriter& w)
{
if (! o.allow_weak_crypto)
{
- // Do not set exit code to EXIT_WARNING for this case as
- // this does not reflect a potential problem with the
- // input file.
+ // Do not set warnings = true for this case as this does
+ // not reflect a potential problem with the input file.
QTC::TC("qpdf", "qpdf weak crypto warning");
std::cerr
<< whoami
@@ -3247,7 +3263,8 @@ QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w)
}
if (o.progress && o.outfilename)
{
- w.registerProgressReporter(new ProgressReporter(o.outfilename));
+ w.registerProgressReporter(
+ new ProgressReporter(this->m->whoami, o.outfilename));
}
}
@@ -3355,18 +3372,15 @@ QPDFJob::doSplitPages(QPDF& pdf, bool& warnings)
std::string outfile = before + page_range + after;
if (QUtil::same_file(o.infilename, outfile.c_str()))
{
- std::cerr << whoami
- << ": split pages would overwrite input file with "
- << outfile << std::endl;
- exit(EXIT_ERROR); // QXXXQ
+ throw std::runtime_error(
+ "split pages would overwrite input file with " + outfile);
}
QPDFWriter w(outpdf, outfile.c_str());
setWriterOptions(outpdf, w);
w.write();
- if (o.verbose)
- {
- std::cout << whoami << ": wrote file " << outfile << std::endl;
- }
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": wrote file " << outfile << std::endl;
+ });
if (outpdf.anyWarnings())
{
warnings = true;
@@ -3399,10 +3413,11 @@ QPDFJob::writeOutfile(QPDF& pdf)
setWriterOptions(pdf, w);
w.write();
}
- if (o.verbose && o.outfilename)
+ if (o.outfilename)
{
- std::cout << whoami << ": wrote file "
- << o.outfilename << std::endl;
+ o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
+ cout << whoami << ": wrote file " << o.outfilename << std::endl;
+ });
}
if (o.replace_input)
{
@@ -3495,7 +3510,7 @@ QPDFJob::run()
this->m->creates_output = ((o.outfilename != nullptr) || o.replace_input);
if (! this->m->creates_output)
{
- do_inspection(pdf, o);
+ doInspection(pdf);
}
else if (o.split_pages)
{
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index b3170b05..a7613af9 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -314,6 +314,7 @@ ArgParser::initOptionTable()
this->ap.addBare("show-pages", b(&ArgParser::argShowPages));
this->ap.addBare("with-images", b(&ArgParser::argWithImages));
this->ap.addBare("json", b(&ArgParser::argJson));
+ // QXXXQ
// The list of selectable top-level keys id duplicated in three
// places: json_schema, do_json, and initOptionTable.
char const* json_key_choices[] = {
@@ -2350,6 +2351,7 @@ ArgParser::doFinalChecks()
}
if (o.optimize_images && (! o.keep_inline_images))
{
+ // QXXXQ this is not a check and doesn't belong here
o.externalize_inline_images = true;
}
if (o.check_requires_password && o.check_is_encrypted)
@@ -2384,7 +2386,7 @@ ArgParser::doFinalChecks()
usage("--split-pages may not be used when"
" writing to standard output");
}
- if (o.verbose)
+ if (o.verbose) // QXXXQ
{
usage("--verbose may not be used when"
" writing to standard output");
@@ -2418,6 +2420,7 @@ int realmain(int argc, char* argv[])
// ArgParser must stay in scope for the duration of qpdf's run as
// it holds dynamic memory used for argv.
QPDFJob j;
+ j.setMessagePrefix(whoami);
ArgParser ap(argc, argv, j);
bool errors = false;
@@ -2439,9 +2442,17 @@ int realmain(int argc, char* argv[])
{
if (! j.suppressWarnings())
{
- std::cerr << whoami << ": operation succeeded with warnings;"
- << " resulting file may have some problems"
- << std::endl;
+ if (j.createsOutput())
+ {
+ std::cerr << whoami << ": operation succeeded with warnings;"
+ << " resulting file may have some problems"
+ << std::endl;
+ }
+ else
+ {
+ std::cerr << whoami << ": operation succeeded with warnings"
+ << std::endl;
+ }
}
// Still return with warning code even if warnings were
// suppressed, so leave warnings == true.
diff --git a/qpdf/qtest/qpdf/append-page-content-damaged-check.out b/qpdf/qtest/qpdf/append-page-content-damaged-check.out
index b529bc35..14457be1 100644
--- a/qpdf/qtest/qpdf/append-page-content-damaged-check.out
+++ b/qpdf/qtest/qpdf/append-page-content-damaged-check.out
@@ -5,3 +5,4 @@ checking append-page-content-damaged.pdf
PDF Version: 1.3
File is not encrypted
File is not linearized
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/bad-jpeg-check.out b/qpdf/qtest/qpdf/bad-jpeg-check.out
index 425c495e..555a6e25 100644
--- a/qpdf/qtest/qpdf/bad-jpeg-check.out
+++ b/qpdf/qtest/qpdf/bad-jpeg-check.out
@@ -4,3 +4,4 @@ File is not encrypted
File is not linearized
WARNING: bad-jpeg.pdf (offset 735): error decoding stream data for object 6 0: Not a JPEG file: starts with 0x77 0x77
WARNING: bad-jpeg.pdf (offset 735): stream will be re-processed without filtering to avoid data loss
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/bad-xref-entry-corrected.out b/qpdf/qtest/qpdf/bad-xref-entry-corrected.out
index 0fbac406..db11ab28 100644
--- a/qpdf/qtest/qpdf/bad-xref-entry-corrected.out
+++ b/qpdf/qtest/qpdf/bad-xref-entry-corrected.out
@@ -12,3 +12,4 @@ WARNING: bad-xref-entry.pdf: Attempting to reconstruct cross-reference table
5/0: uncompressed; offset = 583
6/0: uncompressed; offset = 629
7/0: uncompressed; offset = 774
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/content-stream-errors.out b/qpdf/qtest/qpdf/content-stream-errors.out
index 07a2b81a..5c51b8c2 100644
--- a/qpdf/qtest/qpdf/content-stream-errors.out
+++ b/qpdf/qtest/qpdf/content-stream-errors.out
@@ -5,3 +5,4 @@ File is not linearized
WARNING: page object 3 0 stream 7 0 (content, offset 52): parse error while reading object
WARNING: page object 5 0 stream 15 0 (stream data, offset 117): EOF found while reading inline image
WARNING: page object 6 0 stream 19 0 (content, offset 53): parse error while reading object
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/damaged-stream.out b/qpdf/qtest/qpdf/damaged-stream.out
index 3ef6df62..fe2e5818 100644
--- a/qpdf/qtest/qpdf/damaged-stream.out
+++ b/qpdf/qtest/qpdf/damaged-stream.out
@@ -4,3 +4,4 @@ File is not encrypted
File is not linearized
WARNING: damaged-stream.pdf (offset 426): error decoding stream data for object 5 0: LZWDecoder: bad code received
WARNING: damaged-stream.pdf (offset 426): stream will be re-processed without filtering to avoid data loss
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/eof-reading-token.out b/qpdf/qtest/qpdf/eof-reading-token.out
index 596907c7..111965ec 100644
--- a/qpdf/qtest/qpdf/eof-reading-token.out
+++ b/qpdf/qtest/qpdf/eof-reading-token.out
@@ -3,3 +3,4 @@ PDF Version: 1.3
File is not encrypted
File is not linearized
WARNING: eof-reading-token.pdf object stream 12 (object 13 0, offset 5): EOF while reading token
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/indirect-r-arg.out b/qpdf/qtest/qpdf/indirect-r-arg.out
index 0b8be9e3..ab912e1d 100644
--- a/qpdf/qtest/qpdf/indirect-r-arg.out
+++ b/qpdf/qtest/qpdf/indirect-r-arg.out
@@ -5,3 +5,4 @@ WARNING: indirect-r-arg.pdf (object 1 0, offset 62): expected dictionary key but
PDF Version: 1.3
File is not encrypted
File is not linearized
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/invalid-id-xref.out b/qpdf/qtest/qpdf/invalid-id-xref.out
index 209d5826..c8e7eefc 100644
--- a/qpdf/qtest/qpdf/invalid-id-xref.out
+++ b/qpdf/qtest/qpdf/invalid-id-xref.out
@@ -15,3 +15,4 @@ modify annotations: allowed
modify other: not allowed
modify anything: not allowed
File is not linearized
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/linearization-bounds-1.out b/qpdf/qtest/qpdf/linearization-bounds-1.out
index 3e28d28a..ff81a903 100644
--- a/qpdf/qtest/qpdf/linearization-bounds-1.out
+++ b/qpdf/qtest/qpdf/linearization-bounds-1.out
@@ -6,3 +6,4 @@ WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, off
WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
WARNING: error encountered while checking linearization data: linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/linearization-bounds-2.out b/qpdf/qtest/qpdf/linearization-bounds-2.out
index a558dd42..b7386ab3 100644
--- a/qpdf/qtest/qpdf/linearization-bounds-2.out
+++ b/qpdf/qtest/qpdf/linearization-bounds-2.out
@@ -6,3 +6,4 @@ WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, off
WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
WARNING: error encountered while checking linearization data: linearization-bounds-2.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/linearization-large-vector-alloc.out b/qpdf/qtest/qpdf/linearization-large-vector-alloc.out
index a30f3e06..66ee998c 100644
--- a/qpdf/qtest/qpdf/linearization-large-vector-alloc.out
+++ b/qpdf/qtest/qpdf/linearization-large-vector-alloc.out
@@ -6,3 +6,4 @@ WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object
WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
WARNING: error encountered while checking linearization data: overflow reading bit stream: wanted = 12556; available = 968
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/name-pound-images.out b/qpdf/qtest/qpdf/name-pound-images.out
index 4ceadc35..065e02a7 100644
--- a/qpdf/qtest/qpdf/name-pound-images.out
+++ b/qpdf/qtest/qpdf/name-pound-images.out
@@ -6,3 +6,4 @@ WARNING: name-pound-images.pdf (object 3 0, offset 471): name with stray # will
WARNING: name-pound-images.pdf (object 3 0, offset 508): name with stray # will not work with PDF >= 1.2
WARNING: page object 3 0 stream 4 0 (content, offset 59): name with stray # will not work with PDF >= 1.2
WARNING: page object 3 0 stream 4 0 (content, offset 131): name with stray # will not work with PDF >= 1.2
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/no-pages-types.out b/qpdf/qtest/qpdf/no-pages-types.out
index 28c172a7..1221b68f 100644
--- a/qpdf/qtest/qpdf/no-pages-types.out
+++ b/qpdf/qtest/qpdf/no-pages-types.out
@@ -4,3 +4,4 @@ File is not encrypted
File is not linearized
WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Page but is not; overriding
WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Pages but is not; overriding
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/obj0-check.out b/qpdf/qtest/qpdf/obj0-check.out
index 64cb8d15..d4a33eb6 100644
--- a/qpdf/qtest/qpdf/obj0-check.out
+++ b/qpdf/qtest/qpdf/obj0-check.out
@@ -5,3 +5,4 @@ WARNING: obj0.pdf: Attempting to reconstruct cross-reference table
PDF Version: 1.3
File is not encrypted
File is not linearized
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/pages-loop.out b/qpdf/qtest/qpdf/pages-loop.out
index a84ecb36..dddfc649 100644
--- a/qpdf/qtest/qpdf/pages-loop.out
+++ b/qpdf/qtest/qpdf/pages-loop.out
@@ -3,3 +3,4 @@ PDF Version: 1.3
File is not encrypted
File is not linearized
ERROR: pages-loop.pdf (object 3 0): Loop detected in /Pages structure (getAllPages)
+qpdf: errors detected
diff --git a/qpdf/qtest/qpdf/show-unfilterable.out b/qpdf/qtest/qpdf/show-unfilterable.out
index 464ca2c3..ba802d0d 100644
--- a/qpdf/qtest/qpdf/show-unfilterable.out
+++ b/qpdf/qtest/qpdf/show-unfilterable.out
@@ -1 +1,2 @@
-Unable to filter stream data.
+WARNING: unfilterable.pdf, stream object 4 0: unable to filter stream data
+qpdf: unable to get object 4,0
diff --git a/qpdf/qtest/qpdf/split-content-stream-errors.out b/qpdf/qtest/qpdf/split-content-stream-errors.out
index de5fee75..1eebc495 100644
--- a/qpdf/qtest/qpdf/split-content-stream-errors.out
+++ b/qpdf/qtest/qpdf/split-content-stream-errors.out
@@ -7,3 +7,4 @@ WARNING: split-content-stream-errors.pdf (offset 557): stream will be re-process
WARNING: page object 3 0 (item index 0 (from 0)): ignoring non-stream in an array of streams
WARNING: split-content-stream-errors.pdf (offset 557): error decoding stream data for object 6 0: LZWDecoder: bad code received
ERROR: page 1: content stream (content stream object 6 0): errors while decoding content stream
+qpdf: errors detected
diff --git a/qpdf/qtest/qpdf/xref-errors.out b/qpdf/qtest/qpdf/xref-errors.out
index 27c97736..66420c35 100644
--- a/qpdf/qtest/qpdf/xref-errors.out
+++ b/qpdf/qtest/qpdf/xref-errors.out
@@ -13,3 +13,4 @@ File is not linearized
4/0: uncompressed; offset = 307
5/0: uncompressed; offset = 403
6/0: uncompressed; offset = 438
+qpdf: operation succeeded with warnings
diff --git a/qpdf/qtest/qpdf/zero-offset.out b/qpdf/qtest/qpdf/zero-offset.out
index f789b165..67c99733 100644
--- a/qpdf/qtest/qpdf/zero-offset.out
+++ b/qpdf/qtest/qpdf/zero-offset.out
@@ -3,3 +3,4 @@ PDF Version: 1.3
File is not encrypted
File is not linearized
WARNING: zero-offset.pdf (object 6 0): object has offset 0
+qpdf: operation succeeded with warnings