Commit 1ddf5b4b4bbc3c00c51aba51ff67be47736f6e88

Authored by Jay Berkenbilt
1 parent 0910e767

QPDFJob increment: get rid of exit, handle verbose

Remove all calls to exit() from QPDFJob. Handle code that runs in
verbose mode to enable it to make use of output streams and message
prefix (whoami) from QPDFJob. This removes temporarily duplicated exit
code logic and most access to whoami/std::cout outside of QPDFJob
proper.
include/qpdf/QPDFJob.hh
@@ -33,6 +33,7 @@ @@ -33,6 +33,7 @@
33 #include <set> 33 #include <set>
34 #include <map> 34 #include <map>
35 #include <iostream> 35 #include <iostream>
  36 +#include <functional>
36 37
37 class QPDFWriter; 38 class QPDFWriter;
38 39
@@ -42,9 +43,23 @@ class QPDFJob @@ -42,9 +43,23 @@ class QPDFJob
42 QPDF_DLL 43 QPDF_DLL
43 QPDFJob(); 44 QPDFJob();
44 45
  46 + // Set name that is used to prefix verbose messages, progress
  47 + // messages, and other things that the library writes to output
  48 + // and error streams on the caller's behalf. Defaults to "qpdf".
  49 + QPDF_DLL
  50 + void setMessagePrefix(std::string const&);
  51 +
  52 + // Override streams that errors and output go to. Defaults are
  53 + // std::cout and std::cerr.
45 QPDF_DLL 54 QPDF_DLL
46 void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream); 55 void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
47 56
  57 + // If in verbose mode, call the given function, passing in the
  58 + // output stream and message prefix.
  59 + QPDF_DLL
  60 + void doIfVerbose(
  61 + std::function<void(std::ostream&, std::string const& prefix)> fn);
  62 +
48 QPDF_DLL 63 QPDF_DLL
49 void run(); 64 void run();
50 65
@@ -63,14 +78,13 @@ class QPDFJob @@ -63,14 +78,13 @@ class QPDFJob
63 QPDF_DLL 78 QPDF_DLL
64 bool checkIsEncrypted(); 79 bool checkIsEncrypted();
65 80
66 -  
67 // Return value is bitwise OR of values from qpdf_encryption_status_e 81 // Return value is bitwise OR of values from qpdf_encryption_status_e
68 QPDF_DLL 82 QPDF_DLL
69 unsigned long getEncryptionStatus(); 83 unsigned long getEncryptionStatus();
70 84
71 - // QXXXQ From here to END-PUBLIC should all be private 85 + // QXXXQ From here to END-PUBLIC should all be private or
  86 + // different somehow
72 public: 87 public:
73 -  
74 QPDF_DLL 88 QPDF_DLL
75 static JSON json_schema(std::set<std::string>* keys = 0); 89 static JSON json_schema(std::set<std::string>* keys = 0);
76 QPDF_DLL 90 QPDF_DLL
@@ -165,6 +179,10 @@ class QPDFJob @@ -165,6 +179,10 @@ class QPDFJob
165 void setWriterOptions(QPDF& pdf, QPDFWriter& w); 179 void setWriterOptions(QPDF& pdf, QPDFWriter& w);
166 void doSplitPages(QPDF& pdf, bool& warnings); 180 void doSplitPages(QPDF& pdf, bool& warnings);
167 void writeOutfile(QPDF& pdf); 181 void writeOutfile(QPDF& pdf);
  182 + void doJSON(QPDF& pdf);
  183 + void doInspection(QPDF& pdf);
  184 + void showEncryption(QPDF& pdf);
  185 + void doCheck(QPDF& pdf);
168 186
169 enum remove_unref_e { re_auto, re_yes, re_no }; 187 enum remove_unref_e { re_auto, re_yes, re_no };
170 188
@@ -295,6 +313,7 @@ class QPDFJob @@ -295,6 +313,7 @@ class QPDFJob
295 Members(); 313 Members();
296 Members(Members const&) = delete; 314 Members(Members const&) = delete;
297 315
  316 + std::string whoami;
298 bool warnings; 317 bool warnings;
299 bool creates_output; 318 bool creates_output;
300 std::ostream* out_stream; 319 std::ostream* out_stream;
libqpdf/QPDFJob.cc
@@ -35,8 +35,6 @@ @@ -35,8 +35,6 @@
35 #include <qpdf/QIntC.hh> 35 #include <qpdf/QIntC.hh>
36 36
37 // QXXXQ temporary for compilation 37 // QXXXQ temporary for compilation
38 -static int constexpr EXIT_ERROR = 2;  
39 -static int EXIT_WARNING = 3; // may be changed to 0 at runtime  
40 static char const* whoami = "qpdf"; 38 static char const* whoami = "qpdf";
41 // /QXXXQ 39 // /QXXXQ
42 40
@@ -82,7 +80,8 @@ namespace @@ -82,7 +80,8 @@ namespace
82 class ProgressReporter: public QPDFWriter::ProgressReporter 80 class ProgressReporter: public QPDFWriter::ProgressReporter
83 { 81 {
84 public: 82 public:
85 - ProgressReporter(char const* filename) : 83 + ProgressReporter(std::string const& whoami, char const* filename) :
  84 + whoami(whoami),
86 filename(filename) 85 filename(filename)
87 { 86 {
88 } 87 }
@@ -92,6 +91,7 @@ namespace @@ -92,6 +91,7 @@ namespace
92 91
93 virtual void reportProgress(int); 92 virtual void reportProgress(int);
94 private: 93 private:
  94 + std::string whoami;
95 std::string filename; 95 std::string filename;
96 }; 96 };
97 } 97 }
@@ -133,6 +133,7 @@ ProgressReporter::reportProgress(int percentage) @@ -133,6 +133,7 @@ ProgressReporter::reportProgress(int percentage)
133 133
134 134
135 QPDFJob::Members::Members() : 135 QPDFJob::Members::Members() :
  136 + whoami("qpdf"),
136 warnings(false), 137 warnings(false),
137 creates_output(false), 138 creates_output(false),
138 out_stream(&std::cout), 139 out_stream(&std::cout),
@@ -245,12 +246,28 @@ QPDFJob::QPDFJob() : @@ -245,12 +246,28 @@ QPDFJob::QPDFJob() :
245 } 246 }
246 247
247 void 248 void
  249 +QPDFJob::setMessagePrefix(std::string const& whoami)
  250 +{
  251 + this->m->whoami = whoami;
  252 +}
  253 +
  254 +void
248 QPDFJob::setOutputStreams(std::ostream* out, std::ostream* err) 255 QPDFJob::setOutputStreams(std::ostream* out, std::ostream* err)
249 { 256 {
250 this->m->out_stream = out ? out : &std::cout; 257 this->m->out_stream = out ? out : &std::cout;
251 this->m->err_stream = err ? err : &std::cerr; 258 this->m->err_stream = err ? err : &std::cerr;
252 } 259 }
253 260
  261 +void
  262 +QPDFJob::doIfVerbose(
  263 + std::function<void(std::ostream&, std::string const& prefix)> fn)
  264 +{
  265 + if (this->verbose && (this->m->out_stream != nullptr))
  266 + {
  267 + fn(*(this->m->out_stream), this->m->whoami);
  268 + }
  269 +}
  270 +
254 static void parse_version(std::string const& full_version_string, 271 static void parse_version(std::string const& full_version_string,
255 std::string& version, int& extension_level) 272 std::string& version, int& extension_level)
256 { 273 {
@@ -316,8 +333,10 @@ static std::string show_encryption_method(QPDF::encryption_method_e method) @@ -316,8 +333,10 @@ static std::string show_encryption_method(QPDF::encryption_method_e method)
316 return result; 333 return result;
317 } 334 }
318 335
319 -static void show_encryption(QPDF& pdf, QPDFJob& o) 336 +void
  337 +QPDFJob::showEncryption(QPDF& pdf)
320 { 338 {
  339 + QPDFJob& o = *this; // QXXXQ
321 // Extract /P from /Encrypt 340 // Extract /P from /Encrypt
322 int R = 0; 341 int R = 0;
323 int P = 0; 342 int P = 0;
@@ -380,8 +399,10 @@ static void show_encryption(QPDF&amp; pdf, QPDFJob&amp; o) @@ -380,8 +399,10 @@ static void show_encryption(QPDF&amp; pdf, QPDFJob&amp; o)
380 } 399 }
381 } 400 }
382 401
383 -static void do_check(QPDF& pdf, QPDFJob& o, int& exit_code) 402 +void
  403 +QPDFJob::doCheck(QPDF& pdf)
384 { 404 {
  405 + QPDFJob& o = *this; // QXXXQ
385 // Code below may set okay to false but not to true. 406 // Code below may set okay to false but not to true.
386 // We assume okay until we prove otherwise but may 407 // We assume okay until we prove otherwise but may
387 // continue to perform additional checks after finding 408 // continue to perform additional checks after finding
@@ -399,7 +420,7 @@ static void do_check(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code) @@ -399,7 +420,7 @@ static void do_check(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
399 << pdf.getExtensionLevel(); 420 << pdf.getExtensionLevel();
400 } 421 }
401 std::cout << std::endl; 422 std::cout << std::endl;
402 - show_encryption(pdf, o); 423 + showEncryption(pdf);
403 if (pdf.isLinearized()) 424 if (pdf.isLinearized())
404 { 425 {
405 std::cout << "File is linearized\n"; 426 std::cout << "File is linearized\n";
@@ -443,8 +464,9 @@ static void do_check(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code) @@ -443,8 +464,9 @@ static void do_check(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
443 catch (QPDFExc& e) 464 catch (QPDFExc& e)
444 { 465 {
445 okay = false; 466 okay = false;
446 - std::cerr << "ERROR: page " << pageno << ": "  
447 - << e.what() << std::endl; 467 + *(this->m->err_stream)
  468 + << "ERROR: page " << pageno << ": "
  469 + << e.what() << std::endl;
448 } 470 }
449 } 471 }
450 } 472 }
@@ -453,28 +475,27 @@ static void do_check(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code) @@ -453,28 +475,27 @@ static void do_check(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
453 std::cerr << "ERROR: " << e.what() << std::endl; 475 std::cerr << "ERROR: " << e.what() << std::endl;
454 okay = false; 476 okay = false;
455 } 477 }
456 - if (okay) 478 + if (! okay)
457 { 479 {
458 - if ((! pdf.getWarnings().empty()) || warnings)  
459 - {  
460 - exit_code = EXIT_WARNING;  
461 - }  
462 - else  
463 - {  
464 - std::cout << "No syntax or stream encoding errors"  
465 - << " found; the file may still contain"  
466 - << std::endl  
467 - << "errors that qpdf cannot detect"  
468 - << std::endl;  
469 - } 480 + throw std::runtime_error("errors detected");
  481 + }
  482 +
  483 + if ((! pdf.getWarnings().empty()) || warnings)
  484 + {
  485 + this->m->warnings = TRUE;
470 } 486 }
471 else 487 else
472 { 488 {
473 - exit_code = EXIT_ERROR; 489 + *(this->m->out_stream)
  490 + << "No syntax or stream encoding errors"
  491 + << " found; the file may still contain"
  492 + << std::endl
  493 + << "errors that qpdf cannot detect"
  494 + << std::endl;
474 } 495 }
475 } 496 }
476 497
477 -static void do_show_obj(QPDF& pdf, QPDFJob& o, int& exit_code) 498 +static void do_show_obj(QPDF& pdf, QPDFJob& o)
478 { 499 {
479 QPDFObjectHandle obj; 500 QPDFObjectHandle obj;
480 if (o.show_trailer) 501 if (o.show_trailer)
@@ -485,6 +506,7 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code) @@ -485,6 +506,7 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
485 { 506 {
486 obj = pdf.getObjectByID(o.show_obj, o.show_gen); 507 obj = pdf.getObjectByID(o.show_obj, o.show_gen);
487 } 508 }
  509 + bool error = false;
488 if (obj.isStream()) 510 if (obj.isStream())
489 { 511 {
490 if (o.show_raw_stream_data || o.show_filtered_stream_data) 512 if (o.show_raw_stream_data || o.show_filtered_stream_data)
@@ -494,9 +516,8 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code) @@ -494,9 +516,8 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
494 (! obj.pipeStreamData(0, 0, qpdf_dl_all))) 516 (! obj.pipeStreamData(0, 0, qpdf_dl_all)))
495 { 517 {
496 QTC::TC("qpdf", "qpdf unable to filter"); 518 QTC::TC("qpdf", "qpdf unable to filter");
497 - std::cerr << "Unable to filter stream data."  
498 - << std::endl;  
499 - exit_code = EXIT_ERROR; 519 + obj.warnIfPossible("unable to filter stream data");
  520 + error = true;
500 } 521 }
501 else 522 else
502 { 523 {
@@ -519,6 +540,11 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code) @@ -519,6 +540,11 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
519 { 540 {
520 std::cout << obj.unparseResolved() << std::endl; 541 std::cout << obj.unparseResolved() << std::endl;
521 } 542 }
  543 + if (error)
  544 + {
  545 + throw std::runtime_error(
  546 + "unable to get object " + obj.getObjGen().unparse());
  547 + }
522 } 548 }
523 549
524 static void do_show_pages(QPDF& pdf, QPDFJob& o) 550 static void do_show_pages(QPDF& pdf, QPDFJob& o)
@@ -581,29 +607,28 @@ static void do_list_attachments(QPDF&amp; pdf, QPDFJob&amp; o) @@ -581,29 +607,28 @@ static void do_list_attachments(QPDF&amp; pdf, QPDFJob&amp; o)
581 std::cout << key << " -> " 607 std::cout << key << " -> "
582 << efoh->getEmbeddedFileStream().getObjGen() 608 << efoh->getEmbeddedFileStream().getObjGen()
583 << std::endl; 609 << std::endl;
584 - if (o.verbose)  
585 - { 610 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
586 auto desc = efoh->getDescription(); 611 auto desc = efoh->getDescription();
587 if (! desc.empty()) 612 if (! desc.empty())
588 { 613 {
589 - std::cout << " description: " << desc << std::endl; 614 + cout << " description: " << desc << std::endl;
590 } 615 }
591 - std::cout << " preferred name: " << efoh->getFilename() 616 + cout << " preferred name: " << efoh->getFilename()
592 << std::endl; 617 << std::endl;
593 - std::cout << " all names:" << std::endl; 618 + cout << " all names:" << std::endl;
594 for (auto const& i2: efoh->getFilenames()) 619 for (auto const& i2: efoh->getFilenames())
595 { 620 {
596 - std::cout << " " << i2.first << " -> " << i2.second 621 + cout << " " << i2.first << " -> " << i2.second
597 << std::endl; 622 << std::endl;
598 } 623 }
599 - std::cout << " all data streams:" << std::endl; 624 + cout << " all data streams:" << std::endl;
600 for (auto i2: efoh->getEmbeddedFileStreams().ditems()) 625 for (auto i2: efoh->getEmbeddedFileStreams().ditems())
601 { 626 {
602 - std::cout << " " << i2.first << " -> " 627 + cout << " " << i2.first << " -> "
603 << i2.second.getObjGen() 628 << i2.second.getObjGen()
604 << std::endl; 629 << std::endl;
605 } 630 }
606 - } 631 + });
607 } 632 }
608 } 633 }
609 else 634 else
@@ -612,16 +637,14 @@ static void do_list_attachments(QPDF&amp; pdf, QPDFJob&amp; o) @@ -612,16 +637,14 @@ static void do_list_attachments(QPDF&amp; pdf, QPDFJob&amp; o)
612 } 637 }
613 } 638 }
614 639
615 -static void do_show_attachment(QPDF& pdf, QPDFJob& o, int& exit_code) 640 +static void do_show_attachment(QPDF& pdf, QPDFJob& o)
616 { 641 {
617 QPDFEmbeddedFileDocumentHelper efdh(pdf); 642 QPDFEmbeddedFileDocumentHelper efdh(pdf);
618 auto fs = efdh.getEmbeddedFile(o.attachment_to_show); 643 auto fs = efdh.getEmbeddedFile(o.attachment_to_show);
619 if (! fs) 644 if (! fs)
620 { 645 {
621 - std::cerr << whoami << ": attachment " << o.attachment_to_show  
622 - << " not found" << std::endl;  
623 - exit_code = EXIT_ERROR;  
624 - return; 646 + throw std::runtime_error(
  647 + "attachment " + o.attachment_to_show + " not found");
625 } 648 }
626 auto efs = fs->getEmbeddedFileStream(); 649 auto efs = fs->getEmbeddedFileStream();
627 QUtil::binary_stdout(); 650 QUtil::binary_stdout();
@@ -1149,8 +1172,9 @@ QPDFJob::json_schema(std::set&lt;std::string&gt;* keys) @@ -1149,8 +1172,9 @@ QPDFJob::json_schema(std::set&lt;std::string&gt;* keys)
1149 1172
1150 bool all_keys = ((keys == 0) || keys->empty()); 1173 bool all_keys = ((keys == 0) || keys->empty());
1151 1174
  1175 + // QXXXQ
1152 // The list of selectable top-level keys id duplicated in three 1176 // The list of selectable top-level keys id duplicated in three
1153 - // places: json_schema, do_json, and initOptionTable. 1177 + // places: json_schema, doJSON, and initOptionTable.
1154 if (all_keys || keys->count("objects")) 1178 if (all_keys || keys->count("objects"))
1155 { 1179 {
1156 schema.addDictionaryMember( 1180 schema.addDictionaryMember(
@@ -1456,8 +1480,10 @@ QPDFJob::json_schema(std::set&lt;std::string&gt;* keys) @@ -1456,8 +1480,10 @@ QPDFJob::json_schema(std::set&lt;std::string&gt;* keys)
1456 return schema; 1480 return schema;
1457 } 1481 }
1458 1482
1459 -static void do_json(QPDF& pdf, QPDFJob& o) 1483 +void
  1484 +QPDFJob::doJSON(QPDF& pdf)
1460 { 1485 {
  1486 + QPDFJob& o = *this; // QXXXQ
1461 JSON j = JSON::makeDictionary(); 1487 JSON j = JSON::makeDictionary();
1462 // This version is updated every time a non-backward-compatible 1488 // This version is updated every time a non-backward-compatible
1463 // change is made to the JSON format. Clients of the JSON are to 1489 // change is made to the JSON format. Clients of the JSON are to
@@ -1486,8 +1512,9 @@ static void do_json(QPDF&amp; pdf, QPDFJob&amp; o) @@ -1486,8 +1512,9 @@ static void do_json(QPDF&amp; pdf, QPDFJob&amp; o)
1486 "decodelevel", JSON::makeString(decode_level_str)); 1512 "decodelevel", JSON::makeString(decode_level_str));
1487 1513
1488 bool all_keys = o.json_keys.empty(); 1514 bool all_keys = o.json_keys.empty();
  1515 + // QXXXQ
1489 // The list of selectable top-level keys id duplicated in three 1516 // The list of selectable top-level keys id duplicated in three
1490 - // places: json_schema, do_json, and initOptionTable. 1517 + // places: json_schema, doJSON, and initOptionTable.
1491 if (all_keys || o.json_keys.count("objects")) 1518 if (all_keys || o.json_keys.count("objects"))
1492 { 1519 {
1493 do_json_objects(pdf, o, j); 1520 do_json_objects(pdf, o, j);
@@ -1527,9 +1554,9 @@ static void do_json(QPDF&amp; pdf, QPDFJob&amp; o) @@ -1527,9 +1554,9 @@ static void do_json(QPDF&amp; pdf, QPDFJob&amp; o)
1527 std::list<std::string> errors; 1554 std::list<std::string> errors;
1528 if (! j.checkSchema(schema, errors)) 1555 if (! j.checkSchema(schema, errors))
1529 { 1556 {
1530 - std::cerr  
1531 - << whoami << " didn't create JSON that complies with its own\n\  
1532 -rules. Please report this as a bug at\n\ 1557 + *(this->m->err_stream)
  1558 + << "QPDFJob didn't create JSON that complies with its own rules.\n\
  1559 +Please report this as a bug at\n\
1533 https://github.com/qpdf/qpdf/issues/new\n\ 1560 https://github.com/qpdf/qpdf/issues/new\n\
1534 ideally with the file that caused the error and the output below. Thanks!\n\ 1561 ideally with the file that caused the error and the output below. Thanks!\n\
1535 \n"; 1562 \n";
@@ -1543,16 +1570,17 @@ ideally with the file that caused the error and the output below. Thanks!\n\ @@ -1543,16 +1570,17 @@ ideally with the file that caused the error and the output below. Thanks!\n\
1543 std::cout << j.unparse() << std::endl; 1570 std::cout << j.unparse() << std::endl;
1544 } 1571 }
1545 1572
1546 -static void do_inspection(QPDF& pdf, QPDFJob& o) 1573 +void
  1574 +QPDFJob::doInspection(QPDF& pdf)
1547 { 1575 {
1548 - int exit_code = 0; 1576 + QPDFJob& o = *this; // QXXXQ
1549 if (o.check) 1577 if (o.check)
1550 { 1578 {
1551 - do_check(pdf, o, exit_code); 1579 + doCheck(pdf);
1552 } 1580 }
1553 if (o.json) 1581 if (o.json)
1554 { 1582 {
1555 - do_json(pdf, o); 1583 + doJSON(pdf);
1556 } 1584 }
1557 if (o.show_npages) 1585 if (o.show_npages)
1558 { 1586 {
@@ -1562,7 +1590,7 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o) @@ -1562,7 +1590,7 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1562 } 1590 }
1563 if (o.show_encryption) 1591 if (o.show_encryption)
1564 { 1592 {
1565 - show_encryption(pdf, o); 1593 + showEncryption(pdf);
1566 } 1594 }
1567 if (o.check_linearization) 1595 if (o.check_linearization)
1568 { 1596 {
@@ -1571,9 +1599,9 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o) @@ -1571,9 +1599,9 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1571 std::cout << o.infilename << ": no linearization errors" 1599 std::cout << o.infilename << ": no linearization errors"
1572 << std::endl; 1600 << std::endl;
1573 } 1601 }
1574 - else if (exit_code != EXIT_ERROR) 1602 + else
1575 { 1603 {
1576 - exit_code = EXIT_WARNING; 1604 + this->m->warnings = true;
1577 } 1605 }
1578 } 1606 }
1579 if (o.show_linearization) 1607 if (o.show_linearization)
@@ -1594,7 +1622,7 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o) @@ -1594,7 +1622,7 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1594 } 1622 }
1595 if ((o.show_obj > 0) || o.show_trailer) 1623 if ((o.show_obj > 0) || o.show_trailer)
1596 { 1624 {
1597 - do_show_obj(pdf, o, exit_code); 1625 + do_show_obj(pdf, o);
1598 } 1626 }
1599 if (o.show_pages) 1627 if (o.show_pages)
1600 { 1628 {
@@ -1606,17 +1634,11 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o) @@ -1606,17 +1634,11 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1606 } 1634 }
1607 if (! o.attachment_to_show.empty()) 1635 if (! o.attachment_to_show.empty())
1608 { 1636 {
1609 - do_show_attachment(pdf, o, exit_code);  
1610 - }  
1611 - if ((! pdf.getWarnings().empty()) && (exit_code != EXIT_ERROR))  
1612 - {  
1613 - std::cerr << whoami  
1614 - << ": operation succeeded with warnings" << std::endl;  
1615 - exit_code = EXIT_WARNING; 1637 + do_show_attachment(pdf, o);
1616 } 1638 }
1617 - if (exit_code) 1639 + if (! pdf.getWarnings().empty())
1618 { 1640 {
1619 - exit(exit_code); // QXXXQ 1641 + this->m->warnings = true;
1620 } 1642 }
1621 } 1643 }
1622 1644
@@ -1637,11 +1659,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -1637,11 +1659,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1637 QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace"); 1659 QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace");
1638 if (! (w_obj.isNumber() && h_obj.isNumber())) 1660 if (! (w_obj.isNumber() && h_obj.isNumber()))
1639 { 1661 {
1640 - if (o.verbose && (! description.empty())) 1662 + if (! description.empty())
1641 { 1663 {
1642 - std::cout << whoami << ": " << description  
1643 - << ": not optimizing because image dictionary"  
1644 - << " is missing required keys" << std::endl; 1664 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1665 + cout << whoami << ": " << description
  1666 + << ": not optimizing because image dictionary"
  1667 + << " is missing required keys" << std::endl;
  1668 + });
1645 } 1669 }
1646 return result; 1670 return result;
1647 } 1671 }
@@ -1649,11 +1673,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -1649,11 +1673,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1649 if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8))) 1673 if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8)))
1650 { 1674 {
1651 QTC::TC("qpdf", "qpdf image optimize bits per component"); 1675 QTC::TC("qpdf", "qpdf image optimize bits per component");
1652 - if (o.verbose && (! description.empty())) 1676 + if (! description.empty())
1653 { 1677 {
1654 - std::cout << whoami << ": " << description  
1655 - << ": not optimizing because image has other than"  
1656 - << " 8 bits per component" << std::endl; 1678 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1679 + cout << whoami << ": " << description
  1680 + << ": not optimizing because image has other than"
  1681 + << " 8 bits per component" << std::endl;
  1682 + });
1657 } 1683 }
1658 return result; 1684 return result;
1659 } 1685 }
@@ -1700,11 +1726,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -1700,11 +1726,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1700 else 1726 else
1701 { 1727 {
1702 QTC::TC("qpdf", "qpdf image optimize colorspace"); 1728 QTC::TC("qpdf", "qpdf image optimize colorspace");
1703 - if (o.verbose && (! description.empty())) 1729 + if (! description.empty())
1704 { 1730 {
1705 - std::cout << whoami << ": " << description  
1706 - << ": not optimizing because qpdf can't optimize"  
1707 - << " images with this colorspace" << std::endl; 1731 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1732 + cout << whoami << ": " << description
  1733 + << ": not optimizing because qpdf can't optimize"
  1734 + << " images with this colorspace" << std::endl;
  1735 + });
1708 } 1736 }
1709 return result; 1737 return result;
1710 } 1738 }
@@ -1713,12 +1741,14 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -1713,12 +1741,14 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1713 ((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area))) 1741 ((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area)))
1714 { 1742 {
1715 QTC::TC("qpdf", "qpdf image optimize too small"); 1743 QTC::TC("qpdf", "qpdf image optimize too small");
1716 - if (o.verbose && (! description.empty())) 1744 + if (! description.empty())
1717 { 1745 {
1718 - std::cout << whoami << ": " << description  
1719 - << ": not optimizing because image"  
1720 - << " is smaller than requested minimum dimensions"  
1721 - << std::endl; 1746 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1747 + cout << whoami << ": " << description
  1748 + << ": not optimizing because image"
  1749 + << " is smaller than requested minimum dimensions"
  1750 + << std::endl;
  1751 + });
1722 } 1752 }
1723 return result; 1753 return result;
1724 } 1754 }
@@ -1733,13 +1763,12 @@ ImageOptimizer::evaluate(std::string const&amp; description) @@ -1733,13 +1763,12 @@ ImageOptimizer::evaluate(std::string const&amp; description)
1733 if (! image.pipeStreamData(0, 0, qpdf_dl_specialized, true)) 1763 if (! image.pipeStreamData(0, 0, qpdf_dl_specialized, true))
1734 { 1764 {
1735 QTC::TC("qpdf", "qpdf image optimize no pipeline"); 1765 QTC::TC("qpdf", "qpdf image optimize no pipeline");
1736 - if (o.verbose)  
1737 - {  
1738 - std::cout << whoami << ": " << description  
1739 - << ": not optimizing because unable to decode data"  
1740 - << " or data already uses DCT"  
1741 - << std::endl;  
1742 - } 1766 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1767 + cout << whoami << ": " << description
  1768 + << ": not optimizing because unable to decode data"
  1769 + << " or data already uses DCT"
  1770 + << std::endl;
  1771 + });
1743 return false; 1772 return false;
1744 } 1773 }
1745 Pl_Discard d; 1774 Pl_Discard d;
@@ -1758,21 +1787,19 @@ ImageOptimizer::evaluate(std::string const&amp; description) @@ -1758,21 +1787,19 @@ ImageOptimizer::evaluate(std::string const&amp; description)
1758 if (c.getCount() >= orig_length) 1787 if (c.getCount() >= orig_length)
1759 { 1788 {
1760 QTC::TC("qpdf", "qpdf image optimize no shrink"); 1789 QTC::TC("qpdf", "qpdf image optimize no shrink");
1761 - if (o.verbose)  
1762 - {  
1763 - std::cout << whoami << ": " << description  
1764 - << ": not optimizing because DCT compression does not"  
1765 - << " reduce image size" << std::endl;  
1766 - } 1790 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1791 + cout << whoami << ": " << description
  1792 + << ": not optimizing because DCT compression does not"
  1793 + << " reduce image size" << std::endl;
  1794 + });
1767 return false; 1795 return false;
1768 } 1796 }
1769 - if (o.verbose)  
1770 - {  
1771 - std::cout << whoami << ": " << description  
1772 - << ": optimizing image reduces size from "  
1773 - << orig_length << " to " << c.getCount()  
1774 - << std::endl;  
1775 - } 1797 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1798 + cout << whoami << ": " << description
  1799 + << ": optimizing image reduces size from "
  1800 + << orig_length << " to " << c.getCount()
  1801 + << std::endl;
  1802 + });
1776 return true; 1803 return true;
1777 } 1804 }
1778 1805
@@ -1889,13 +1916,15 @@ static PointerHolder&lt;QPDF&gt; do_process( @@ -1889,13 +1916,15 @@ static PointerHolder&lt;QPDF&gt; do_process(
1889 throw e; 1916 throw e;
1890 } 1917 }
1891 } 1918 }
1892 - if ((! warned) && o.verbose) 1919 + if (! warned)
1893 { 1920 {
1894 warned = true; 1921 warned = true;
1895 - std::cout << whoami << ": supplied password didn't work;"  
1896 - << " trying other passwords based on interpreting"  
1897 - << " password with different string encodings"  
1898 - << std::endl; 1922 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  1923 + cout << whoami << ": supplied password didn't work;"
  1924 + << " trying other passwords based on interpreting"
  1925 + << " password with different string encodings"
  1926 + << std::endl;
  1927 + });
1899 } 1928 }
1900 } 1929 }
1901 // Should not be reachable 1930 // Should not be reachable
@@ -2032,10 +2061,9 @@ static void do_under_overlay_for_page( @@ -2032,10 +2061,9 @@ static void do_under_overlay_for_page(
2032 iter != pagenos[pageno].end(); ++iter) 2061 iter != pagenos[pageno].end(); ++iter)
2033 { 2062 {
2034 int from_pageno = *iter; 2063 int from_pageno = *iter;
2035 - if (o.verbose)  
2036 - {  
2037 - std::cout << " " << uo.which << " " << from_pageno << std::endl;  
2038 - } 2064 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2065 + cout << " " << uo.which << " " << from_pageno << std::endl;
  2066 + });
2039 auto from_page = pages.at(QIntC::to_size(from_pageno - 1)); 2067 auto from_page = pages.at(QIntC::to_size(from_pageno - 1));
2040 if (0 == fo.count(from_pageno)) 2068 if (0 == fo.count(from_pageno))
2041 { 2069 {
@@ -2116,16 +2144,14 @@ QPDFJob::handleUnderOverlay(QPDF&amp; pdf) @@ -2116,16 +2144,14 @@ QPDFJob::handleUnderOverlay(QPDF&amp; pdf)
2116 QPDFPageDocumentHelper main_pdh(pdf); 2144 QPDFPageDocumentHelper main_pdh(pdf);
2117 std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages(); 2145 std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages();
2118 size_t main_npages = main_pages.size(); 2146 size_t main_npages = main_pages.size();
2119 - if (o.verbose)  
2120 - {  
2121 - std::cout << whoami << ": processing underlay/overlay" << std::endl;  
2122 - } 2147 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2148 + cout << whoami << ": processing underlay/overlay" << std::endl;
  2149 + });
2123 for (size_t i = 0; i < main_npages; ++i) 2150 for (size_t i = 0; i < main_npages; ++i)
2124 { 2151 {
2125 - if (o.verbose)  
2126 - {  
2127 - std::cout << " page " << 1+i << std::endl;  
2128 - } 2152 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2153 + cout << " page " << 1+i << std::endl;
  2154 + });
2129 do_under_overlay_for_page(pdf, o, o.underlay, underlay_pagenos, i, 2155 do_under_overlay_for_page(pdf, o, o.underlay, underlay_pagenos, i,
2130 underlay_fo, upages, main_pages.at(i), 2156 underlay_fo, upages, main_pages.at(i),
2131 true); 2157 true);
@@ -2174,12 +2200,11 @@ QPDFJob::addAttachments(QPDF&amp; pdf) @@ -2174,12 +2200,11 @@ QPDFJob::addAttachments(QPDF&amp; pdf)
2174 } 2200 }
2175 2201
2176 efdh.replaceEmbeddedFile(to_add.key, fs); 2202 efdh.replaceEmbeddedFile(to_add.key, fs);
2177 - if (o.verbose)  
2178 - {  
2179 - std::cout << whoami << ": attached " << to_add.path  
2180 - << " as " << to_add.filename  
2181 - << " with key " << to_add.key << std::endl;  
2182 - } 2203 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2204 + cout << whoami << ": attached " << to_add.path
  2205 + << " as " << to_add.filename
  2206 + << " with key " << to_add.key << std::endl;
  2207 + });
2183 } 2208 }
2184 2209
2185 if (! duplicated_keys.empty()) 2210 if (! duplicated_keys.empty())
@@ -2210,11 +2235,10 @@ QPDFJob::copyAttachments(QPDF&amp; pdf) @@ -2210,11 +2235,10 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2210 std::vector<std::string> duplicates; 2235 std::vector<std::string> duplicates;
2211 for (auto const& to_copy: o.attachments_to_copy) 2236 for (auto const& to_copy: o.attachments_to_copy)
2212 { 2237 {
2213 - if (o.verbose)  
2214 - {  
2215 - std::cout << whoami << ": copying attachments from "  
2216 - << to_copy.path << std::endl;  
2217 - } 2238 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2239 + cout << whoami << ": copying attachments from "
  2240 + << to_copy.path << std::endl;
  2241 + });
2218 auto other = processFile( 2242 auto other = processFile(
2219 to_copy.path.c_str(), to_copy.password.c_str()); 2243 to_copy.path.c_str(), to_copy.password.c_str());
2220 QPDFEmbeddedFileDocumentHelper other_efdh(*other); 2244 QPDFEmbeddedFileDocumentHelper other_efdh(*other);
@@ -2233,11 +2257,11 @@ QPDFJob::copyAttachments(QPDF&amp; pdf) @@ -2233,11 +2257,11 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2233 iter.second->getObjectHandle()); 2257 iter.second->getObjectHandle());
2234 efdh.replaceEmbeddedFile( 2258 efdh.replaceEmbeddedFile(
2235 new_key, QPDFFileSpecObjectHelper(new_fs_oh)); 2259 new_key, QPDFFileSpecObjectHelper(new_fs_oh));
2236 - if (o.verbose)  
2237 - {  
2238 - std::cout << " " << iter.first << " -> " << new_key  
2239 - << std::endl;  
2240 - } 2260 + o.doIfVerbose([&](std::ostream& cout,
  2261 + std::string const& whoami) {
  2262 + cout << " " << iter.first << " -> " << new_key
  2263 + << std::endl;
  2264 + });
2241 } 2265 }
2242 } 2266 }
2243 2267
@@ -2361,11 +2385,11 @@ QPDFJob::handleTransformations(QPDF&amp; pdf) @@ -2361,11 +2385,11 @@ QPDFJob::handleTransformations(QPDF&amp; pdf)
2361 { 2385 {
2362 if (efdh.removeEmbeddedFile(key)) 2386 if (efdh.removeEmbeddedFile(key))
2363 { 2387 {
2364 - if (o.verbose)  
2365 - {  
2366 - std::cout << whoami << 2388 + o.doIfVerbose([&](std::ostream& cout,
  2389 + std::string const& whoami) {
  2390 + cout << whoami <<
2367 ": removed attachment " << key << std::endl; 2391 ": removed attachment " << key << std::endl;
2368 - } 2392 + });
2369 } 2393 }
2370 else 2394 else
2371 { 2395 {
@@ -2407,11 +2431,10 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o) @@ -2407,11 +2431,10 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2407 std::set<QPDFObjGen> resources_seen; // shared resources detection 2431 std::set<QPDFObjGen> resources_seen; // shared resources detection
2408 std::set<QPDFObjGen> nodes_seen; // loop detection 2432 std::set<QPDFObjGen> nodes_seen; // loop detection
2409 2433
2410 - if (o.verbose)  
2411 - {  
2412 - std::cout << whoami << ": " << pdf.getFilename()  
2413 - << ": checking for shared resources" << std::endl;  
2414 - } 2434 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2435 + cout << whoami << ": " << pdf.getFilename()
  2436 + << ": checking for shared resources" << std::endl;
  2437 + });
2415 2438
2416 std::list<QPDFObjectHandle> queue; 2439 std::list<QPDFObjectHandle> queue;
2417 queue.push_back(pdf.getRoot().getKey("/Pages")); 2440 queue.push_back(pdf.getRoot().getKey("/Pages"));
@@ -2433,12 +2456,12 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o) @@ -2433,12 +2456,12 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2433 if (dict.hasKey("/Resources")) 2456 if (dict.hasKey("/Resources"))
2434 { 2457 {
2435 QTC::TC("qpdf", "qpdf found resources in non-leaf"); 2458 QTC::TC("qpdf", "qpdf found resources in non-leaf");
2436 - if (o.verbose)  
2437 - {  
2438 - std::cout << " found resources in non-leaf page node "  
2439 - << og.getObj() << " " << og.getGen()  
2440 - << std::endl;  
2441 - } 2459 + o.doIfVerbose([&](std::ostream& cout,
  2460 + std::string const& whoami) {
  2461 + cout << " found resources in non-leaf page node "
  2462 + << og.getObj() << " " << og.getGen()
  2463 + << std::endl;
  2464 + });
2442 return true; 2465 return true;
2443 } 2466 }
2444 int n = kids.getArrayNItems(); 2467 int n = kids.getArrayNItems();
@@ -2457,15 +2480,15 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o) @@ -2457,15 +2480,15 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2457 if (resources_seen.count(resources_og)) 2480 if (resources_seen.count(resources_og))
2458 { 2481 {
2459 QTC::TC("qpdf", "qpdf found shared resources in leaf"); 2482 QTC::TC("qpdf", "qpdf found shared resources in leaf");
2460 - if (o.verbose)  
2461 - {  
2462 - std::cout << " found shared resources in leaf node "  
2463 - << og.getObj() << " " << og.getGen()  
2464 - << ": "  
2465 - << resources_og.getObj() << " "  
2466 - << resources_og.getGen()  
2467 - << std::endl;  
2468 - } 2483 + o.doIfVerbose([&](std::ostream& cout,
  2484 + std::string const& whoami) {
  2485 + cout << " found shared resources in leaf node "
  2486 + << og.getObj() << " " << og.getGen()
  2487 + << ": "
  2488 + << resources_og.getObj() << " "
  2489 + << resources_og.getGen()
  2490 + << std::endl;
  2491 + });
2469 return true; 2492 return true;
2470 } 2493 }
2471 resources_seen.insert(resources_og); 2494 resources_seen.insert(resources_og);
@@ -2479,15 +2502,15 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o) @@ -2479,15 +2502,15 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2479 if (resources_seen.count(xobject_og)) 2502 if (resources_seen.count(xobject_og))
2480 { 2503 {
2481 QTC::TC("qpdf", "qpdf found shared xobject in leaf"); 2504 QTC::TC("qpdf", "qpdf found shared xobject in leaf");
2482 - if (o.verbose)  
2483 - {  
2484 - std::cout << " found shared xobject in leaf node "  
2485 - << og.getObj() << " " << og.getGen()  
2486 - << ": "  
2487 - << xobject_og.getObj() << " "  
2488 - << xobject_og.getGen()  
2489 - << std::endl;  
2490 - } 2505 + o.doIfVerbose([&](std::ostream& cout,
  2506 + std::string const& whoami) {
  2507 + cout << " found shared xobject in leaf node "
  2508 + << og.getObj() << " " << og.getGen()
  2509 + << ": "
  2510 + << xobject_og.getObj() << " "
  2511 + << xobject_og.getGen()
  2512 + << std::endl;
  2513 + });
2491 return true; 2514 return true;
2492 } 2515 }
2493 resources_seen.insert(xobject_og); 2516 resources_seen.insert(xobject_og);
@@ -2512,10 +2535,9 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o) @@ -2512,10 +2535,9 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2512 } 2535 }
2513 } 2536 }
2514 2537
2515 - if (o.verbose)  
2516 - {  
2517 - std::cout << whoami << ": no shared resources found" << std::endl;  
2518 - } 2538 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2539 + cout << whoami << ": no shared resources found" << std::endl;
  2540 + });
2519 return false; 2541 return false;
2520 } 2542 }
2521 2543
@@ -2570,20 +2592,18 @@ static void handle_page_specs( @@ -2570,20 +2592,18 @@ static void handle_page_specs(
2570 if (filenames.size() > o.keep_files_open_threshold) 2592 if (filenames.size() > o.keep_files_open_threshold)
2571 { 2593 {
2572 QTC::TC("qpdf", "qpdf disable keep files open"); 2594 QTC::TC("qpdf", "qpdf disable keep files open");
2573 - if (o.verbose)  
2574 - {  
2575 - std::cout << whoami << ": selecting --keep-open-files=n"  
2576 - << std::endl;  
2577 - } 2595 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2596 + cout << whoami << ": selecting --keep-open-files=n"
  2597 + << std::endl;
  2598 + });
2578 o.keep_files_open = false; 2599 o.keep_files_open = false;
2579 } 2600 }
2580 else 2601 else
2581 { 2602 {
2582 - if (o.verbose)  
2583 - {  
2584 - std::cout << whoami << ": selecting --keep-open-files=y"  
2585 - << std::endl;  
2586 - } 2603 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2604 + cout << whoami << ": selecting --keep-open-files=y"
  2605 + << std::endl;
  2606 + });
2587 o.keep_files_open = true; 2607 o.keep_files_open = true;
2588 QTC::TC("qpdf", "qpdf don't disable keep files open"); 2608 QTC::TC("qpdf", "qpdf don't disable keep files open");
2589 } 2609 }
@@ -2618,11 +2638,10 @@ static void handle_page_specs( @@ -2618,11 +2638,10 @@ static void handle_page_specs(
2618 QTC::TC("qpdf", "qpdf pages encryption password"); 2638 QTC::TC("qpdf", "qpdf pages encryption password");
2619 password = o.encryption_file_password; 2639 password = o.encryption_file_password;
2620 } 2640 }
2621 - if (o.verbose)  
2622 - {  
2623 - std::cout << whoami << ": processing "  
2624 - << page_spec.filename << std::endl;  
2625 - } 2641 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2642 + cout << whoami << ": processing "
  2643 + << page_spec.filename << std::endl;
  2644 + });
2626 PointerHolder<InputSource> is; 2645 PointerHolder<InputSource> is;
2627 ClosedFileInputSource* cis = 0; 2646 ClosedFileInputSource* cis = 0;
2628 if (! o.keep_files_open) 2647 if (! o.keep_files_open)
@@ -2690,12 +2709,11 @@ static void handle_page_specs( @@ -2690,12 +2709,11 @@ static void handle_page_specs(
2690 // without changing their object numbers. This enables other 2709 // without changing their object numbers. This enables other
2691 // things in the original file, such as outlines, to continue to 2710 // things in the original file, such as outlines, to continue to
2692 // work. 2711 // work.
2693 - if (o.verbose)  
2694 - {  
2695 - std::cout << whoami  
2696 - << ": removing unreferenced pages from primary input"  
2697 - << std::endl;  
2698 - } 2712 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2713 + cout << whoami
  2714 + << ": removing unreferenced pages from primary input"
  2715 + << std::endl;
  2716 + });
2699 QPDFPageDocumentHelper dh(pdf); 2717 QPDFPageDocumentHelper dh(pdf);
2700 std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages(); 2718 std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages();
2701 for (std::vector<QPDFPageObjectHelper>::iterator iter = 2719 for (std::vector<QPDFPageObjectHelper>::iterator iter =
@@ -2765,11 +2783,10 @@ static void handle_page_specs( @@ -2765,11 +2783,10 @@ static void handle_page_specs(
2765 { 2783 {
2766 any_page_labels = true; 2784 any_page_labels = true;
2767 } 2785 }
2768 - if (o.verbose)  
2769 - {  
2770 - std::cout << whoami << ": adding pages from "  
2771 - << page_data.filename << std::endl;  
2772 - } 2786 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  2787 + cout << whoami << ": adding pages from "
  2788 + << page_data.filename << std::endl;
  2789 + });
2773 for (std::vector<int>::iterator pageno_iter = 2790 for (std::vector<int>::iterator pageno_iter =
2774 page_data.selected_pages.begin(); 2791 page_data.selected_pages.begin();
2775 pageno_iter != page_data.selected_pages.end(); 2792 pageno_iter != page_data.selected_pages.end();
@@ -3002,15 +3019,15 @@ static void maybe_fix_write_password(int R, QPDFJob&amp; o, std::string&amp; password) @@ -3002,15 +3019,15 @@ static void maybe_fix_write_password(int R, QPDFJob&amp; o, std::string&amp; password)
3002 if (QUtil::utf8_to_pdf_doc(password, encoded)) 3019 if (QUtil::utf8_to_pdf_doc(password, encoded))
3003 { 3020 {
3004 QTC::TC("qpdf", "qpdf auto-encode password"); 3021 QTC::TC("qpdf", "qpdf auto-encode password");
3005 - if (o.verbose)  
3006 - {  
3007 - std::cout 3022 + o.doIfVerbose([&](std::ostream& cout,
  3023 + std::string const& whoami) {
  3024 + cout
3008 << whoami 3025 << whoami
3009 << ": automatically converting Unicode" 3026 << ": automatically converting Unicode"
3010 << " password to single-byte encoding as" 3027 << " password to single-byte encoding as"
3011 << " required for 40-bit or 128-bit" 3028 << " required for 40-bit or 128-bit"
3012 << " encryption" << std::endl; 3029 << " encryption" << std::endl;
3013 - } 3030 + });
3014 password = encoded; 3031 password = encoded;
3015 } 3032 }
3016 else 3033 else
@@ -3088,9 +3105,8 @@ static void set_encryption_options(QPDF&amp; pdf, QPDFJob&amp; o, QPDFWriter&amp; w) @@ -3088,9 +3105,8 @@ static void set_encryption_options(QPDF&amp; pdf, QPDFJob&amp; o, QPDFWriter&amp; w)
3088 { 3105 {
3089 if (! o.allow_weak_crypto) 3106 if (! o.allow_weak_crypto)
3090 { 3107 {
3091 - // Do not set exit code to EXIT_WARNING for this case as  
3092 - // this does not reflect a potential problem with the  
3093 - // input file. 3108 + // Do not set warnings = true for this case as this does
  3109 + // not reflect a potential problem with the input file.
3094 QTC::TC("qpdf", "qpdf weak crypto warning"); 3110 QTC::TC("qpdf", "qpdf weak crypto warning");
3095 std::cerr 3111 std::cerr
3096 << whoami 3112 << whoami
@@ -3247,7 +3263,8 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w) @@ -3247,7 +3263,8 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
3247 } 3263 }
3248 if (o.progress && o.outfilename) 3264 if (o.progress && o.outfilename)
3249 { 3265 {
3250 - w.registerProgressReporter(new ProgressReporter(o.outfilename)); 3266 + w.registerProgressReporter(
  3267 + new ProgressReporter(this->m->whoami, o.outfilename));
3251 } 3268 }
3252 } 3269 }
3253 3270
@@ -3355,18 +3372,15 @@ QPDFJob::doSplitPages(QPDF&amp; pdf, bool&amp; warnings) @@ -3355,18 +3372,15 @@ QPDFJob::doSplitPages(QPDF&amp; pdf, bool&amp; warnings)
3355 std::string outfile = before + page_range + after; 3372 std::string outfile = before + page_range + after;
3356 if (QUtil::same_file(o.infilename, outfile.c_str())) 3373 if (QUtil::same_file(o.infilename, outfile.c_str()))
3357 { 3374 {
3358 - std::cerr << whoami  
3359 - << ": split pages would overwrite input file with "  
3360 - << outfile << std::endl;  
3361 - exit(EXIT_ERROR); // QXXXQ 3375 + throw std::runtime_error(
  3376 + "split pages would overwrite input file with " + outfile);
3362 } 3377 }
3363 QPDFWriter w(outpdf, outfile.c_str()); 3378 QPDFWriter w(outpdf, outfile.c_str());
3364 setWriterOptions(outpdf, w); 3379 setWriterOptions(outpdf, w);
3365 w.write(); 3380 w.write();
3366 - if (o.verbose)  
3367 - {  
3368 - std::cout << whoami << ": wrote file " << outfile << std::endl;  
3369 - } 3381 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  3382 + cout << whoami << ": wrote file " << outfile << std::endl;
  3383 + });
3370 if (outpdf.anyWarnings()) 3384 if (outpdf.anyWarnings())
3371 { 3385 {
3372 warnings = true; 3386 warnings = true;
@@ -3399,10 +3413,11 @@ QPDFJob::writeOutfile(QPDF&amp; pdf) @@ -3399,10 +3413,11 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3399 setWriterOptions(pdf, w); 3413 setWriterOptions(pdf, w);
3400 w.write(); 3414 w.write();
3401 } 3415 }
3402 - if (o.verbose && o.outfilename) 3416 + if (o.outfilename)
3403 { 3417 {
3404 - std::cout << whoami << ": wrote file "  
3405 - << o.outfilename << std::endl; 3418 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
  3419 + cout << whoami << ": wrote file " << o.outfilename << std::endl;
  3420 + });
3406 } 3421 }
3407 if (o.replace_input) 3422 if (o.replace_input)
3408 { 3423 {
@@ -3495,7 +3510,7 @@ QPDFJob::run() @@ -3495,7 +3510,7 @@ QPDFJob::run()
3495 this->m->creates_output = ((o.outfilename != nullptr) || o.replace_input); 3510 this->m->creates_output = ((o.outfilename != nullptr) || o.replace_input);
3496 if (! this->m->creates_output) 3511 if (! this->m->creates_output)
3497 { 3512 {
3498 - do_inspection(pdf, o); 3513 + doInspection(pdf);
3499 } 3514 }
3500 else if (o.split_pages) 3515 else if (o.split_pages)
3501 { 3516 {
qpdf/qpdf.cc
@@ -314,6 +314,7 @@ ArgParser::initOptionTable() @@ -314,6 +314,7 @@ ArgParser::initOptionTable()
314 this->ap.addBare("show-pages", b(&ArgParser::argShowPages)); 314 this->ap.addBare("show-pages", b(&ArgParser::argShowPages));
315 this->ap.addBare("with-images", b(&ArgParser::argWithImages)); 315 this->ap.addBare("with-images", b(&ArgParser::argWithImages));
316 this->ap.addBare("json", b(&ArgParser::argJson)); 316 this->ap.addBare("json", b(&ArgParser::argJson));
  317 + // QXXXQ
317 // The list of selectable top-level keys id duplicated in three 318 // The list of selectable top-level keys id duplicated in three
318 // places: json_schema, do_json, and initOptionTable. 319 // places: json_schema, do_json, and initOptionTable.
319 char const* json_key_choices[] = { 320 char const* json_key_choices[] = {
@@ -2350,6 +2351,7 @@ ArgParser::doFinalChecks() @@ -2350,6 +2351,7 @@ ArgParser::doFinalChecks()
2350 } 2351 }
2351 if (o.optimize_images && (! o.keep_inline_images)) 2352 if (o.optimize_images && (! o.keep_inline_images))
2352 { 2353 {
  2354 + // QXXXQ this is not a check and doesn't belong here
2353 o.externalize_inline_images = true; 2355 o.externalize_inline_images = true;
2354 } 2356 }
2355 if (o.check_requires_password && o.check_is_encrypted) 2357 if (o.check_requires_password && o.check_is_encrypted)
@@ -2384,7 +2386,7 @@ ArgParser::doFinalChecks() @@ -2384,7 +2386,7 @@ ArgParser::doFinalChecks()
2384 usage("--split-pages may not be used when" 2386 usage("--split-pages may not be used when"
2385 " writing to standard output"); 2387 " writing to standard output");
2386 } 2388 }
2387 - if (o.verbose) 2389 + if (o.verbose) // QXXXQ
2388 { 2390 {
2389 usage("--verbose may not be used when" 2391 usage("--verbose may not be used when"
2390 " writing to standard output"); 2392 " writing to standard output");
@@ -2418,6 +2420,7 @@ int realmain(int argc, char* argv[]) @@ -2418,6 +2420,7 @@ int realmain(int argc, char* argv[])
2418 // ArgParser must stay in scope for the duration of qpdf's run as 2420 // ArgParser must stay in scope for the duration of qpdf's run as
2419 // it holds dynamic memory used for argv. 2421 // it holds dynamic memory used for argv.
2420 QPDFJob j; 2422 QPDFJob j;
  2423 + j.setMessagePrefix(whoami);
2421 ArgParser ap(argc, argv, j); 2424 ArgParser ap(argc, argv, j);
2422 2425
2423 bool errors = false; 2426 bool errors = false;
@@ -2439,9 +2442,17 @@ int realmain(int argc, char* argv[]) @@ -2439,9 +2442,17 @@ int realmain(int argc, char* argv[])
2439 { 2442 {
2440 if (! j.suppressWarnings()) 2443 if (! j.suppressWarnings())
2441 { 2444 {
2442 - std::cerr << whoami << ": operation succeeded with warnings;"  
2443 - << " resulting file may have some problems"  
2444 - << std::endl; 2445 + if (j.createsOutput())
  2446 + {
  2447 + std::cerr << whoami << ": operation succeeded with warnings;"
  2448 + << " resulting file may have some problems"
  2449 + << std::endl;
  2450 + }
  2451 + else
  2452 + {
  2453 + std::cerr << whoami << ": operation succeeded with warnings"
  2454 + << std::endl;
  2455 + }
2445 } 2456 }
2446 // Still return with warning code even if warnings were 2457 // Still return with warning code even if warnings were
2447 // suppressed, so leave warnings == true. 2458 // suppressed, so leave warnings == true.
qpdf/qtest/qpdf/append-page-content-damaged-check.out
@@ -5,3 +5,4 @@ checking append-page-content-damaged.pdf @@ -5,3 +5,4 @@ checking append-page-content-damaged.pdf
5 PDF Version: 1.3 5 PDF Version: 1.3
6 File is not encrypted 6 File is not encrypted
7 File is not linearized 7 File is not linearized
  8 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/bad-jpeg-check.out
@@ -4,3 +4,4 @@ File is not encrypted @@ -4,3 +4,4 @@ File is not encrypted
4 File is not linearized 4 File is not linearized
5 WARNING: bad-jpeg.pdf (offset 735): error decoding stream data for object 6 0: Not a JPEG file: starts with 0x77 0x77 5 WARNING: bad-jpeg.pdf (offset 735): error decoding stream data for object 6 0: Not a JPEG file: starts with 0x77 0x77
6 WARNING: bad-jpeg.pdf (offset 735): stream will be re-processed without filtering to avoid data loss 6 WARNING: bad-jpeg.pdf (offset 735): stream will be re-processed without filtering to avoid data loss
  7 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/bad-xref-entry-corrected.out
@@ -12,3 +12,4 @@ WARNING: bad-xref-entry.pdf: Attempting to reconstruct cross-reference table @@ -12,3 +12,4 @@ WARNING: bad-xref-entry.pdf: Attempting to reconstruct cross-reference table
12 5/0: uncompressed; offset = 583 12 5/0: uncompressed; offset = 583
13 6/0: uncompressed; offset = 629 13 6/0: uncompressed; offset = 629
14 7/0: uncompressed; offset = 774 14 7/0: uncompressed; offset = 774
  15 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/content-stream-errors.out
@@ -5,3 +5,4 @@ File is not linearized @@ -5,3 +5,4 @@ File is not linearized
5 WARNING: page object 3 0 stream 7 0 (content, offset 52): parse error while reading object 5 WARNING: page object 3 0 stream 7 0 (content, offset 52): parse error while reading object
6 WARNING: page object 5 0 stream 15 0 (stream data, offset 117): EOF found while reading inline image 6 WARNING: page object 5 0 stream 15 0 (stream data, offset 117): EOF found while reading inline image
7 WARNING: page object 6 0 stream 19 0 (content, offset 53): parse error while reading object 7 WARNING: page object 6 0 stream 19 0 (content, offset 53): parse error while reading object
  8 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/damaged-stream.out
@@ -4,3 +4,4 @@ File is not encrypted @@ -4,3 +4,4 @@ File is not encrypted
4 File is not linearized 4 File is not linearized
5 WARNING: damaged-stream.pdf (offset 426): error decoding stream data for object 5 0: LZWDecoder: bad code received 5 WARNING: damaged-stream.pdf (offset 426): error decoding stream data for object 5 0: LZWDecoder: bad code received
6 WARNING: damaged-stream.pdf (offset 426): stream will be re-processed without filtering to avoid data loss 6 WARNING: damaged-stream.pdf (offset 426): stream will be re-processed without filtering to avoid data loss
  7 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/eof-reading-token.out
@@ -3,3 +3,4 @@ PDF Version: 1.3 @@ -3,3 +3,4 @@ PDF Version: 1.3
3 File is not encrypted 3 File is not encrypted
4 File is not linearized 4 File is not linearized
5 WARNING: eof-reading-token.pdf object stream 12 (object 13 0, offset 5): EOF while reading token 5 WARNING: eof-reading-token.pdf object stream 12 (object 13 0, offset 5): EOF while reading token
  6 +qpdf: operation succeeded with warnings
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 @@ -5,3 +5,4 @@ WARNING: indirect-r-arg.pdf (object 1 0, offset 62): expected dictionary key but
5 PDF Version: 1.3 5 PDF Version: 1.3
6 File is not encrypted 6 File is not encrypted
7 File is not linearized 7 File is not linearized
  8 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/invalid-id-xref.out
@@ -15,3 +15,4 @@ modify annotations: allowed @@ -15,3 +15,4 @@ modify annotations: allowed
15 modify other: not allowed 15 modify other: not allowed
16 modify anything: not allowed 16 modify anything: not allowed
17 File is not linearized 17 File is not linearized
  18 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/linearization-bounds-1.out
@@ -6,3 +6,4 @@ WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, off @@ -6,3 +6,4 @@ WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, off
6 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length 6 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106 7 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 WARNING: error encountered while checking linearization data: linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds 8 WARNING: error encountered while checking linearization data: linearization-bounds-1.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds
  9 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/linearization-bounds-2.out
@@ -6,3 +6,4 @@ WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, off @@ -6,3 +6,4 @@ WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, off
6 WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length 6 WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106 7 WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 WARNING: error encountered while checking linearization data: linearization-bounds-2.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds 8 WARNING: error encountered while checking linearization data: linearization-bounds-2.pdf (linearization hint table, offset 1183): /S (shared object) offset is out of bounds
  9 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/linearization-large-vector-alloc.out
@@ -6,3 +6,4 @@ WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object @@ -6,3 +6,4 @@ WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object
6 WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length 6 WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106 7 WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 WARNING: error encountered while checking linearization data: overflow reading bit stream: wanted = 12556; available = 968 8 WARNING: error encountered while checking linearization data: overflow reading bit stream: wanted = 12556; available = 968
  9 +qpdf: operation succeeded with warnings
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 @@ -6,3 +6,4 @@ WARNING: name-pound-images.pdf (object 3 0, offset 471): name with stray # will
6 WARNING: name-pound-images.pdf (object 3 0, offset 508): name with stray # will not work with PDF >= 1.2 6 WARNING: name-pound-images.pdf (object 3 0, offset 508): name with stray # will not work with PDF >= 1.2
7 WARNING: page object 3 0 stream 4 0 (content, offset 59): name with stray # will not work with PDF >= 1.2 7 WARNING: page object 3 0 stream 4 0 (content, offset 59): name with stray # will not work with PDF >= 1.2
8 WARNING: page object 3 0 stream 4 0 (content, offset 131): name with stray # will not work with PDF >= 1.2 8 WARNING: page object 3 0 stream 4 0 (content, offset 131): name with stray # will not work with PDF >= 1.2
  9 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/no-pages-types.out
@@ -4,3 +4,4 @@ File is not encrypted @@ -4,3 +4,4 @@ File is not encrypted
4 File is not linearized 4 File is not linearized
5 WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Page but is not; overriding 5 WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Page but is not; overriding
6 WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Pages but is not; overriding 6 WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Pages but is not; overriding
  7 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/obj0-check.out
@@ -5,3 +5,4 @@ WARNING: obj0.pdf: Attempting to reconstruct cross-reference table @@ -5,3 +5,4 @@ WARNING: obj0.pdf: Attempting to reconstruct cross-reference table
5 PDF Version: 1.3 5 PDF Version: 1.3
6 File is not encrypted 6 File is not encrypted
7 File is not linearized 7 File is not linearized
  8 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/pages-loop.out
@@ -3,3 +3,4 @@ PDF Version: 1.3 @@ -3,3 +3,4 @@ PDF Version: 1.3
3 File is not encrypted 3 File is not encrypted
4 File is not linearized 4 File is not linearized
5 ERROR: pages-loop.pdf (object 3 0): Loop detected in /Pages structure (getAllPages) 5 ERROR: pages-loop.pdf (object 3 0): Loop detected in /Pages structure (getAllPages)
  6 +qpdf: errors detected
qpdf/qtest/qpdf/show-unfilterable.out
1 -Unable to filter stream data. 1 +WARNING: unfilterable.pdf, stream object 4 0: unable to filter stream data
  2 +qpdf: unable to get object 4,0
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 @@ -7,3 +7,4 @@ WARNING: split-content-stream-errors.pdf (offset 557): stream will be re-process
7 WARNING: page object 3 0 (item index 0 (from 0)): ignoring non-stream in an array of streams 7 WARNING: page object 3 0 (item index 0 (from 0)): ignoring non-stream in an array of streams
8 WARNING: split-content-stream-errors.pdf (offset 557): error decoding stream data for object 6 0: LZWDecoder: bad code received 8 WARNING: split-content-stream-errors.pdf (offset 557): error decoding stream data for object 6 0: LZWDecoder: bad code received
9 ERROR: page 1: content stream (content stream object 6 0): errors while decoding content stream 9 ERROR: page 1: content stream (content stream object 6 0): errors while decoding content stream
  10 +qpdf: errors detected
qpdf/qtest/qpdf/xref-errors.out
@@ -13,3 +13,4 @@ File is not linearized @@ -13,3 +13,4 @@ File is not linearized
13 4/0: uncompressed; offset = 307 13 4/0: uncompressed; offset = 307
14 5/0: uncompressed; offset = 403 14 5/0: uncompressed; offset = 403
15 6/0: uncompressed; offset = 438 15 6/0: uncompressed; offset = 438
  16 +qpdf: operation succeeded with warnings
qpdf/qtest/qpdf/zero-offset.out
@@ -3,3 +3,4 @@ PDF Version: 1.3 @@ -3,3 +3,4 @@ PDF Version: 1.3
3 File is not encrypted 3 File is not encrypted
4 File is not linearized 4 File is not linearized
5 WARNING: zero-offset.pdf (object 6 0): object has offset 0 5 WARNING: zero-offset.pdf (object 6 0): object has offset 0
  6 +qpdf: operation succeeded with warnings