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 33 #include <set>
34 34 #include <map>
35 35 #include <iostream>
  36 +#include <functional>
36 37  
37 38 class QPDFWriter;
38 39  
... ... @@ -42,9 +43,23 @@ class QPDFJob
42 43 QPDF_DLL
43 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 54 QPDF_DLL
46 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 63 QPDF_DLL
49 64 void run();
50 65  
... ... @@ -63,14 +78,13 @@ class QPDFJob
63 78 QPDF_DLL
64 79 bool checkIsEncrypted();
65 80  
66   -
67 81 // Return value is bitwise OR of values from qpdf_encryption_status_e
68 82 QPDF_DLL
69 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 87 public:
73   -
74 88 QPDF_DLL
75 89 static JSON json_schema(std::set<std::string>* keys = 0);
76 90 QPDF_DLL
... ... @@ -165,6 +179,10 @@ class QPDFJob
165 179 void setWriterOptions(QPDF& pdf, QPDFWriter& w);
166 180 void doSplitPages(QPDF& pdf, bool& warnings);
167 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 187 enum remove_unref_e { re_auto, re_yes, re_no };
170 188  
... ... @@ -295,6 +313,7 @@ class QPDFJob
295 313 Members();
296 314 Members(Members const&) = delete;
297 315  
  316 + std::string whoami;
298 317 bool warnings;
299 318 bool creates_output;
300 319 std::ostream* out_stream;
... ...
libqpdf/QPDFJob.cc
... ... @@ -35,8 +35,6 @@
35 35 #include <qpdf/QIntC.hh>
36 36  
37 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 38 static char const* whoami = "qpdf";
41 39 // /QXXXQ
42 40  
... ... @@ -82,7 +80,8 @@ namespace
82 80 class ProgressReporter: public QPDFWriter::ProgressReporter
83 81 {
84 82 public:
85   - ProgressReporter(char const* filename) :
  83 + ProgressReporter(std::string const& whoami, char const* filename) :
  84 + whoami(whoami),
86 85 filename(filename)
87 86 {
88 87 }
... ... @@ -92,6 +91,7 @@ namespace
92 91  
93 92 virtual void reportProgress(int);
94 93 private:
  94 + std::string whoami;
95 95 std::string filename;
96 96 };
97 97 }
... ... @@ -133,6 +133,7 @@ ProgressReporter::reportProgress(int percentage)
133 133  
134 134  
135 135 QPDFJob::Members::Members() :
  136 + whoami("qpdf"),
136 137 warnings(false),
137 138 creates_output(false),
138 139 out_stream(&std::cout),
... ... @@ -245,12 +246,28 @@ QPDFJob::QPDFJob() :
245 246 }
246 247  
247 248 void
  249 +QPDFJob::setMessagePrefix(std::string const& whoami)
  250 +{
  251 + this->m->whoami = whoami;
  252 +}
  253 +
  254 +void
248 255 QPDFJob::setOutputStreams(std::ostream* out, std::ostream* err)
249 256 {
250 257 this->m->out_stream = out ? out : &std::cout;
251 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 271 static void parse_version(std::string const& full_version_string,
255 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 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 340 // Extract /P from /Encrypt
322 341 int R = 0;
323 342 int P = 0;
... ... @@ -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 406 // Code below may set okay to false but not to true.
386 407 // We assume okay until we prove otherwise but may
387 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 420 << pdf.getExtensionLevel();
400 421 }
401 422 std::cout << std::endl;
402   - show_encryption(pdf, o);
  423 + showEncryption(pdf);
403 424 if (pdf.isLinearized())
404 425 {
405 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 464 catch (QPDFExc& e)
444 465 {
445 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 475 std::cerr << "ERROR: " << e.what() << std::endl;
454 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 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 500 QPDFObjectHandle obj;
480 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 506 {
486 507 obj = pdf.getObjectByID(o.show_obj, o.show_gen);
487 508 }
  509 + bool error = false;
488 510 if (obj.isStream())
489 511 {
490 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 516 (! obj.pipeStreamData(0, 0, qpdf_dl_all)))
495 517 {
496 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 522 else
502 523 {
... ... @@ -519,6 +540,11 @@ static void do_show_obj(QPDF&amp; pdf, QPDFJob&amp; o, int&amp; exit_code)
519 540 {
520 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 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 607 std::cout << key << " -> "
582 608 << efoh->getEmbeddedFileStream().getObjGen()
583 609 << std::endl;
584   - if (o.verbose)
585   - {
  610 + o.doIfVerbose([&](std::ostream& cout, std::string const& whoami) {
586 611 auto desc = efoh->getDescription();
587 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 617 << std::endl;
593   - std::cout << " all names:" << std::endl;
  618 + cout << " all names:" << std::endl;
594 619 for (auto const& i2: efoh->getFilenames())
595 620 {
596   - std::cout << " " << i2.first << " -> " << i2.second
  621 + cout << " " << i2.first << " -> " << i2.second
597 622 << std::endl;
598 623 }
599   - std::cout << " all data streams:" << std::endl;
  624 + cout << " all data streams:" << std::endl;
600 625 for (auto i2: efoh->getEmbeddedFileStreams().ditems())
601 626 {
602   - std::cout << " " << i2.first << " -> "
  627 + cout << " " << i2.first << " -> "
603 628 << i2.second.getObjGen()
604 629 << std::endl;
605 630 }
606   - }
  631 + });
607 632 }
608 633 }
609 634 else
... ... @@ -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 642 QPDFEmbeddedFileDocumentHelper efdh(pdf);
618 643 auto fs = efdh.getEmbeddedFile(o.attachment_to_show);
619 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 649 auto efs = fs->getEmbeddedFileStream();
627 650 QUtil::binary_stdout();
... ... @@ -1149,8 +1172,9 @@ QPDFJob::json_schema(std::set&lt;std::string&gt;* keys)
1149 1172  
1150 1173 bool all_keys = ((keys == 0) || keys->empty());
1151 1174  
  1175 + // QXXXQ
1152 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 1178 if (all_keys || keys->count("objects"))
1155 1179 {
1156 1180 schema.addDictionaryMember(
... ... @@ -1456,8 +1480,10 @@ QPDFJob::json_schema(std::set&lt;std::string&gt;* keys)
1456 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 1487 JSON j = JSON::makeDictionary();
1462 1488 // This version is updated every time a non-backward-compatible
1463 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 1512 "decodelevel", JSON::makeString(decode_level_str));
1487 1513  
1488 1514 bool all_keys = o.json_keys.empty();
  1515 + // QXXXQ
1489 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 1518 if (all_keys || o.json_keys.count("objects"))
1492 1519 {
1493 1520 do_json_objects(pdf, o, j);
... ... @@ -1527,9 +1554,9 @@ static void do_json(QPDF&amp; pdf, QPDFJob&amp; o)
1527 1554 std::list<std::string> errors;
1528 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 1560 https://github.com/qpdf/qpdf/issues/new\n\
1534 1561 ideally with the file that caused the error and the output below. Thanks!\n\
1535 1562 \n";
... ... @@ -1543,16 +1570,17 @@ ideally with the file that caused the error and the output below. Thanks!\n\
1543 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 1577 if (o.check)
1550 1578 {
1551   - do_check(pdf, o, exit_code);
  1579 + doCheck(pdf);
1552 1580 }
1553 1581 if (o.json)
1554 1582 {
1555   - do_json(pdf, o);
  1583 + doJSON(pdf);
1556 1584 }
1557 1585 if (o.show_npages)
1558 1586 {
... ... @@ -1562,7 +1590,7 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1562 1590 }
1563 1591 if (o.show_encryption)
1564 1592 {
1565   - show_encryption(pdf, o);
  1593 + showEncryption(pdf);
1566 1594 }
1567 1595 if (o.check_linearization)
1568 1596 {
... ... @@ -1571,9 +1599,9 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1571 1599 std::cout << o.infilename << ": no linearization errors"
1572 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 1607 if (o.show_linearization)
... ... @@ -1594,7 +1622,7 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1594 1622 }
1595 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 1627 if (o.show_pages)
1600 1628 {
... ... @@ -1606,17 +1634,11 @@ static void do_inspection(QPDF&amp; pdf, QPDFJob&amp; o)
1606 1634 }
1607 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 1659 QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace");
1638 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 1670 return result;
1647 1671 }
... ... @@ -1649,11 +1673,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1649 1673 if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8)))
1650 1674 {
1651 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 1684 return result;
1659 1685 }
... ... @@ -1700,11 +1726,13 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1700 1726 else
1701 1727 {
1702 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 1737 return result;
1710 1738 }
... ... @@ -1713,12 +1741,14 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
1713 1741 ((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area)))
1714 1742 {
1715 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 1753 return result;
1724 1754 }
... ... @@ -1733,13 +1763,12 @@ ImageOptimizer::evaluate(std::string const&amp; description)
1733 1763 if (! image.pipeStreamData(0, 0, qpdf_dl_specialized, true))
1734 1764 {
1735 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 1772 return false;
1744 1773 }
1745 1774 Pl_Discard d;
... ... @@ -1758,21 +1787,19 @@ ImageOptimizer::evaluate(std::string const&amp; description)
1758 1787 if (c.getCount() >= orig_length)
1759 1788 {
1760 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 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 1803 return true;
1777 1804 }
1778 1805  
... ... @@ -1889,13 +1916,15 @@ static PointerHolder&lt;QPDF&gt; do_process(
1889 1916 throw e;
1890 1917 }
1891 1918 }
1892   - if ((! warned) && o.verbose)
  1919 + if (! warned)
1893 1920 {
1894 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 1930 // Should not be reachable
... ... @@ -2032,10 +2061,9 @@ static void do_under_overlay_for_page(
2032 2061 iter != pagenos[pageno].end(); ++iter)
2033 2062 {
2034 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 2067 auto from_page = pages.at(QIntC::to_size(from_pageno - 1));
2040 2068 if (0 == fo.count(from_pageno))
2041 2069 {
... ... @@ -2116,16 +2144,14 @@ QPDFJob::handleUnderOverlay(QPDF&amp; pdf)
2116 2144 QPDFPageDocumentHelper main_pdh(pdf);
2117 2145 std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages();
2118 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 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 2155 do_under_overlay_for_page(pdf, o, o.underlay, underlay_pagenos, i,
2130 2156 underlay_fo, upages, main_pages.at(i),
2131 2157 true);
... ... @@ -2174,12 +2200,11 @@ QPDFJob::addAttachments(QPDF&amp; pdf)
2174 2200 }
2175 2201  
2176 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 2210 if (! duplicated_keys.empty())
... ... @@ -2210,11 +2235,10 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2210 2235 std::vector<std::string> duplicates;
2211 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 2242 auto other = processFile(
2219 2243 to_copy.path.c_str(), to_copy.password.c_str());
2220 2244 QPDFEmbeddedFileDocumentHelper other_efdh(*other);
... ... @@ -2233,11 +2257,11 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2233 2257 iter.second->getObjectHandle());
2234 2258 efdh.replaceEmbeddedFile(
2235 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 2385 {
2362 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 2391 ": removed attachment " << key << std::endl;
2368   - }
  2392 + });
2369 2393 }
2370 2394 else
2371 2395 {
... ... @@ -2407,11 +2431,10 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2407 2431 std::set<QPDFObjGen> resources_seen; // shared resources detection
2408 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 2439 std::list<QPDFObjectHandle> queue;
2417 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 2456 if (dict.hasKey("/Resources"))
2434 2457 {
2435 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 2465 return true;
2443 2466 }
2444 2467 int n = kids.getArrayNItems();
... ... @@ -2457,15 +2480,15 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2457 2480 if (resources_seen.count(resources_og))
2458 2481 {
2459 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 2492 return true;
2470 2493 }
2471 2494 resources_seen.insert(resources_og);
... ... @@ -2479,15 +2502,15 @@ static bool should_remove_unreferenced_resources(QPDF&amp; pdf, QPDFJob&amp; o)
2479 2502 if (resources_seen.count(xobject_og))
2480 2503 {
2481 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 2514 return true;
2492 2515 }
2493 2516 resources_seen.insert(xobject_og);
... ... @@ -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 2541 return false;
2520 2542 }
2521 2543  
... ... @@ -2570,20 +2592,18 @@ static void handle_page_specs(
2570 2592 if (filenames.size() > o.keep_files_open_threshold)
2571 2593 {
2572 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 2599 o.keep_files_open = false;
2579 2600 }
2580 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 2607 o.keep_files_open = true;
2588 2608 QTC::TC("qpdf", "qpdf don't disable keep files open");
2589 2609 }
... ... @@ -2618,11 +2638,10 @@ static void handle_page_specs(
2618 2638 QTC::TC("qpdf", "qpdf pages encryption password");
2619 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 2645 PointerHolder<InputSource> is;
2627 2646 ClosedFileInputSource* cis = 0;
2628 2647 if (! o.keep_files_open)
... ... @@ -2690,12 +2709,11 @@ static void handle_page_specs(
2690 2709 // without changing their object numbers. This enables other
2691 2710 // things in the original file, such as outlines, to continue to
2692 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 2717 QPDFPageDocumentHelper dh(pdf);
2700 2718 std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages();
2701 2719 for (std::vector<QPDFPageObjectHelper>::iterator iter =
... ... @@ -2765,11 +2783,10 @@ static void handle_page_specs(
2765 2783 {
2766 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 2790 for (std::vector<int>::iterator pageno_iter =
2774 2791 page_data.selected_pages.begin();
2775 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 3019 if (QUtil::utf8_to_pdf_doc(password, encoded))
3003 3020 {
3004 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 3025 << whoami
3009 3026 << ": automatically converting Unicode"
3010 3027 << " password to single-byte encoding as"
3011 3028 << " required for 40-bit or 128-bit"
3012 3029 << " encryption" << std::endl;
3013   - }
  3030 + });
3014 3031 password = encoded;
3015 3032 }
3016 3033 else
... ... @@ -3088,9 +3105,8 @@ static void set_encryption_options(QPDF&amp; pdf, QPDFJob&amp; o, QPDFWriter&amp; w)
3088 3105 {
3089 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 3110 QTC::TC("qpdf", "qpdf weak crypto warning");
3095 3111 std::cerr
3096 3112 << whoami
... ... @@ -3247,7 +3263,8 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
3247 3263 }
3248 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 3372 std::string outfile = before + page_range + after;
3356 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 3378 QPDFWriter w(outpdf, outfile.c_str());
3364 3379 setWriterOptions(outpdf, w);
3365 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 3384 if (outpdf.anyWarnings())
3371 3385 {
3372 3386 warnings = true;
... ... @@ -3399,10 +3413,11 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3399 3413 setWriterOptions(pdf, w);
3400 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 3422 if (o.replace_input)
3408 3423 {
... ... @@ -3495,7 +3510,7 @@ QPDFJob::run()
3495 3510 this->m->creates_output = ((o.outfilename != nullptr) || o.replace_input);
3496 3511 if (! this->m->creates_output)
3497 3512 {
3498   - do_inspection(pdf, o);
  3513 + doInspection(pdf);
3499 3514 }
3500 3515 else if (o.split_pages)
3501 3516 {
... ...
qpdf/qpdf.cc
... ... @@ -314,6 +314,7 @@ ArgParser::initOptionTable()
314 314 this->ap.addBare("show-pages", b(&ArgParser::argShowPages));
315 315 this->ap.addBare("with-images", b(&ArgParser::argWithImages));
316 316 this->ap.addBare("json", b(&ArgParser::argJson));
  317 + // QXXXQ
317 318 // The list of selectable top-level keys id duplicated in three
318 319 // places: json_schema, do_json, and initOptionTable.
319 320 char const* json_key_choices[] = {
... ... @@ -2350,6 +2351,7 @@ ArgParser::doFinalChecks()
2350 2351 }
2351 2352 if (o.optimize_images && (! o.keep_inline_images))
2352 2353 {
  2354 + // QXXXQ this is not a check and doesn't belong here
2353 2355 o.externalize_inline_images = true;
2354 2356 }
2355 2357 if (o.check_requires_password && o.check_is_encrypted)
... ... @@ -2384,7 +2386,7 @@ ArgParser::doFinalChecks()
2384 2386 usage("--split-pages may not be used when"
2385 2387 " writing to standard output");
2386 2388 }
2387   - if (o.verbose)
  2389 + if (o.verbose) // QXXXQ
2388 2390 {
2389 2391 usage("--verbose may not be used when"
2390 2392 " writing to standard output");
... ... @@ -2418,6 +2420,7 @@ int realmain(int argc, char* argv[])
2418 2420 // ArgParser must stay in scope for the duration of qpdf's run as
2419 2421 // it holds dynamic memory used for argv.
2420 2422 QPDFJob j;
  2423 + j.setMessagePrefix(whoami);
2421 2424 ArgParser ap(argc, argv, j);
2422 2425  
2423 2426 bool errors = false;
... ... @@ -2439,9 +2442,17 @@ int realmain(int argc, char* argv[])
2439 2442 {
2440 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 2457 // Still return with warning code even if warnings were
2447 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 5 PDF Version: 1.3
6 6 File is not encrypted
7 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 4 File is not linearized
5 5 WARNING: bad-jpeg.pdf (offset 735): error decoding stream data for object 6 0: Not a JPEG file: starts with 0x77 0x77
6 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 12 5/0: uncompressed; offset = 583
13 13 6/0: uncompressed; offset = 629
14 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 5 WARNING: page object 3 0 stream 7 0 (content, offset 52): parse error while reading object
6 6 WARNING: page object 5 0 stream 15 0 (stream data, offset 117): EOF found while reading inline image
7 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 4 File is not linearized
5 5 WARNING: damaged-stream.pdf (offset 426): error decoding stream data for object 5 0: LZWDecoder: bad code received
6 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 File is not encrypted
4 4 File is not linearized
5 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 5 PDF Version: 1.3
6 6 File is not encrypted
7 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 15 modify other: not allowed
16 16 modify anything: not allowed
17 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 6 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 7 WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 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 6 WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 7 WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 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 6 WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): attempting to recover stream length
7 7 WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, offset 1183): recovered stream length: 106
8 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 6 WARNING: name-pound-images.pdf (object 3 0, offset 508): name with stray # will not work with PDF >= 1.2
7 7 WARNING: page object 3 0 stream 4 0 (content, offset 59): name with stray # will not work with PDF >= 1.2
8 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 4 File is not linearized
5 5 WARNING: no-pages-types.pdf (page tree node, offset 307): /Type key should be /Page but is not; overriding
6 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 5 PDF Version: 1.3
6 6 File is not encrypted
7 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 File is not encrypted
4 4 File is not linearized
5 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 7 WARNING: page object 3 0 (item index 0 (from 0)): ignoring non-stream in an array of streams
8 8 WARNING: split-content-stream-errors.pdf (offset 557): error decoding stream data for object 6 0: LZWDecoder: bad code received
9 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 13 4/0: uncompressed; offset = 307
14 14 5/0: uncompressed; offset = 403
15 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 File is not encrypted
4 4 File is not linearized
5 5 WARNING: zero-offset.pdf (object 6 0): object has offset 0
  6 +qpdf: operation succeeded with warnings
... ...