Commit 641e92c6a7662a01f488947c3791f3b77e85517f

Authored by Jay Berkenbilt
1 parent f1f71196

QPDF, QPDFJob: use QPDFLogger instead of custom output streams

ChangeLog
1 2022-06-05 Jay Berkenbilt <ejb@ql.org> 1 2022-06-05 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * QPDFJob: API breaking change: QPDFJob::doIfVerbose passes a
  4 + Pipeline& rather than a std::ostream& to the the callback
  5 + function.
  6 +
3 * Add integer types to pipeline's operator<<: short, int, long, 7 * Add integer types to pipeline's operator<<: short, int, long,
4 long long, unsigned short, unsigned int, unsigned long, unsigned 8 long long, unsigned short, unsigned int, unsigned long, unsigned
5 long long. 9 long long.
README-maintainer
@@ -512,6 +512,28 @@ to the owner of the parent directory source tree. @@ -512,6 +512,28 @@ to the owner of the parent directory source tree.
512 Note: this will leave some extra files (like .bash_history) in the 512 Note: this will leave some extra files (like .bash_history) in the
513 parent directory of the source tree. You will want to clean those up. 513 parent directory of the source tree. You will want to clean those up.
514 514
  515 +DEPRECATION
  516 +
  517 +This is a reminder of how to use and test deprecation.
  518 +
  519 +To temporarily disable deprecation warnings for testing:
  520 +
  521 +#ifdef _MSC_VER
  522 +# pragma warning(disable : 4996)
  523 +#endif
  524 +#if (defined(__GNUC__) || defined(__clang__))
  525 +# pragma GCC diagnostic push
  526 +# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  527 +#endif
  528 + // Do deprecated thing here
  529 +#if (defined(__GNUC__) || defined(__clang__))
  530 +# pragma GCC diagnostic pop
  531 +#endif
  532 +
  533 +To declare something as deprecated:
  534 +
  535 +[[deprecated("explanation")]]
  536 +
515 537
516 LOCAL WINDOWS TESTING PROCEDURE 538 LOCAL WINDOWS TESTING PROCEDURE
517 539
include/qpdf/QPDF.hh
@@ -48,6 +48,7 @@ @@ -48,6 +48,7 @@
48 class QPDF_Stream; 48 class QPDF_Stream;
49 class BitStream; 49 class BitStream;
50 class BitWriter; 50 class BitWriter;
  51 +class QPDFLogger;
51 52
52 class QPDF 53 class QPDF
53 { 54 {
@@ -209,20 +210,30 @@ class QPDF @@ -209,20 +210,30 @@ class QPDF
209 210
210 // Parameter settings 211 // Parameter settings
211 212
212 - // By default, warning messages are issued to std::cerr and output  
213 - // messages printed by certain check calls are issued to  
214 - // std::cout. This method allows you to specify alternative  
215 - // streams for this purpose. Note that no normal QPDF operations  
216 - // generate output to std::cout, so for applications that just  
217 - // wish to avoid creating output and don't call any check  
218 - // functions, calling setSuppressWarnings(true) is sufficient.  
219 - // Applications that wish to present check or warning information  
220 - // to users may replace the output and error streams to capture  
221 - // the output and errors for other use. A null value for either  
222 - // stream will cause QPDF to use std::cout or std::cerr as  
223 - // appropriate.  
224 - QPDF_DLL  
225 - void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream); 213 + // To capture or redirect output, configure the logger returned by
  214 + // getLogger(). By default, all QPDF and QPDFJob objects share the
  215 + // global logger. If you need a private logger for some reason,
  216 + // pass a new one to setLogger(). See comments in QPDFLogger.hh
  217 + // for details on configuring the logger.
  218 + //
  219 + // Note that no normal QPDF operations generate output to standard
  220 + // output, so for applications that just wish to avoid creating
  221 + // output for warnings and don't call any check functions, calling
  222 + // setSuppressWarnings(true) is sufficient.
  223 + QPDF_DLL
  224 + std::shared_ptr<QPDFLogger> getLogger();
  225 + QPDF_DLL
  226 + void setLogger(std::shared_ptr<QPDFLogger>);
  227 +
  228 + // This deprecated method is the old way to capture output, but it
  229 + // didn't capture all output. See comments above for getLogger and
  230 + // setLogger. This will be removed in QPDF 12. For now, it
  231 + // configures a private logger, separating this object from the
  232 + // default logger, and calls setOutputStreams on that logger. See
  233 + // QPDFLogger.hh for additional details.
  234 + [[deprecated(
  235 + "configure logger from getLogger() or call setLogger()")]] QPDF_DLL void
  236 + setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
226 237
227 // If true, ignore any cross-reference streams in a hybrid file 238 // If true, ignore any cross-reference streams in a hybrid file
228 // (one that contains both cross-reference streams and 239 // (one that contains both cross-reference streams and
@@ -618,18 +629,17 @@ class QPDF @@ -618,18 +629,17 @@ class QPDF
618 629
619 // Performs various sanity checks on a linearized file. Return 630 // Performs various sanity checks on a linearized file. Return
620 // true if no errors or warnings. Otherwise, return false and 631 // true if no errors or warnings. Otherwise, return false and
621 - // output errors and warnings to std::cout or the output stream  
622 - // specified in a call to setOutputStreams. It is recommended for  
623 - // linearization errors to be treated as warnings. 632 + // output errors and warnings to the default output stream
  633 + // (std::cout or whatever is configured in the logger). It is
  634 + // recommended for linearization errors to be treated as warnings.
624 QPDF_DLL 635 QPDF_DLL
625 bool checkLinearization(); 636 bool checkLinearization();
626 637
627 // Calls checkLinearization() and, if possible, prints normalized 638 // Calls checkLinearization() and, if possible, prints normalized
628 - // contents of some of the hints tables to std::cout or the output  
629 - // stream specified in a call to setOutputStreams. Normalization  
630 - // includes adding min values to delta values and adjusting  
631 - // offsets based on the location and size of the primary hint  
632 - // stream. 639 + // contents of some of the hints tables to the default output
  640 + // stream. Normalization includes adding min values to delta
  641 + // values and adjusting offsets based on the location and size of
  642 + // the primary hint stream.
633 QPDF_DLL 643 QPDF_DLL
634 void showLinearizationData(); 644 void showLinearizationData();
635 645
@@ -1661,6 +1671,7 @@ class QPDF @@ -1661,6 +1671,7 @@ class QPDF
1661 Members(); 1671 Members();
1662 Members(Members const&) = delete; 1672 Members(Members const&) = delete;
1663 1673
  1674 + std::shared_ptr<QPDFLogger> log;
1664 unsigned long long unique_id; 1675 unsigned long long unique_id;
1665 QPDFTokenizer tokenizer; 1676 QPDFTokenizer tokenizer;
1666 std::shared_ptr<InputSource> file; 1677 std::shared_ptr<InputSource> file;
@@ -1668,8 +1679,6 @@ class QPDF @@ -1668,8 +1679,6 @@ class QPDF
1668 bool provided_password_is_hex_key; 1679 bool provided_password_is_hex_key;
1669 bool ignore_xref_streams; 1680 bool ignore_xref_streams;
1670 bool suppress_warnings; 1681 bool suppress_warnings;
1671 - std::ostream* out_stream;  
1672 - std::ostream* err_stream;  
1673 bool attempt_recovery; 1682 bool attempt_recovery;
1674 std::shared_ptr<EncryptionParameters> encp; 1683 std::shared_ptr<EncryptionParameters> encp;
1675 std::string pdf_version; 1684 std::string pdf_version;
include/qpdf/QPDFJob.hh
@@ -41,6 +41,7 @@ @@ -41,6 +41,7 @@
41 41
42 class QPDFWriter; 42 class QPDFWriter;
43 class Pipeline; 43 class Pipeline;
  44 +class QPDFLogger;
44 45
45 class QPDFJob 46 class QPDFJob
46 { 47 {
@@ -108,10 +109,28 @@ class QPDFJob @@ -108,10 +109,28 @@ class QPDFJob
108 QPDF_DLL 109 QPDF_DLL
109 void setMessagePrefix(std::string const&); 110 void setMessagePrefix(std::string const&);
110 111
111 - // Override streams that errors and output go to. Defaults are  
112 - // std::cout and std::cerr. Pass nullptr to use the default. 112 + // To capture or redirect output, configure the logger returned by
  113 + // getLogger(). By default, all QPDF and QPDFJob objects share the
  114 + // global logger. If you need a private logger for some reason,
  115 + // pass a new one to setLogger(). See comments in QPDFLogger.hh
  116 + // for details on configuring the logger.
  117 + //
  118 + // If you set a custom logger here, the logger will be passed to
  119 + // all subsequent QPDF objects created by this QPDFJob object.
  120 + QPDF_DLL
  121 + std::shared_ptr<QPDFLogger> getLogger();
113 QPDF_DLL 122 QPDF_DLL
114 - void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream); 123 + void setLogger(std::shared_ptr<QPDFLogger>);
  124 +
  125 + // This deprecated method is the old way to capture output, but it
  126 + // didn't capture all output. See comments above for getLogger and
  127 + // setLogger. This will be removed in QPDF 12. For now, it
  128 + // configures a private logger, separating this object from the
  129 + // default logger, and calls setOutputStreams on that logger. See
  130 + // QPDFLogger.hh for additional details.
  131 + [[deprecated(
  132 + "configure logger from getLogger() or call setLogger()")]] QPDF_DLL void
  133 + setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
115 134
116 // Check to make sure no contradictory options have been 135 // Check to make sure no contradictory options have been
117 // specified. This is called automatically after initializing from 136 // specified. This is called automatically after initializing from
@@ -393,8 +412,8 @@ class QPDFJob @@ -393,8 +412,8 @@ class QPDFJob
393 // If in verbose mode, call the given function, passing in the 412 // If in verbose mode, call the given function, passing in the
394 // output stream and message prefix. 413 // output stream and message prefix.
395 QPDF_DLL 414 QPDF_DLL
396 - void doIfVerbose(  
397 - std::function<void(std::ostream&, std::string const& prefix)> fn); 415 + void
  416 + doIfVerbose(std::function<void(Pipeline&, std::string const& prefix)> fn);
398 417
399 // Provide a string that is the help information ("schema" for the 418 // Provide a string that is the help information ("schema" for the
400 // qpdf-specific JSON object) for version 1 of the JSON output. 419 // qpdf-specific JSON object) for version 1 of the JSON output.
@@ -548,10 +567,9 @@ class QPDFJob @@ -548,10 +567,9 @@ class QPDFJob
548 Members(); 567 Members();
549 Members(Members const&) = delete; 568 Members(Members const&) = delete;
550 569
  570 + std::shared_ptr<QPDFLogger> log;
551 std::string message_prefix; 571 std::string message_prefix;
552 bool warnings; 572 bool warnings;
553 - std::ostream* cout;  
554 - std::ostream* cerr;  
555 unsigned long encryption_status; 573 unsigned long encryption_status;
556 bool verbose; 574 bool verbose;
557 std::shared_ptr<char> password; 575 std::shared_ptr<char> password;
libqpdf/QPDF.cc
@@ -18,14 +18,15 @@ @@ -18,14 +18,15 @@
18 #include <qpdf/OffsetInputSource.hh> 18 #include <qpdf/OffsetInputSource.hh>
19 #include <qpdf/Pipeline.hh> 19 #include <qpdf/Pipeline.hh>
20 #include <qpdf/Pl_Discard.hh> 20 #include <qpdf/Pl_Discard.hh>
21 -#include <qpdf/QTC.hh>  
22 -#include <qpdf/QUtil.hh>  
23 - 21 +#include <qpdf/Pl_OStream.hh>
24 #include <qpdf/QPDFExc.hh> 22 #include <qpdf/QPDFExc.hh>
  23 +#include <qpdf/QPDFLogger.hh>
25 #include <qpdf/QPDF_Array.hh> 24 #include <qpdf/QPDF_Array.hh>
26 #include <qpdf/QPDF_Dictionary.hh> 25 #include <qpdf/QPDF_Dictionary.hh>
27 #include <qpdf/QPDF_Null.hh> 26 #include <qpdf/QPDF_Null.hh>
28 #include <qpdf/QPDF_Stream.hh> 27 #include <qpdf/QPDF_Stream.hh>
  28 +#include <qpdf/QTC.hh>
  29 +#include <qpdf/QUtil.hh>
29 30
30 // This must be a fixed value. This API returns a const reference to 31 // This must be a fixed value. This API returns a const reference to
31 // it, and the C API relies on its being static as well. 32 // it, and the C API relies on its being static as well.
@@ -212,13 +213,12 @@ QPDF::EncryptionParameters::EncryptionParameters() : @@ -212,13 +213,12 @@ QPDF::EncryptionParameters::EncryptionParameters() :
212 } 213 }
213 214
214 QPDF::Members::Members() : 215 QPDF::Members::Members() :
  216 + log(QPDFLogger::defaultLogger()),
215 unique_id(0), 217 unique_id(0),
216 file(new InvalidInputSource()), 218 file(new InvalidInputSource()),
217 provided_password_is_hex_key(false), 219 provided_password_is_hex_key(false),
218 ignore_xref_streams(false), 220 ignore_xref_streams(false),
219 suppress_warnings(false), 221 suppress_warnings(false),
220 - out_stream(&std::cout),  
221 - err_stream(&std::cerr),  
222 attempt_recovery(true), 222 attempt_recovery(true),
223 encp(new EncryptionParameters), 223 encp(new EncryptionParameters),
224 pushed_inherited_attributes_to_pages(false), 224 pushed_inherited_attributes_to_pages(false),
@@ -339,11 +339,23 @@ QPDF::setIgnoreXRefStreams(bool val) @@ -339,11 +339,23 @@ QPDF::setIgnoreXRefStreams(bool val)
339 this->m->ignore_xref_streams = val; 339 this->m->ignore_xref_streams = val;
340 } 340 }
341 341
  342 +std::shared_ptr<QPDFLogger>
  343 +QPDF::getLogger()
  344 +{
  345 + return this->m->log;
  346 +}
  347 +
  348 +void
  349 +QPDF::setLogger(std::shared_ptr<QPDFLogger> l)
  350 +{
  351 + this->m->log = l;
  352 +}
  353 +
342 void 354 void
343 QPDF::setOutputStreams(std::ostream* out, std::ostream* err) 355 QPDF::setOutputStreams(std::ostream* out, std::ostream* err)
344 { 356 {
345 - this->m->out_stream = out ? out : &std::cout;  
346 - this->m->err_stream = err ? err : &std::cerr; 357 + setLogger(std::make_shared<QPDFLogger>());
  358 + this->m->log->setOutputStreams(out, err);
347 } 359 }
348 360
349 void 361 void
@@ -533,8 +545,8 @@ QPDF::warn(QPDFExc const&amp; e) @@ -533,8 +545,8 @@ QPDF::warn(QPDFExc const&amp; e)
533 { 545 {
534 this->m->warnings.push_back(e); 546 this->m->warnings.push_back(e);
535 if (!this->m->suppress_warnings) { 547 if (!this->m->suppress_warnings) {
536 - *this->m->err_stream << "WARNING: " << this->m->warnings.back().what()  
537 - << std::endl; 548 + *this->m->log->getWarn()
  549 + << "WARNING: " << this->m->warnings.back().what() << "\n";
538 } 550 }
539 } 551 }
540 552
@@ -1345,18 +1357,18 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2, bool overwrite) @@ -1345,18 +1357,18 @@ QPDF::insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2, bool overwrite)
1345 void 1357 void
1346 QPDF::showXRefTable() 1358 QPDF::showXRefTable()
1347 { 1359 {
  1360 + auto& cout = *this->m->log->getInfo();
1348 for (auto const& iter: this->m->xref_table) { 1361 for (auto const& iter: this->m->xref_table) {
1349 QPDFObjGen const& og = iter.first; 1362 QPDFObjGen const& og = iter.first;
1350 QPDFXRefEntry const& entry = iter.second; 1363 QPDFXRefEntry const& entry = iter.second;
1351 - *this->m->out_stream << og.getObj() << "/" << og.getGen() << ": "; 1364 + cout << og.getObj() << "/" << og.getGen() << ": ";
1352 switch (entry.getType()) { 1365 switch (entry.getType()) {
1353 case 1: 1366 case 1:
1354 - *this->m->out_stream << "uncompressed; offset = "  
1355 - << entry.getOffset(); 1367 + cout << "uncompressed; offset = " << entry.getOffset();
1356 break; 1368 break;
1357 1369
1358 case 2: 1370 case 2:
1359 - *this->m->out_stream 1371 + *this->m->log->getInfo()
1360 << "compressed; stream = " << entry.getObjStreamNumber() 1372 << "compressed; stream = " << entry.getObjStreamNumber()
1361 << ", index = " << entry.getObjStreamIndex(); 1373 << ", index = " << entry.getObjStreamIndex();
1362 break; 1374 break;
@@ -1366,7 +1378,7 @@ QPDF::showXRefTable() @@ -1366,7 +1378,7 @@ QPDF::showXRefTable()
1366 " showing xref_table"); 1378 " showing xref_table");
1367 break; 1379 break;
1368 } 1380 }
1369 - *this->m->out_stream << std::endl; 1381 + this->m->log->info("\n");
1370 } 1382 }
1371 } 1383 }
1372 1384
libqpdf/QPDFJob.cc
@@ -17,9 +17,6 @@ @@ -17,9 +17,6 @@
17 #include <qpdf/Pl_OStream.hh> 17 #include <qpdf/Pl_OStream.hh>
18 #include <qpdf/Pl_StdioFile.hh> 18 #include <qpdf/Pl_StdioFile.hh>
19 #include <qpdf/Pl_String.hh> 19 #include <qpdf/Pl_String.hh>
20 -#include <qpdf/QTC.hh>  
21 -#include <qpdf/QUtil.hh>  
22 -  
23 #include <qpdf/QIntC.hh> 20 #include <qpdf/QIntC.hh>
24 #include <qpdf/QPDF.hh> 21 #include <qpdf/QPDF.hh>
25 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 22 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
@@ -27,6 +24,7 @@ @@ -27,6 +24,7 @@
27 #include <qpdf/QPDFCryptoProvider.hh> 24 #include <qpdf/QPDFCryptoProvider.hh>
28 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh> 25 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
29 #include <qpdf/QPDFExc.hh> 26 #include <qpdf/QPDFExc.hh>
  27 +#include <qpdf/QPDFLogger.hh>
30 #include <qpdf/QPDFOutlineDocumentHelper.hh> 28 #include <qpdf/QPDFOutlineDocumentHelper.hh>
31 #include <qpdf/QPDFPageDocumentHelper.hh> 29 #include <qpdf/QPDFPageDocumentHelper.hh>
32 #include <qpdf/QPDFPageLabelDocumentHelper.hh> 30 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
@@ -34,6 +32,8 @@ @@ -34,6 +32,8 @@
34 #include <qpdf/QPDFSystemError.hh> 32 #include <qpdf/QPDFSystemError.hh>
35 #include <qpdf/QPDFUsage.hh> 33 #include <qpdf/QPDFUsage.hh>
36 #include <qpdf/QPDFWriter.hh> 34 #include <qpdf/QPDFWriter.hh>
  35 +#include <qpdf/QTC.hh>
  36 +#include <qpdf/QUtil.hh>
37 37
38 #include <qpdf/auto_job_schema.hh> // JOB_SCHEMA_DATA 38 #include <qpdf/auto_job_schema.hh> // JOB_SCHEMA_DATA
39 39
@@ -93,10 +93,8 @@ namespace @@ -93,10 +93,8 @@ namespace
93 { 93 {
94 public: 94 public:
95 ProgressReporter( 95 ProgressReporter(
96 - std::ostream& cout,  
97 - std::string const& prefix,  
98 - char const* filename) :  
99 - cout(cout), 96 + Pipeline& p, std::string const& prefix, char const* filename) :
  97 + p(p),
100 prefix(prefix), 98 prefix(prefix),
101 filename(filename) 99 filename(filename)
102 { 100 {
@@ -105,7 +103,7 @@ namespace @@ -105,7 +103,7 @@ namespace
105 virtual void reportProgress(int); 103 virtual void reportProgress(int);
106 104
107 private: 105 private:
108 - std::ostream& cout; 106 + Pipeline& p;
109 std::string prefix; 107 std::string prefix;
110 std::string filename; 108 std::string filename;
111 }; 109 };
@@ -135,10 +133,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -135,10 +133,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
135 QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace"); 133 QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace");
136 if (!(w_obj.isNumber() && h_obj.isNumber())) { 134 if (!(w_obj.isNumber() && h_obj.isNumber())) {
137 if (!description.empty()) { 135 if (!description.empty()) {
138 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
139 - cout << prefix << ": " << description  
140 - << ": not optimizing because image dictionary"  
141 - << " is missing required keys" << std::endl; 136 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  137 + v << prefix << ": " << description
  138 + << ": not optimizing because image dictionary"
  139 + << " is missing required keys\n";
142 }); 140 });
143 } 141 }
144 return result; 142 return result;
@@ -147,10 +145,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -147,10 +145,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
147 if (!(components_obj.isInteger() && (components_obj.getIntValue() == 8))) { 145 if (!(components_obj.isInteger() && (components_obj.getIntValue() == 8))) {
148 QTC::TC("qpdf", "QPDFJob image optimize bits per component"); 146 QTC::TC("qpdf", "QPDFJob image optimize bits per component");
149 if (!description.empty()) { 147 if (!description.empty()) {
150 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
151 - cout << prefix << ": " << description  
152 - << ": not optimizing because image has other than"  
153 - << " 8 bits per component" << std::endl; 148 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  149 + v << prefix << ": " << description
  150 + << ": not optimizing because image has other than"
  151 + << " 8 bits per component\n";
154 }); 152 });
155 } 153 }
156 return result; 154 return result;
@@ -185,10 +183,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -185,10 +183,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
185 } else { 183 } else {
186 QTC::TC("qpdf", "QPDFJob image optimize colorspace"); 184 QTC::TC("qpdf", "QPDFJob image optimize colorspace");
187 if (!description.empty()) { 185 if (!description.empty()) {
188 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
189 - cout << prefix << ": " << description  
190 - << ": not optimizing because qpdf can't optimize"  
191 - << " images with this colorspace" << std::endl; 186 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  187 + v << prefix << ": " << description
  188 + << ": not optimizing because qpdf can't optimize"
  189 + << " images with this colorspace\n";
192 }); 190 });
193 } 191 }
194 return result; 192 return result;
@@ -198,11 +196,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next) @@ -198,11 +196,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
198 ((this->oi_min_area > 0) && ((w * h) <= this->oi_min_area))) { 196 ((this->oi_min_area > 0) && ((w * h) <= this->oi_min_area))) {
199 QTC::TC("qpdf", "QPDFJob image optimize too small"); 197 QTC::TC("qpdf", "QPDFJob image optimize too small");
200 if (!description.empty()) { 198 if (!description.empty()) {
201 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
202 - cout << prefix << ": " << description  
203 - << ": not optimizing because image"  
204 - << " is smaller than requested minimum dimensions"  
205 - << std::endl; 199 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  200 + v << prefix << ": " << description
  201 + << ": not optimizing because image"
  202 + << " is smaller than requested minimum dimensions\n";
206 }); 203 });
207 } 204 }
208 return result; 205 return result;
@@ -217,10 +214,10 @@ ImageOptimizer::evaluate(std::string const&amp; description) @@ -217,10 +214,10 @@ ImageOptimizer::evaluate(std::string const&amp; description)
217 { 214 {
218 if (!image.pipeStreamData(0, 0, qpdf_dl_specialized, true)) { 215 if (!image.pipeStreamData(0, 0, qpdf_dl_specialized, true)) {
219 QTC::TC("qpdf", "QPDFJob image optimize no pipeline"); 216 QTC::TC("qpdf", "QPDFJob image optimize no pipeline");
220 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
221 - cout << prefix << ": " << description  
222 - << ": not optimizing because unable to decode data"  
223 - << " or data already uses DCT" << std::endl; 217 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  218 + v << prefix << ": " << description
  219 + << ": not optimizing because unable to decode data"
  220 + << " or data already uses DCT\n";
224 }); 221 });
225 return false; 222 return false;
226 } 223 }
@@ -237,17 +234,17 @@ ImageOptimizer::evaluate(std::string const&amp; description) @@ -237,17 +234,17 @@ ImageOptimizer::evaluate(std::string const&amp; description)
237 long long orig_length = image.getDict().getKey("/Length").getIntValue(); 234 long long orig_length = image.getDict().getKey("/Length").getIntValue();
238 if (c.getCount() >= orig_length) { 235 if (c.getCount() >= orig_length) {
239 QTC::TC("qpdf", "QPDFJob image optimize no shrink"); 236 QTC::TC("qpdf", "QPDFJob image optimize no shrink");
240 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
241 - cout << prefix << ": " << description  
242 - << ": not optimizing because DCT compression does not"  
243 - << " reduce image size" << std::endl; 237 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  238 + v << prefix << ": " << description
  239 + << ": not optimizing because DCT compression does not"
  240 + << " reduce image size\n";
244 }); 241 });
245 return false; 242 return false;
246 } 243 }
247 - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
248 - cout << prefix << ": " << description  
249 - << ": optimizing image reduces size from " << orig_length << " to "  
250 - << c.getCount() << std::endl; 244 + o.doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  245 + v << prefix << ": " << description
  246 + << ": optimizing image reduces size from " << orig_length << " to "
  247 + << c.getCount() << "\n";
251 }); 248 });
252 return true; 249 return true;
253 } 250 }
@@ -304,8 +301,8 @@ QPDFPageData::QPDFPageData(QPDFPageData const&amp; other, int page) : @@ -304,8 +301,8 @@ QPDFPageData::QPDFPageData(QPDFPageData const&amp; other, int page) :
304 void 301 void
305 ProgressReporter::reportProgress(int percentage) 302 ProgressReporter::reportProgress(int percentage)
306 { 303 {
307 - this->cout << prefix << ": " << filename  
308 - << ": write progress: " << percentage << "%" << std::endl; 304 + this->p << prefix << ": " << filename << ": write progress: " << percentage
  305 + << "%\n";
309 } 306 }
310 307
311 // These default values are duplicated in help and docs. 308 // These default values are duplicated in help and docs.
@@ -316,10 +313,9 @@ static int constexpr DEFAULT_OI_MIN_AREA = 16384; @@ -316,10 +313,9 @@ static int constexpr DEFAULT_OI_MIN_AREA = 16384;
316 static int constexpr DEFAULT_II_MIN_BYTES = 1024; 313 static int constexpr DEFAULT_II_MIN_BYTES = 1024;
317 314
318 QPDFJob::Members::Members() : 315 QPDFJob::Members::Members() :
  316 + log(QPDFLogger::defaultLogger()),
319 message_prefix("qpdf"), 317 message_prefix("qpdf"),
320 warnings(false), 318 warnings(false),
321 - cout(&std::cout),  
322 - cerr(&std::cerr),  
323 encryption_status(0), 319 encryption_status(0),
324 verbose(false), 320 verbose(false),
325 password(0), 321 password(0),
@@ -442,19 +438,31 @@ QPDFJob::setMessagePrefix(std::string const&amp; message_prefix) @@ -442,19 +438,31 @@ QPDFJob::setMessagePrefix(std::string const&amp; message_prefix)
442 this->m->message_prefix = message_prefix; 438 this->m->message_prefix = message_prefix;
443 } 439 }
444 440
  441 +std::shared_ptr<QPDFLogger>
  442 +QPDFJob::getLogger()
  443 +{
  444 + return this->m->log;
  445 +}
  446 +
  447 +void
  448 +QPDFJob::setLogger(std::shared_ptr<QPDFLogger> l)
  449 +{
  450 + this->m->log = l;
  451 +}
  452 +
445 void 453 void
446 QPDFJob::setOutputStreams(std::ostream* out, std::ostream* err) 454 QPDFJob::setOutputStreams(std::ostream* out, std::ostream* err)
447 { 455 {
448 - this->m->cout = out ? out : &std::cout;  
449 - this->m->cerr = err ? err : &std::cerr; 456 + setLogger(std::make_shared<QPDFLogger>());
  457 + this->m->log->setOutputStreams(out, err);
450 } 458 }
451 459
452 void 460 void
453 QPDFJob::doIfVerbose( 461 QPDFJob::doIfVerbose(
454 - std::function<void(std::ostream&, std::string const& prefix)> fn) 462 + std::function<void(Pipeline&, std::string const& prefix)> fn)
455 { 463 {
456 - if (this->m->verbose && (this->m->cout != nullptr)) {  
457 - fn(*(this->m->cout), this->m->message_prefix); 464 + if (this->m->verbose) {
  465 + fn(*this->m->log->getInfo(), this->m->message_prefix);
458 } 466 }
459 } 467 }
460 468
@@ -587,14 +595,13 @@ QPDFJob::run() @@ -587,14 +595,13 @@ QPDFJob::run()
587 } 595 }
588 if (this->m->warnings && (!this->m->suppress_warnings)) { 596 if (this->m->warnings && (!this->m->suppress_warnings)) {
589 if (createsOutput()) { 597 if (createsOutput()) {
590 - (*this->m->cerr) 598 + *this->m->log->getWarn()
591 << this->m->message_prefix 599 << this->m->message_prefix
592 << ": operation succeeded with warnings;" 600 << ": operation succeeded with warnings;"
593 - << " resulting file may have some problems" << std::endl; 601 + << " resulting file may have some problems\n";
594 } else { 602 } else {
595 - (*this->m->cerr)  
596 - << this->m->message_prefix  
597 - << ": operation succeeded with warnings" << std::endl; 603 + *this->m->log->getWarn() << this->m->message_prefix
  604 + << ": operation succeeded with warnings\n";
598 } 605 }
599 } 606 }
600 } 607 }
@@ -722,7 +729,7 @@ QPDFJob::getEncryptionStatus() @@ -722,7 +729,7 @@ QPDFJob::getEncryptionStatus()
722 void 729 void
723 QPDFJob::setQPDFOptions(QPDF& pdf) 730 QPDFJob::setQPDFOptions(QPDF& pdf)
724 { 731 {
725 - pdf.setOutputStreams(this->m->cout, this->m->cerr); 732 + pdf.setLogger(this->m->log);
726 if (m->ignore_xref_streams) { 733 if (m->ignore_xref_streams) {
727 pdf.setIgnoreXRefStreams(true); 734 pdf.setIgnoreXRefStreams(true);
728 } 735 }
@@ -778,50 +785,47 @@ QPDFJob::showEncryption(QPDF&amp; pdf) @@ -778,50 +785,47 @@ QPDFJob::showEncryption(QPDF&amp; pdf)
778 QPDF::encryption_method_e stream_method = QPDF::e_unknown; 785 QPDF::encryption_method_e stream_method = QPDF::e_unknown;
779 QPDF::encryption_method_e string_method = QPDF::e_unknown; 786 QPDF::encryption_method_e string_method = QPDF::e_unknown;
780 QPDF::encryption_method_e file_method = QPDF::e_unknown; 787 QPDF::encryption_method_e file_method = QPDF::e_unknown;
781 - auto& cout = *this->m->cout; 788 + auto& cout = *this->m->log->getInfo();
782 if (!pdf.isEncrypted(R, P, V, stream_method, string_method, file_method)) { 789 if (!pdf.isEncrypted(R, P, V, stream_method, string_method, file_method)) {
783 - cout << "File is not encrypted" << std::endl; 790 + cout << "File is not encrypted\n";
784 } else { 791 } else {
785 - cout << "R = " << R << std::endl;  
786 - cout << "P = " << P << std::endl; 792 + cout << "R = " << R << "\n";
  793 + cout << "P = " << P << "\n";
787 std::string user_password = pdf.getTrimmedUserPassword(); 794 std::string user_password = pdf.getTrimmedUserPassword();
788 std::string encryption_key = pdf.getEncryptionKey(); 795 std::string encryption_key = pdf.getEncryptionKey();
789 - cout << "User password = " << user_password << std::endl; 796 + cout << "User password = " << user_password << "\n";
790 if (m->show_encryption_key) { 797 if (m->show_encryption_key) {
791 cout << "Encryption key = " << QUtil::hex_encode(encryption_key) 798 cout << "Encryption key = " << QUtil::hex_encode(encryption_key)
792 - << std::endl; 799 + << "\n";
793 } 800 }
794 if (pdf.ownerPasswordMatched()) { 801 if (pdf.ownerPasswordMatched()) {
795 - cout << "Supplied password is owner password" << std::endl; 802 + cout << "Supplied password is owner password\n";
796 } 803 }
797 if (pdf.userPasswordMatched()) { 804 if (pdf.userPasswordMatched()) {
798 - cout << "Supplied password is user password" << std::endl; 805 + cout << "Supplied password is user password\n";
799 } 806 }
800 cout << "extract for accessibility: " 807 cout << "extract for accessibility: "
801 - << show_bool(pdf.allowAccessibility()) << std::endl 808 + << show_bool(pdf.allowAccessibility()) << "\n"
802 << "extract for any purpose: " << show_bool(pdf.allowExtractAll()) 809 << "extract for any purpose: " << show_bool(pdf.allowExtractAll())
803 - << std::endl 810 + << "\n"
804 << "print low resolution: " << show_bool(pdf.allowPrintLowRes()) 811 << "print low resolution: " << show_bool(pdf.allowPrintLowRes())
805 - << std::endl 812 + << "\n"
806 << "print high resolution: " << show_bool(pdf.allowPrintHighRes()) 813 << "print high resolution: " << show_bool(pdf.allowPrintHighRes())
807 - << std::endl 814 + << "\n"
808 << "modify document assembly: " 815 << "modify document assembly: "
809 - << show_bool(pdf.allowModifyAssembly()) << std::endl  
810 - << "modify forms: " << show_bool(pdf.allowModifyForm())  
811 - << std::endl 816 + << show_bool(pdf.allowModifyAssembly()) << "\n"
  817 + << "modify forms: " << show_bool(pdf.allowModifyForm()) << "\n"
812 << "modify annotations: " << show_bool(pdf.allowModifyAnnotation()) 818 << "modify annotations: " << show_bool(pdf.allowModifyAnnotation())
813 - << std::endl  
814 - << "modify other: " << show_bool(pdf.allowModifyOther())  
815 - << std::endl  
816 - << "modify anything: " << show_bool(pdf.allowModifyAll())  
817 - << std::endl; 819 + << "\n"
  820 + << "modify other: " << show_bool(pdf.allowModifyOther()) << "\n"
  821 + << "modify anything: " << show_bool(pdf.allowModifyAll()) << "\n";
818 if (V >= 4) { 822 if (V >= 4) {
819 cout << "stream encryption method: " 823 cout << "stream encryption method: "
820 - << show_encryption_method(stream_method) << std::endl 824 + << show_encryption_method(stream_method) << "\n"
821 << "string encryption method: " 825 << "string encryption method: "
822 - << show_encryption_method(string_method) << std::endl 826 + << show_encryption_method(string_method) << "\n"
823 << "file encryption method: " 827 << "file encryption method: "
824 - << show_encryption_method(file_method) << std::endl; 828 + << show_encryption_method(file_method) << "\n";
825 } 829 }
826 } 830 }
827 } 831 }
@@ -835,15 +839,15 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -835,15 +839,15 @@ QPDFJob::doCheck(QPDF&amp; pdf)
835 // errors. 839 // errors.
836 bool okay = true; 840 bool okay = true;
837 bool warnings = false; 841 bool warnings = false;
838 - auto& cout = *this->m->cout;  
839 - cout << "checking " << m->infilename << std::endl; 842 + auto& cout = *this->m->log->getInfo();
  843 + cout << "checking " << m->infilename.get() << "\n";
840 try { 844 try {
841 int extension_level = pdf.getExtensionLevel(); 845 int extension_level = pdf.getExtensionLevel();
842 cout << "PDF Version: " << pdf.getPDFVersion(); 846 cout << "PDF Version: " << pdf.getPDFVersion();
843 if (extension_level > 0) { 847 if (extension_level > 0) {
844 cout << " extension level " << pdf.getExtensionLevel(); 848 cout << " extension level " << pdf.getExtensionLevel();
845 } 849 }
846 - cout << std::endl; 850 + cout << "\n";
847 showEncryption(pdf); 851 showEncryption(pdf);
848 if (pdf.isLinearized()) { 852 if (pdf.isLinearized()) {
849 cout << "File is linearized\n"; 853 cout << "File is linearized\n";
@@ -875,12 +879,12 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -875,12 +879,12 @@ QPDFJob::doCheck(QPDF&amp; pdf)
875 page.parseContents(&discard_contents); 879 page.parseContents(&discard_contents);
876 } catch (QPDFExc& e) { 880 } catch (QPDFExc& e) {
877 okay = false; 881 okay = false;
878 - *(this->m->cerr) << "ERROR: page " << pageno << ": " << e.what()  
879 - << std::endl; 882 + *this->m->log->getError()
  883 + << "ERROR: page " << pageno << ": " << e.what() << "\n";
880 } 884 }
881 } 885 }
882 } catch (std::exception& e) { 886 } catch (std::exception& e) {
883 - (*this->m->cerr) << "ERROR: " << e.what() << std::endl; 887 + *this->m->log->getError() << "ERROR: " << e.what() << "\n";
884 okay = false; 888 okay = false;
885 } 889 }
886 if (!okay) { 890 if (!okay) {
@@ -890,9 +894,9 @@ QPDFJob::doCheck(QPDF&amp; pdf) @@ -890,9 +894,9 @@ QPDFJob::doCheck(QPDF&amp; pdf)
890 if ((!pdf.getWarnings().empty()) || warnings) { 894 if ((!pdf.getWarnings().empty()) || warnings) {
891 this->m->warnings = true; 895 this->m->warnings = true;
892 } else { 896 } else {
893 - *(this->m->cout) << "No syntax or stream encoding errors"  
894 - << " found; the file may still contain" << std::endl  
895 - << "errors that qpdf cannot detect" << std::endl; 897 + *this->m->log->getInfo() << "No syntax or stream encoding errors"
  898 + << " found; the file may still contain\n"
  899 + << "errors that qpdf cannot detect\n";
896 } 900 }
897 } 901 }
898 902
@@ -922,11 +926,11 @@ QPDFJob::doShowObj(QPDF&amp; pdf) @@ -922,11 +926,11 @@ QPDFJob::doShowObj(QPDF&amp; pdf)
922 filter ? qpdf_dl_all : qpdf_dl_none); 926 filter ? qpdf_dl_all : qpdf_dl_none);
923 } 927 }
924 } else { 928 } else {
925 - *(this->m->cout) << "Object is stream. Dictionary:" << std::endl  
926 - << obj.getDict().unparseResolved() << std::endl; 929 + *this->m->log->getInfo() << "Object is stream. Dictionary:\n"
  930 + << obj.getDict().unparseResolved() << "\n";
927 } 931 }
928 } else { 932 } else {
929 - *(this->m->cout) << obj.unparseResolved() << std::endl; 933 + *this->m->log->getInfo() << obj.unparseResolved() << "\n";
930 } 934 }
931 if (error) { 935 if (error) {
932 throw std::runtime_error( 936 throw std::runtime_error(
@@ -938,17 +942,17 @@ void @@ -938,17 +942,17 @@ void
938 QPDFJob::doShowPages(QPDF& pdf) 942 QPDFJob::doShowPages(QPDF& pdf)
939 { 943 {
940 int pageno = 0; 944 int pageno = 0;
941 - auto& cout = *this->m->cout; 945 + auto& cout = *this->m->log->getInfo();
942 for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) { 946 for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
943 QPDFObjectHandle page = ph.getObjectHandle(); 947 QPDFObjectHandle page = ph.getObjectHandle();
944 ++pageno; 948 ++pageno;
945 949
946 cout << "page " << pageno << ": " << page.getObjectID() << " " 950 cout << "page " << pageno << ": " << page.getObjectID() << " "
947 - << page.getGeneration() << " R" << std::endl; 951 + << page.getGeneration() << " R\n";
948 if (m->show_page_images) { 952 if (m->show_page_images) {
949 std::map<std::string, QPDFObjectHandle> images = ph.getImages(); 953 std::map<std::string, QPDFObjectHandle> images = ph.getImages();
950 if (!images.empty()) { 954 if (!images.empty()) {
951 - cout << " images:" << std::endl; 955 + cout << " images:\n";
952 for (auto const& iter2: images) { 956 for (auto const& iter2: images) {
953 std::string const& name = iter2.first; 957 std::string const& name = iter2.first;
954 QPDFObjectHandle image = iter2.second; 958 QPDFObjectHandle image = iter2.second;
@@ -956,14 +960,14 @@ QPDFJob::doShowPages(QPDF&amp; pdf) @@ -956,14 +960,14 @@ QPDFJob::doShowPages(QPDF&amp; pdf)
956 int width = dict.getKey("/Width").getIntValueAsInt(); 960 int width = dict.getKey("/Width").getIntValueAsInt();
957 int height = dict.getKey("/Height").getIntValueAsInt(); 961 int height = dict.getKey("/Height").getIntValueAsInt();
958 cout << " " << name << ": " << image.unparse() << ", " 962 cout << " " << name << ": " << image.unparse() << ", "
959 - << width << " x " << height << std::endl; 963 + << width << " x " << height << "\n";
960 } 964 }
961 } 965 }
962 } 966 }
963 967
964 - cout << " content:" << std::endl; 968 + cout << " content:\n";
965 for (auto& iter2: ph.getPageContents()) { 969 for (auto& iter2: ph.getPageContents()) {
966 - cout << " " << iter2.unparse() << std::endl; 970 + cout << " " << iter2.unparse() << "\n";
967 } 971 }
968 } 972 }
969 } 973 }
@@ -976,39 +980,36 @@ QPDFJob::doListAttachments(QPDF&amp; pdf) @@ -976,39 +980,36 @@ QPDFJob::doListAttachments(QPDF&amp; pdf)
976 for (auto const& i: efdh.getEmbeddedFiles()) { 980 for (auto const& i: efdh.getEmbeddedFiles()) {
977 std::string const& key = i.first; 981 std::string const& key = i.first;
978 auto efoh = i.second; 982 auto efoh = i.second;
979 - *(this->m->cout)  
980 - << key << " -> " << efoh->getEmbeddedFileStream().getObjGen()  
981 - << std::endl;  
982 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) { 983 + *this->m->log->getInfo()
  984 + << key << " -> "
  985 + << efoh->getEmbeddedFileStream().getObjGen().unparse() << "\n";
  986 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
983 auto desc = efoh->getDescription(); 987 auto desc = efoh->getDescription();
984 if (!desc.empty()) { 988 if (!desc.empty()) {
985 - cout << " description: " << desc << std::endl; 989 + v << " description: " << desc << "\n";
986 } 990 }
987 - cout << " preferred name: " << efoh->getFilename()  
988 - << std::endl;  
989 - cout << " all names:" << std::endl; 991 + v << " preferred name: " << efoh->getFilename() << "\n";
  992 + v << " all names:\n";
990 for (auto const& i2: efoh->getFilenames()) { 993 for (auto const& i2: efoh->getFilenames()) {
991 - cout << " " << i2.first << " -> " << i2.second  
992 - << std::endl; 994 + v << " " << i2.first << " -> " << i2.second << "\n";
993 } 995 }
994 - cout << " all data streams:" << std::endl; 996 + v << " all data streams:\n";
995 for (auto i2: efoh->getEmbeddedFileStreams().ditems()) { 997 for (auto i2: efoh->getEmbeddedFileStreams().ditems()) {
996 auto efs = QPDFEFStreamObjectHelper(i2.second); 998 auto efs = QPDFEFStreamObjectHelper(i2.second);
997 - cout << " " << i2.first << " -> "  
998 - << efs.getObjectHandle().getObjGen() << std::endl;  
999 - cout << " creation date: " << efs.getCreationDate()  
1000 - << std::endl  
1001 - << " modification date: " << efs.getModDate()  
1002 - << std::endl  
1003 - << " mime type: " << efs.getSubtype() << std::endl  
1004 - << " checksum: "  
1005 - << QUtil::hex_encode(efs.getChecksum()) << std::endl; 999 + v << " " << i2.first << " -> "
  1000 + << efs.getObjectHandle().getObjGen().unparse() << "\n";
  1001 + v << " creation date: " << efs.getCreationDate()
  1002 + << "\n"
  1003 + << " modification date: " << efs.getModDate() << "\n"
  1004 + << " mime type: " << efs.getSubtype() << "\n"
  1005 + << " checksum: "
  1006 + << QUtil::hex_encode(efs.getChecksum()) << "\n";
1006 } 1007 }
1007 }); 1008 });
1008 } 1009 }
1009 } else { 1010 } else {
1010 - *(this->m->cout) << m->infilename << " has no embedded files"  
1011 - << std::endl; 1011 + *this->m->log->getInfo()
  1012 + << m->infilename.get() << " has no embedded files\n";
1012 } 1013 }
1013 } 1014 }
1014 1015
@@ -1816,11 +1817,10 @@ QPDFJob::doJSON(QPDF&amp; pdf, Pipeline* p) @@ -1816,11 +1817,10 @@ QPDFJob::doJSON(QPDF&amp; pdf, Pipeline* p)
1816 std::list<std::string> errors; 1817 std::list<std::string> errors;
1817 JSON captured = JSON::parse(captured_json); 1818 JSON captured = JSON::parse(captured_json);
1818 if (!captured.checkSchema(schema, errors)) { 1819 if (!captured.checkSchema(schema, errors)) {
1819 - *(this->m->cerr) << "QPDFJob didn't create JSON that complies with"  
1820 - " its own rules."  
1821 - << std::endl; 1820 + this->m->log->error("QPDFJob didn't create JSON that complies with "
  1821 + "its own rules.\n");
1822 for (auto const& error: errors) { 1822 for (auto const& error: errors) {
1823 - *(this->m->cerr) << error << std::endl; 1823 + *this->m->log->getError() << error << "\n";
1824 } 1824 }
1825 } 1825 }
1826 } 1826 }
@@ -1829,30 +1829,26 @@ QPDFJob::doJSON(QPDF&amp; pdf, Pipeline* p) @@ -1829,30 +1829,26 @@ QPDFJob::doJSON(QPDF&amp; pdf, Pipeline* p)
1829 void 1829 void
1830 QPDFJob::doInspection(QPDF& pdf) 1830 QPDFJob::doInspection(QPDF& pdf)
1831 { 1831 {
  1832 + auto& cout = *this->m->log->getInfo();
1832 if (m->check) { 1833 if (m->check) {
1833 doCheck(pdf); 1834 doCheck(pdf);
1834 } 1835 }
1835 if (m->json_version) { 1836 if (m->json_version) {
1836 - Pl_OStream os("stdout", *(this->m->cout));  
1837 - doJSON(pdf, &os);  
1838 - os.finish(); 1837 + doJSON(pdf, &cout);
1839 } 1838 }
1840 if (m->show_npages) { 1839 if (m->show_npages) {
1841 QTC::TC("qpdf", "QPDFJob npages"); 1840 QTC::TC("qpdf", "QPDFJob npages");
1842 - *(this->m->cout)  
1843 - << pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue()  
1844 - << std::endl; 1841 + cout << pdf.getRoot().getKey("/Pages").getKey("/Count").getIntValue()
  1842 + << "\n";
1845 } 1843 }
1846 if (m->show_encryption) { 1844 if (m->show_encryption) {
1847 showEncryption(pdf); 1845 showEncryption(pdf);
1848 } 1846 }
1849 if (m->check_linearization) { 1847 if (m->check_linearization) {
1850 if (!pdf.isLinearized()) { 1848 if (!pdf.isLinearized()) {
1851 - *(this->m->cout)  
1852 - << m->infilename << " is not linearized" << std::endl; 1849 + cout << m->infilename.get() << " is not linearized\n";
1853 } else if (pdf.checkLinearization()) { 1850 } else if (pdf.checkLinearization()) {
1854 - *(this->m->cout)  
1855 - << m->infilename << ": no linearization errors" << std::endl; 1851 + cout << m->infilename.get() << ": no linearization errors\n";
1856 } else { 1852 } else {
1857 this->m->warnings = true; 1853 this->m->warnings = true;
1858 } 1854 }
@@ -1861,8 +1857,7 @@ QPDFJob::doInspection(QPDF&amp; pdf) @@ -1861,8 +1857,7 @@ QPDFJob::doInspection(QPDF&amp; pdf)
1861 if (pdf.isLinearized()) { 1857 if (pdf.isLinearized()) {
1862 pdf.showLinearizationData(); 1858 pdf.showLinearizationData();
1863 } else { 1859 } else {
1864 - *(this->m->cout)  
1865 - << m->infilename << " is not linearized" << std::endl; 1860 + cout << m->infilename.get() << " is not linearized\n";
1866 } 1861 }
1867 } 1862 }
1868 if (m->show_xref) { 1863 if (m->show_xref) {
@@ -1979,11 +1974,10 @@ QPDFJob::doProcess( @@ -1979,11 +1974,10 @@ QPDFJob::doProcess(
1979 } 1974 }
1980 if (!warned) { 1975 if (!warned) {
1981 warned = true; 1976 warned = true;
1982 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
1983 - cout << prefix << ": supplied password didn't work;"  
1984 - << " trying other passwords based on interpreting"  
1985 - << " password with different string encodings"  
1986 - << std::endl; 1977 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  1978 + v << prefix << ": supplied password didn't work;"
  1979 + << " trying other passwords based on interpreting"
  1980 + << " password with different string encodings\n";
1987 }); 1981 });
1988 } 1982 }
1989 } 1983 }
@@ -2097,8 +2091,8 @@ QPDFJob::doUnderOverlayForPage( @@ -2097,8 +2091,8 @@ QPDFJob::doUnderOverlayForPage(
2097 "/Resources", QPDFObjectHandle::newDictionary()); 2091 "/Resources", QPDFObjectHandle::newDictionary());
2098 } 2092 }
2099 for (int from_pageno: pagenos[pageno]) { 2093 for (int from_pageno: pagenos[pageno]) {
2100 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2101 - cout << " " << uo.which << " " << from_pageno << std::endl; 2094 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2095 + v << " " << uo.which << " " << from_pageno << "\n";
2102 }); 2096 });
2103 auto from_page = pages.at(QIntC::to_size(from_pageno - 1)); 2097 auto from_page = pages.at(QIntC::to_size(from_pageno - 1));
2104 if (0 == fo.count(from_pageno)) { 2098 if (0 == fo.count(from_pageno)) {
@@ -2187,12 +2181,12 @@ QPDFJob::handleUnderOverlay(QPDF&amp; pdf) @@ -2187,12 +2181,12 @@ QPDFJob::handleUnderOverlay(QPDF&amp; pdf)
2187 QPDFPageDocumentHelper main_pdh(pdf); 2181 QPDFPageDocumentHelper main_pdh(pdf);
2188 std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages(); 2182 std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages();
2189 size_t main_npages = main_pages.size(); 2183 size_t main_npages = main_pages.size();
2190 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2191 - cout << prefix << ": processing underlay/overlay" << std::endl; 2184 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2185 + v << prefix << ": processing underlay/overlay\n";
2192 }); 2186 });
2193 for (size_t i = 0; i < main_npages; ++i) { 2187 for (size_t i = 0; i < main_npages; ++i) {
2194 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2195 - cout << " page " << 1 + i << std::endl; 2188 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2189 + v << " page " << 1 + i << "\n";
2196 }); 2190 });
2197 doUnderOverlayForPage( 2191 doUnderOverlayForPage(
2198 pdf, 2192 pdf,
@@ -2248,9 +2242,9 @@ QPDFJob::addAttachments(QPDF&amp; pdf) @@ -2248,9 +2242,9 @@ QPDFJob::addAttachments(QPDF&amp; pdf)
2248 } 2242 }
2249 2243
2250 efdh.replaceEmbeddedFile(to_add.key, fs); 2244 efdh.replaceEmbeddedFile(to_add.key, fs);
2251 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2252 - cout << prefix << ": attached " << to_add.path << " as "  
2253 - << to_add.filename << " with key " << to_add.key << std::endl; 2245 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2246 + v << prefix << ": attached " << to_add.path << " as "
  2247 + << to_add.filename << " with key " << to_add.key << "\n";
2254 }); 2248 });
2255 } 2249 }
2256 2250
@@ -2276,9 +2270,9 @@ QPDFJob::copyAttachments(QPDF&amp; pdf) @@ -2276,9 +2270,9 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2276 QPDFEmbeddedFileDocumentHelper efdh(pdf); 2270 QPDFEmbeddedFileDocumentHelper efdh(pdf);
2277 std::vector<std::string> duplicates; 2271 std::vector<std::string> duplicates;
2278 for (auto const& to_copy: m->attachments_to_copy) { 2272 for (auto const& to_copy: m->attachments_to_copy) {
2279 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2280 - cout << prefix << ": copying attachments from " << to_copy.path  
2281 - << std::endl; 2273 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2274 + v << prefix << ": copying attachments from " << to_copy.path
  2275 + << "\n";
2282 }); 2276 });
2283 auto other = processFile( 2277 auto other = processFile(
2284 to_copy.path.c_str(), to_copy.password.c_str(), false, false); 2278 to_copy.path.c_str(), to_copy.password.c_str(), false, false);
@@ -2294,9 +2288,8 @@ QPDFJob::copyAttachments(QPDF&amp; pdf) @@ -2294,9 +2288,8 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2294 pdf.copyForeignObject(iter.second->getObjectHandle()); 2288 pdf.copyForeignObject(iter.second->getObjectHandle());
2295 efdh.replaceEmbeddedFile( 2289 efdh.replaceEmbeddedFile(
2296 new_key, QPDFFileSpecObjectHelper(new_fs_oh)); 2290 new_key, QPDFFileSpecObjectHelper(new_fs_oh));
2297 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2298 - cout << " " << iter.first << " -> " << new_key  
2299 - << std::endl; 2291 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2292 + v << " " << iter.first << " -> " << new_key << "\n";
2300 }); 2293 });
2301 } 2294 }
2302 } 2295 }
@@ -2398,9 +2391,8 @@ QPDFJob::handleTransformations(QPDF&amp; pdf) @@ -2398,9 +2391,8 @@ QPDFJob::handleTransformations(QPDF&amp; pdf)
2398 QPDFEmbeddedFileDocumentHelper efdh(pdf); 2391 QPDFEmbeddedFileDocumentHelper efdh(pdf);
2399 for (auto const& key: m->attachments_to_remove) { 2392 for (auto const& key: m->attachments_to_remove) {
2400 if (efdh.removeEmbeddedFile(key)) { 2393 if (efdh.removeEmbeddedFile(key)) {
2401 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2402 - cout << prefix << ": removed attachment " << key  
2403 - << std::endl; 2394 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2395 + v << prefix << ": removed attachment " << key << "\n";
2404 }); 2396 });
2405 } else { 2397 } else {
2406 throw std::runtime_error("attachment " + key + " not found"); 2398 throw std::runtime_error("attachment " + key + " not found");
@@ -2437,9 +2429,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2437,9 +2429,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2437 std::set<QPDFObjGen> resources_seen; // shared resources detection 2429 std::set<QPDFObjGen> resources_seen; // shared resources detection
2438 std::set<QPDFObjGen> nodes_seen; // loop detection 2430 std::set<QPDFObjGen> nodes_seen; // loop detection
2439 2431
2440 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2441 - cout << prefix << ": " << pdf.getFilename()  
2442 - << ": checking for shared resources" << std::endl; 2432 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2433 + v << prefix << ": " << pdf.getFilename()
  2434 + << ": checking for shared resources\n";
2443 }); 2435 });
2444 2436
2445 std::list<QPDFObjectHandle> queue; 2437 std::list<QPDFObjectHandle> queue;
@@ -2458,9 +2450,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2458,9 +2450,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2458 // This is a non-leaf node. 2450 // This is a non-leaf node.
2459 if (dict.hasKey("/Resources")) { 2451 if (dict.hasKey("/Resources")) {
2460 QTC::TC("qpdf", "QPDFJob found resources in non-leaf"); 2452 QTC::TC("qpdf", "QPDFJob found resources in non-leaf");
2461 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2462 - cout << " found resources in non-leaf page node "  
2463 - << og.getObj() << " " << og.getGen() << std::endl; 2453 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2454 + v << " found resources in non-leaf page node "
  2455 + << og.getObj() << " " << og.getGen() << "\n";
2464 }); 2456 });
2465 return true; 2457 return true;
2466 } 2458 }
@@ -2475,13 +2467,12 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2475,13 +2467,12 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2475 QPDFObjGen resources_og = resources.getObjGen(); 2467 QPDFObjGen resources_og = resources.getObjGen();
2476 if (resources_seen.count(resources_og)) { 2468 if (resources_seen.count(resources_og)) {
2477 QTC::TC("qpdf", "QPDFJob found shared resources in leaf"); 2469 QTC::TC("qpdf", "QPDFJob found shared resources in leaf");
2478 - doIfVerbose(  
2479 - [&](std::ostream& cout, std::string const& prefix) {  
2480 - cout << " found shared resources in leaf node "  
2481 - << og.getObj() << " " << og.getGen() << ": "  
2482 - << resources_og.getObj() << " "  
2483 - << resources_og.getGen() << std::endl;  
2484 - }); 2470 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2471 + v << " found shared resources in leaf node "
  2472 + << og.getObj() << " " << og.getGen() << ": "
  2473 + << resources_og.getObj() << " "
  2474 + << resources_og.getGen() << "\n";
  2475 + });
2485 return true; 2476 return true;
2486 } 2477 }
2487 resources_seen.insert(resources_og); 2478 resources_seen.insert(resources_og);
@@ -2493,13 +2484,12 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2493,13 +2484,12 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2493 QPDFObjGen xobject_og = xobject.getObjGen(); 2484 QPDFObjGen xobject_og = xobject.getObjGen();
2494 if (resources_seen.count(xobject_og)) { 2485 if (resources_seen.count(xobject_og)) {
2495 QTC::TC("qpdf", "QPDFJob found shared xobject in leaf"); 2486 QTC::TC("qpdf", "QPDFJob found shared xobject in leaf");
2496 - doIfVerbose(  
2497 - [&](std::ostream& cout, std::string const& prefix) {  
2498 - cout << " found shared xobject in leaf node "  
2499 - << og.getObj() << " " << og.getGen() << ": "  
2500 - << xobject_og.getObj() << " "  
2501 - << xobject_og.getGen() << std::endl;  
2502 - }); 2487 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2488 + v << " found shared xobject in leaf node "
  2489 + << og.getObj() << " " << og.getGen() << ": "
  2490 + << xobject_og.getObj() << " " << xobject_og.getGen()
  2491 + << "\n";
  2492 + });
2503 return true; 2493 return true;
2504 } 2494 }
2505 resources_seen.insert(xobject_og); 2495 resources_seen.insert(xobject_og);
@@ -2515,8 +2505,8 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf) @@ -2515,8 +2505,8 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2515 } 2505 }
2516 } 2506 }
2517 2507
2518 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2519 - cout << prefix << ": no shared resources found" << std::endl; 2508 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2509 + v << prefix << ": no shared resources found\n";
2520 }); 2510 });
2521 return false; 2511 return false;
2522 } 2512 }
@@ -2567,9 +2557,9 @@ QPDFJob::handlePageSpecs( @@ -2567,9 +2557,9 @@ QPDFJob::handlePageSpecs(
2567 "qpdf", 2557 "qpdf",
2568 "QPDFJob automatically set keep files open", 2558 "QPDFJob automatically set keep files open",
2569 m->keep_files_open ? 0 : 1); 2559 m->keep_files_open ? 0 : 1);
2570 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2571 - cout << prefix << ": selecting --keep-open-files="  
2572 - << (m->keep_files_open ? "y" : "n") << std::endl; 2560 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2561 + v << prefix << ": selecting --keep-open-files="
  2562 + << (m->keep_files_open ? "y" : "n") << "\n";
2573 }); 2563 });
2574 } 2564 }
2575 2565
@@ -2597,9 +2587,8 @@ QPDFJob::handlePageSpecs( @@ -2597,9 +2587,8 @@ QPDFJob::handlePageSpecs(
2597 QTC::TC("qpdf", "QPDFJob pages encryption password"); 2587 QTC::TC("qpdf", "QPDFJob pages encryption password");
2598 password = m->encryption_file_password.get(); 2588 password = m->encryption_file_password.get();
2599 } 2589 }
2600 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2601 - cout << prefix << ": processing " << page_spec.filename  
2602 - << std::endl; 2590 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2591 + v << prefix << ": processing " << page_spec.filename << "\n";
2603 }); 2592 });
2604 std::shared_ptr<InputSource> is; 2593 std::shared_ptr<InputSource> is;
2605 ClosedFileInputSource* cis = 0; 2594 ClosedFileInputSource* cis = 0;
@@ -2660,9 +2649,8 @@ QPDFJob::handlePageSpecs( @@ -2660,9 +2649,8 @@ QPDFJob::handlePageSpecs(
2660 // without changing their object numbers. This enables other 2649 // without changing their object numbers. This enables other
2661 // things in the original file, such as outlines, to continue to 2650 // things in the original file, such as outlines, to continue to
2662 // work. 2651 // work.
2663 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2664 - cout << prefix << ": removing unreferenced pages from primary input"  
2665 - << std::endl; 2652 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2653 + v << prefix << ": removing unreferenced pages from primary input\n";
2666 }); 2654 });
2667 QPDFPageDocumentHelper dh(pdf); 2655 QPDFPageDocumentHelper dh(pdf);
2668 std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages(); 2656 std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages();
@@ -2720,9 +2708,8 @@ QPDFJob::handlePageSpecs( @@ -2720,9 +2708,8 @@ QPDFJob::handlePageSpecs(
2720 if (pldh.hasPageLabels()) { 2708 if (pldh.hasPageLabels()) {
2721 any_page_labels = true; 2709 any_page_labels = true;
2722 } 2710 }
2723 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
2724 - cout << prefix << ": adding pages from " << page_data.filename  
2725 - << std::endl; 2711 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  2712 + v << prefix << ": adding pages from " << page_data.filename << "\n";
2726 }); 2713 });
2727 for (auto pageno_iter: page_data.selected_pages) { 2714 for (auto pageno_iter: page_data.selected_pages) {
2728 // Pages are specified from 1 but numbered from 0 in the 2715 // Pages are specified from 1 but numbered from 0 in the
@@ -2910,18 +2897,17 @@ QPDFJob::maybeFixWritePassword(int R, std::string&amp; password) @@ -2910,18 +2897,17 @@ QPDFJob::maybeFixWritePassword(int R, std::string&amp; password)
2910 std::string encoded; 2897 std::string encoded;
2911 if (QUtil::utf8_to_pdf_doc(password, encoded)) { 2898 if (QUtil::utf8_to_pdf_doc(password, encoded)) {
2912 QTC::TC("qpdf", "QPDFJob auto-encode password"); 2899 QTC::TC("qpdf", "QPDFJob auto-encode password");
2913 - doIfVerbose(  
2914 - [&](std::ostream& cout, std::string const& prefix) {  
2915 - cout << prefix  
2916 - << ": automatically converting Unicode"  
2917 - << " password to single-byte encoding as"  
2918 - << " required for 40-bit or 128-bit"  
2919 - << " encryption" << std::endl;  
2920 - }); 2900 + doIfVerbose([&](Pipeline& v,
  2901 + std::string const& prefix) {
  2902 + v << prefix << ": automatically converting Unicode"
  2903 + << " password to single-byte encoding as"
  2904 + << " required for 40-bit or 128-bit"
  2905 + << " encryption\n";
  2906 + });
2921 password = encoded; 2907 password = encoded;
2922 } else { 2908 } else {
2923 QTC::TC("qpdf", "QPDFJob bytes fallback warning"); 2909 QTC::TC("qpdf", "QPDFJob bytes fallback warning");
2924 - *(this->m->cerr) 2910 + *this->m->log->getError()
2925 << this->m->message_prefix << ": WARNING: " 2911 << this->m->message_prefix << ": WARNING: "
2926 << "supplied password looks like a Unicode" 2912 << "supplied password looks like a Unicode"
2927 << " password with characters not allowed in" 2913 << " password with characters not allowed in"
@@ -2929,8 +2915,7 @@ QPDFJob::maybeFixWritePassword(int R, std::string&amp; password) @@ -2929,8 +2915,7 @@ QPDFJob::maybeFixWritePassword(int R, std::string&amp; password)
2929 << " most readers will not be able to open this" 2915 << " most readers will not be able to open this"
2930 << " file with the supplied password." 2916 << " file with the supplied password."
2931 << " (Use --password-mode=bytes to suppress this" 2917 << " (Use --password-mode=bytes to suppress this"
2932 - << " warning and use the password anyway.)"  
2933 - << std::endl; 2918 + << " warning and use the password anyway.)\n";
2934 } 2919 }
2935 } else if ((R >= 5) && (!is_valid_utf8)) { 2920 } else if ((R >= 5) && (!is_valid_utf8)) {
2936 QTC::TC("qpdf", "QPDFJob invalid utf-8 in auto"); 2921 QTC::TC("qpdf", "QPDFJob invalid utf-8 in auto");
@@ -2968,26 +2953,24 @@ QPDFJob::setEncryptionOptions(QPDF&amp; pdf, QPDFWriter&amp; w) @@ -2968,26 +2953,24 @@ QPDFJob::setEncryptionOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
2968 throw std::logic_error("bad encryption keylen"); 2953 throw std::logic_error("bad encryption keylen");
2969 } 2954 }
2970 if ((R > 3) && (m->r3_accessibility == false)) { 2955 if ((R > 3) && (m->r3_accessibility == false)) {
2971 - *(this->m->cerr) << this->m->message_prefix  
2972 - << ": -accessibility=n is ignored for modern"  
2973 - << " encryption formats" << std::endl; 2956 + *this->m->log->getError() << this->m->message_prefix
  2957 + << ": -accessibility=n is ignored for modern"
  2958 + << " encryption formats\n";
2974 } 2959 }
2975 maybeFixWritePassword(R, m->user_password); 2960 maybeFixWritePassword(R, m->user_password);
2976 maybeFixWritePassword(R, m->owner_password); 2961 maybeFixWritePassword(R, m->owner_password);
2977 if ((R < 4) || ((R == 4) && (!m->use_aes))) { 2962 if ((R < 4) || ((R == 4) && (!m->use_aes))) {
2978 if (!m->allow_weak_crypto) { 2963 if (!m->allow_weak_crypto) {
2979 QTC::TC("qpdf", "QPDFJob weak crypto error"); 2964 QTC::TC("qpdf", "QPDFJob weak crypto error");
2980 - *(this->m->cerr) 2965 + *this->m->log->getError()
2981 << this->m->message_prefix 2966 << this->m->message_prefix
2982 << ": refusing to write a file with RC4, a weak cryptographic " 2967 << ": refusing to write a file with RC4, a weak cryptographic "
2983 - "algorithm"  
2984 - << std::endl  
2985 - << "Please use 256-bit keys for better security." << std::endl  
2986 - << "Pass --allow-weak-crypto to enable writing insecure files."  
2987 - << std::endl 2968 + "algorithm\n"
  2969 + << "Please use 256-bit keys for better security.\n"
  2970 + << "Pass --allow-weak-crypto to enable writing insecure "
  2971 + "files.\n"
2988 << "See also " 2972 << "See also "
2989 - "https://qpdf.readthedocs.io/en/stable/weak-crypto.html"  
2990 - << std::endl; 2973 + "https://qpdf.readthedocs.io/en/stable/weak-crypto.html\n";
2991 throw std::runtime_error( 2974 throw std::runtime_error(
2992 "refusing to write a file with weak crypto"); 2975 "refusing to write a file with weak crypto");
2993 } 2976 }
@@ -3160,7 +3143,7 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w) @@ -3160,7 +3143,7 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
3160 std::shared_ptr<QPDFWriter::ProgressReporter>( 3143 std::shared_ptr<QPDFWriter::ProgressReporter>(
3161 // line-break 3144 // line-break
3162 new ProgressReporter( 3145 new ProgressReporter(
3163 - *(this->m->cout), 3146 + *this->m->log->getInfo(),
3164 this->m->message_prefix, 3147 this->m->message_prefix,
3165 m->outfilename.get()))); 3148 m->outfilename.get())));
3166 } 3149 }
@@ -3260,8 +3243,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf, bool&amp; warnings) @@ -3260,8 +3243,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf, bool&amp; warnings)
3260 QPDFWriter w(outpdf, outfile.c_str()); 3243 QPDFWriter w(outpdf, outfile.c_str());
3261 setWriterOptions(outpdf, w); 3244 setWriterOptions(outpdf, w);
3262 w.write(); 3245 w.write();
3263 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
3264 - cout << prefix << ": wrote file " << outfile << std::endl; 3246 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  3247 + v << prefix << ": wrote file " << outfile << "\n";
3265 }); 3248 });
3266 if (outpdf.anyWarnings()) { 3249 if (outpdf.anyWarnings()) {
3267 warnings = true; 3250 warnings = true;
@@ -3295,8 +3278,8 @@ QPDFJob::writeOutfile(QPDF&amp; pdf) @@ -3295,8 +3278,8 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3295 w.write(); 3278 w.write();
3296 } 3279 }
3297 if (m->outfilename) { 3280 if (m->outfilename) {
3298 - doIfVerbose([&](std::ostream& cout, std::string const& prefix) {  
3299 - cout << prefix << ": wrote file " << m->outfilename << std::endl; 3281 + doIfVerbose([&](Pipeline& v, std::string const& prefix) {
  3282 + v << prefix << ": wrote file " << m->outfilename.get() << "\n";
3300 }); 3283 });
3301 } 3284 }
3302 if (m->replace_input) { 3285 if (m->replace_input) {
@@ -3313,18 +3296,19 @@ QPDFJob::writeOutfile(QPDF&amp; pdf) @@ -3313,18 +3296,19 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3313 QUtil::rename_file(m->infilename.get(), backup.c_str()); 3296 QUtil::rename_file(m->infilename.get(), backup.c_str());
3314 QUtil::rename_file(temp_out.get(), m->infilename.get()); 3297 QUtil::rename_file(temp_out.get(), m->infilename.get());
3315 if (warnings) { 3298 if (warnings) {
3316 - *(this->m->cerr) << this->m->message_prefix  
3317 - << ": there are warnings; original file kept in "  
3318 - << backup << std::endl; 3299 + *this->m->log->getError()
  3300 + << this->m->message_prefix
  3301 + << ": there are warnings; original file kept in " << backup
  3302 + << "\n";
3319 } else { 3303 } else {
3320 try { 3304 try {
3321 QUtil::remove_file(backup.c_str()); 3305 QUtil::remove_file(backup.c_str());
3322 } catch (QPDFSystemError& e) { 3306 } catch (QPDFSystemError& e) {
3323 - *(this->m->cerr) 3307 + *this->m->log->getError()
3324 << this->m->message_prefix 3308 << this->m->message_prefix
3325 << ": unable to delete original file (" << e.what() << ");" 3309 << ": unable to delete original file (" << e.what() << ");"
3326 << " original file left in " << backup 3310 << " original file left in " << backup
3327 - << ", but the input was successfully replaced" << std::endl; 3311 + << ", but the input was successfully replaced\n";
3328 } 3312 }
3329 } 3313 }
3330 } 3314 }
@@ -3353,7 +3337,7 @@ QPDFJob::writeJSON(QPDF&amp; pdf) @@ -3353,7 +3337,7 @@ QPDFJob::writeJSON(QPDF&amp; pdf)
3353 "name is unknown"); 3337 "name is unknown");
3354 } else { 3338 } else {
3355 QTC::TC("qpdf", "QPDFJob write json to stdout"); 3339 QTC::TC("qpdf", "QPDFJob write json to stdout");
3356 - fp = std::make_shared<Pl_OStream>("json output", *this->m->cout); 3340 + fp = this->m->log->getInfo();
3357 } 3341 }
3358 std::set<std::string> json_objects; 3342 std::set<std::string> json_objects;
3359 if (this->m->json_objects.count("trailer")) { 3343 if (this->m->json_objects.count("trailer")) {
libqpdf/QPDF_linearization.cc
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 #include <qpdf/Pl_Count.hh> 8 #include <qpdf/Pl_Count.hh>
9 #include <qpdf/Pl_Flate.hh> 9 #include <qpdf/Pl_Flate.hh>
10 #include <qpdf/QPDFExc.hh> 10 #include <qpdf/QPDFExc.hh>
  11 +#include <qpdf/QPDFLogger.hh>
11 #include <qpdf/QTC.hh> 12 #include <qpdf/QTC.hh>
12 #include <qpdf/QUtil.hh> 13 #include <qpdf/QUtil.hh>
13 14
@@ -72,9 +73,9 @@ QPDF::checkLinearization() @@ -72,9 +73,9 @@ QPDF::checkLinearization()
72 readLinearizationData(); 73 readLinearizationData();
73 result = checkLinearizationInternal(); 74 result = checkLinearizationInternal();
74 } catch (std::runtime_error& e) { 75 } catch (std::runtime_error& e) {
75 - *this->m->err_stream 76 + *this->m->log->getError()
76 << "WARNING: error encountered while checking linearization data: " 77 << "WARNING: error encountered while checking linearization data: "
77 - << e.what() << std::endl; 78 + << e.what() << "\n";
78 } 79 }
79 return result; 80 return result;
80 } 81 }
@@ -360,9 +361,9 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length) @@ -360,9 +361,9 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
360 } 361 }
361 qpdf_offset_t computed_end = offset + toO(length); 362 qpdf_offset_t computed_end = offset + toO(length);
362 if ((computed_end < min_end_offset) || (computed_end > max_end_offset)) { 363 if ((computed_end < min_end_offset) || (computed_end > max_end_offset)) {
363 - *this->m->err_stream << "expected = " << computed_end  
364 - << "; actual = " << min_end_offset << ".."  
365 - << max_end_offset << std::endl; 364 + *this->m->log->getError()
  365 + << "expected = " << computed_end << "; actual = " << min_end_offset
  366 + << ".." << max_end_offset << "\n";
366 throw QPDFExc( 367 throw QPDFExc(
367 qpdf_e_damaged_pdf, 368 qpdf_e_damaged_pdf,
368 this->m->file->getName(), 369 this->m->file->getName(),
@@ -643,14 +644,14 @@ QPDF::checkLinearizationInternal() @@ -643,14 +644,14 @@ QPDF::checkLinearizationInternal()
643 if (!errors.empty()) { 644 if (!errors.empty()) {
644 result = false; 645 result = false;
645 for (auto const& error: errors) { 646 for (auto const& error: errors) {
646 - *this->m->err_stream << "WARNING: " << error << std::endl; 647 + *this->m->log->getError() << "WARNING: " << error << "\n";
647 } 648 }
648 } 649 }
649 650
650 if (!warnings.empty()) { 651 if (!warnings.empty()) {
651 result = false; 652 result = false;
652 for (auto const& warning: warnings) { 653 for (auto const& warning: warnings) {
653 - *this->m->err_stream << "WARNING: " << warning << std::endl; 654 + *this->m->log->getError() << "WARNING: " << warning << "\n";
654 } 655 }
655 } 656 }
656 657
@@ -1013,39 +1014,34 @@ QPDF::showLinearizationData() @@ -1013,39 +1014,34 @@ QPDF::showLinearizationData()
1013 checkLinearizationInternal(); 1014 checkLinearizationInternal();
1014 dumpLinearizationDataInternal(); 1015 dumpLinearizationDataInternal();
1015 } catch (QPDFExc& e) { 1016 } catch (QPDFExc& e) {
1016 - *this->m->err_stream << e.what() << std::endl; 1017 + *this->m->log->getError() << e.what() << "\n";
1017 } 1018 }
1018 } 1019 }
1019 1020
1020 void 1021 void
1021 QPDF::dumpLinearizationDataInternal() 1022 QPDF::dumpLinearizationDataInternal()
1022 { 1023 {
1023 - *this->m->out_stream << this->m->file->getName()  
1024 - << ": linearization data:" << std::endl  
1025 - << std::endl;  
1026 -  
1027 - *this->m->out_stream  
1028 - << "file_size: " << this->m->linp.file_size << std::endl  
1029 - << "first_page_object: " << this->m->linp.first_page_object << std::endl  
1030 - << "first_page_end: " << this->m->linp.first_page_end << std::endl  
1031 - << "npages: " << this->m->linp.npages << std::endl  
1032 - << "xref_zero_offset: " << this->m->linp.xref_zero_offset << std::endl  
1033 - << "first_page: " << this->m->linp.first_page << std::endl  
1034 - << "H_offset: " << this->m->linp.H_offset << std::endl  
1035 - << "H_length: " << this->m->linp.H_length << std::endl  
1036 - << std::endl;  
1037 -  
1038 - *this->m->out_stream << "Page Offsets Hint Table" << std::endl << std::endl; 1024 + *this->m->log->getInfo()
  1025 + << this->m->file->getName() << ": linearization data:\n\n";
  1026 +
  1027 + *this->m->log->getInfo()
  1028 + << "file_size: " << this->m->linp.file_size << "\n"
  1029 + << "first_page_object: " << this->m->linp.first_page_object << "\n"
  1030 + << "first_page_end: " << this->m->linp.first_page_end << "\n"
  1031 + << "npages: " << this->m->linp.npages << "\n"
  1032 + << "xref_zero_offset: " << this->m->linp.xref_zero_offset << "\n"
  1033 + << "first_page: " << this->m->linp.first_page << "\n"
  1034 + << "H_offset: " << this->m->linp.H_offset << "\n"
  1035 + << "H_length: " << this->m->linp.H_length << "\n"
  1036 + << "\n";
  1037 +
  1038 + *this->m->log->getInfo() << "Page Offsets Hint Table\n\n";
1039 dumpHPageOffset(); 1039 dumpHPageOffset();
1040 - *this->m->out_stream << std::endl  
1041 - << "Shared Objects Hint Table" << std::endl  
1042 - << std::endl; 1040 + *this->m->log->getInfo() << "\nShared Objects Hint Table\n\n";
1043 dumpHSharedObject(); 1041 dumpHSharedObject();
1044 1042
1045 if (this->m->outline_hints.nobjects > 0) { 1043 if (this->m->outline_hints.nobjects > 0) {
1046 - *this->m->out_stream << std::endl  
1047 - << "Outlines Hint Table" << std::endl  
1048 - << std::endl; 1044 + *this->m->log->getInfo() << "\nOutlines Hint Table\n\n";
1049 dumpHGeneric(this->m->outline_hints); 1045 dumpHGeneric(this->m->outline_hints);
1050 } 1046 }
1051 } 1047 }
@@ -1066,42 +1062,41 @@ void @@ -1066,42 +1062,41 @@ void
1066 QPDF::dumpHPageOffset() 1062 QPDF::dumpHPageOffset()
1067 { 1063 {
1068 HPageOffset& t = this->m->page_offset_hints; 1064 HPageOffset& t = this->m->page_offset_hints;
1069 - *this->m->out_stream  
1070 - << "min_nobjects: " << t.min_nobjects << std::endl  
1071 - << "first_page_offset: " << adjusted_offset(t.first_page_offset)  
1072 - << std::endl  
1073 - << "nbits_delta_nobjects: " << t.nbits_delta_nobjects << std::endl  
1074 - << "min_page_length: " << t.min_page_length << std::endl  
1075 - << "nbits_delta_page_length: " << t.nbits_delta_page_length << std::endl  
1076 - << "min_content_offset: " << t.min_content_offset << std::endl 1065 + *this->m->log->getInfo()
  1066 + << "min_nobjects: " << t.min_nobjects << "\n"
  1067 + << "first_page_offset: " << adjusted_offset(t.first_page_offset) << "\n"
  1068 + << "nbits_delta_nobjects: " << t.nbits_delta_nobjects << "\n"
  1069 + << "min_page_length: " << t.min_page_length << "\n"
  1070 + << "nbits_delta_page_length: " << t.nbits_delta_page_length << "\n"
  1071 + << "min_content_offset: " << t.min_content_offset << "\n"
1077 << "nbits_delta_content_offset: " << t.nbits_delta_content_offset 1072 << "nbits_delta_content_offset: " << t.nbits_delta_content_offset
1078 - << std::endl  
1079 - << "min_content_length: " << t.min_content_length << std::endl 1073 + << "\n"
  1074 + << "min_content_length: " << t.min_content_length << "\n"
1080 << "nbits_delta_content_length: " << t.nbits_delta_content_length 1075 << "nbits_delta_content_length: " << t.nbits_delta_content_length
1081 - << std::endl  
1082 - << "nbits_nshared_objects: " << t.nbits_nshared_objects << std::endl  
1083 - << "nbits_shared_identifier: " << t.nbits_shared_identifier << std::endl  
1084 - << "nbits_shared_numerator: " << t.nbits_shared_numerator << std::endl  
1085 - << "shared_denominator: " << t.shared_denominator << std::endl; 1076 + << "\n"
  1077 + << "nbits_nshared_objects: " << t.nbits_nshared_objects << "\n"
  1078 + << "nbits_shared_identifier: " << t.nbits_shared_identifier << "\n"
  1079 + << "nbits_shared_numerator: " << t.nbits_shared_numerator << "\n"
  1080 + << "shared_denominator: " << t.shared_denominator << "\n";
1086 1081
1087 for (size_t i1 = 0; i1 < toS(this->m->linp.npages); ++i1) { 1082 for (size_t i1 = 0; i1 < toS(this->m->linp.npages); ++i1) {
1088 HPageOffsetEntry& pe = t.entries.at(i1); 1083 HPageOffsetEntry& pe = t.entries.at(i1);
1089 - *this->m->out_stream  
1090 - << "Page " << i1 << ":" << std::endl  
1091 - << " nobjects: " << pe.delta_nobjects + t.min_nobjects << std::endl 1084 + *this->m->log->getInfo()
  1085 + << "Page " << i1 << ":\n"
  1086 + << " nobjects: " << pe.delta_nobjects + t.min_nobjects << "\n"
1092 << " length: " << pe.delta_page_length + t.min_page_length 1087 << " length: " << pe.delta_page_length + t.min_page_length
1093 - << std::endl 1088 + << "\n"
1094 // content offset is relative to page, not file 1089 // content offset is relative to page, not file
1095 << " content_offset: " 1090 << " content_offset: "
1096 - << pe.delta_content_offset + t.min_content_offset << std::endl 1091 + << pe.delta_content_offset + t.min_content_offset << "\n"
1097 << " content_length: " 1092 << " content_length: "
1098 - << pe.delta_content_length + t.min_content_length << std::endl  
1099 - << " nshared_objects: " << pe.nshared_objects << std::endl; 1093 + << pe.delta_content_length + t.min_content_length << "\n"
  1094 + << " nshared_objects: " << pe.nshared_objects << "\n";
1100 for (size_t i2 = 0; i2 < toS(pe.nshared_objects); ++i2) { 1095 for (size_t i2 = 0; i2 < toS(pe.nshared_objects); ++i2) {
1101 - *this->m->out_stream << " identifier " << i2 << ": "  
1102 - << pe.shared_identifiers.at(i2) << std::endl;  
1103 - *this->m->out_stream << " numerator " << i2 << ": "  
1104 - << pe.shared_numerators.at(i2) << std::endl; 1096 + *this->m->log->getInfo() << " identifier " << i2 << ": "
  1097 + << pe.shared_identifiers.at(i2) << "\n";
  1098 + *this->m->log->getInfo() << " numerator " << i2 << ": "
  1099 + << pe.shared_numerators.at(i2) << "\n";
1105 } 1100 }
1106 } 1101 }
1107 } 1102 }
@@ -1110,33 +1105,30 @@ void @@ -1110,33 +1105,30 @@ void
1110 QPDF::dumpHSharedObject() 1105 QPDF::dumpHSharedObject()
1111 { 1106 {
1112 HSharedObject& t = this->m->shared_object_hints; 1107 HSharedObject& t = this->m->shared_object_hints;
1113 - *this->m->out_stream << "first_shared_obj: " << t.first_shared_obj  
1114 - << std::endl  
1115 - << "first_shared_offset: "  
1116 - << adjusted_offset(t.first_shared_offset) << std::endl  
1117 - << "nshared_first_page: " << t.nshared_first_page  
1118 - << std::endl  
1119 - << "nshared_total: " << t.nshared_total << std::endl  
1120 - << "nbits_nobjects: " << t.nbits_nobjects << std::endl  
1121 - << "min_group_length: " << t.min_group_length  
1122 - << std::endl  
1123 - << "nbits_delta_group_length: "  
1124 - << t.nbits_delta_group_length << std::endl; 1108 + *this->m->log->getInfo()
  1109 + << "first_shared_obj: " << t.first_shared_obj << "\n"
  1110 + << "first_shared_offset: " << adjusted_offset(t.first_shared_offset)
  1111 + << "\n"
  1112 + << "nshared_first_page: " << t.nshared_first_page << "\n"
  1113 + << "nshared_total: " << t.nshared_total << "\n"
  1114 + << "nbits_nobjects: " << t.nbits_nobjects << "\n"
  1115 + << "min_group_length: " << t.min_group_length << "\n"
  1116 + << "nbits_delta_group_length: " << t.nbits_delta_group_length << "\n";
1125 1117
1126 for (size_t i = 0; i < toS(t.nshared_total); ++i) { 1118 for (size_t i = 0; i < toS(t.nshared_total); ++i) {
1127 HSharedObjectEntry& se = t.entries.at(i); 1119 HSharedObjectEntry& se = t.entries.at(i);
1128 - *this->m->out_stream  
1129 - << "Shared Object " << i << ":" << std::endl 1120 + *this->m->log->getInfo()
  1121 + << "Shared Object " << i << ":\n"
1130 << " group length: " << se.delta_group_length + t.min_group_length 1122 << " group length: " << se.delta_group_length + t.min_group_length
1131 - << std::endl; 1123 + << "\n";
1132 // PDF spec says signature present nobjects_minus_one are 1124 // PDF spec says signature present nobjects_minus_one are
1133 // always 0, so print them only if they have a non-zero value. 1125 // always 0, so print them only if they have a non-zero value.
1134 if (se.signature_present) { 1126 if (se.signature_present) {
1135 - *this->m->out_stream << " signature present" << std::endl; 1127 + *this->m->log->getInfo() << " signature present\n";
1136 } 1128 }
1137 if (se.nobjects_minus_one != 0) { 1129 if (se.nobjects_minus_one != 0) {
1138 - *this->m->out_stream << " nobjects: " << se.nobjects_minus_one + 1  
1139 - << std::endl; 1130 + *this->m->log->getInfo()
  1131 + << " nobjects: " << se.nobjects_minus_one + 1 << "\n";
1140 } 1132 }
1141 } 1133 }
1142 } 1134 }
@@ -1144,11 +1136,12 @@ QPDF::dumpHSharedObject() @@ -1144,11 +1136,12 @@ QPDF::dumpHSharedObject()
1144 void 1136 void
1145 QPDF::dumpHGeneric(HGeneric& t) 1137 QPDF::dumpHGeneric(HGeneric& t)
1146 { 1138 {
1147 - *this->m->out_stream << "first_object: " << t.first_object << std::endl  
1148 - << "first_object_offset: "  
1149 - << adjusted_offset(t.first_object_offset) << std::endl  
1150 - << "nobjects: " << t.nobjects << std::endl  
1151 - << "group_length: " << t.group_length << std::endl; 1139 + *this->m->log->getInfo()
  1140 + << "first_object: " << t.first_object << "\n"
  1141 + << "first_object_offset: " << adjusted_offset(t.first_object_offset)
  1142 + << "\n"
  1143 + << "nobjects: " << t.nobjects << "\n"
  1144 + << "group_length: " << t.group_length << "\n";
1152 } 1145 }
1153 1146
1154 QPDFObjectHandle 1147 QPDFObjectHandle
manual/release-notes.rst
@@ -103,6 +103,10 @@ For a detailed list of changes, please see the file @@ -103,6 +103,10 @@ For a detailed list of changes, please see the file
103 ``QPDFNumberTreeObjectHelper`` constructors that don't take a 103 ``QPDFNumberTreeObjectHelper`` constructors that don't take a
104 ``QPDF&`` argument. 104 ``QPDF&`` argument.
105 105
  106 + - The function passed to and called by ``QPDFJob::doIfVerbose``
  107 + now takes a ``Pipeline&` argument instead of a ``std::ostream&``
  108 + argument.
  109 +
106 - Intentionally break API to call attention to operations that 110 - Intentionally break API to call attention to operations that
107 write files with insecure encryption: 111 write files with insecure encryption:
108 112
qpdf/test_driver.cc
@@ -570,7 +570,17 @@ test_11(QPDF&amp; pdf, char const* arg2) @@ -570,7 +570,17 @@ test_11(QPDF&amp; pdf, char const* arg2)
570 static void 570 static void
571 test_12(QPDF& pdf, char const* arg2) 571 test_12(QPDF& pdf, char const* arg2)
572 { 572 {
  573 +#ifdef _MSC_VER
  574 +# pragma warning(disable : 4996)
  575 +#endif
  576 +#if (defined(__GNUC__) || defined(__clang__))
  577 +# pragma GCC diagnostic push
  578 +# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  579 +#endif
573 pdf.setOutputStreams(0, 0); 580 pdf.setOutputStreams(0, 0);
  581 +#if (defined(__GNUC__) || defined(__clang__))
  582 +# pragma GCC diagnostic pop
  583 +#endif
574 pdf.showLinearizationData(); 584 pdf.showLinearizationData();
575 } 585 }
576 586
@@ -579,7 +589,17 @@ test_13(QPDF&amp; pdf, char const* arg2) @@ -579,7 +589,17 @@ test_13(QPDF&amp; pdf, char const* arg2)
579 { 589 {
580 std::ostringstream out; 590 std::ostringstream out;
581 std::ostringstream err; 591 std::ostringstream err;
  592 +#ifdef _MSC_VER
  593 +# pragma warning(disable : 4996)
  594 +#endif
  595 +#if (defined(__GNUC__) || defined(__clang__))
  596 +# pragma GCC diagnostic push
  597 +# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  598 +#endif
582 pdf.setOutputStreams(&out, &err); 599 pdf.setOutputStreams(&out, &err);
  600 +#if (defined(__GNUC__) || defined(__clang__))
  601 +# pragma GCC diagnostic pop
  602 +#endif
583 pdf.showLinearizationData(); 603 pdf.showLinearizationData();
584 std::cout << "---output---" << std::endl 604 std::cout << "---output---" << std::endl
585 << out.str() << "---error---" << std::endl 605 << out.str() << "---error---" << std::endl
@@ -2951,7 +2971,17 @@ test_84(QPDF&amp; pdf, char const* arg2) @@ -2951,7 +2971,17 @@ test_84(QPDF&amp; pdf, char const* arg2)
2951 std::ostringstream cerr; 2971 std::ostringstream cerr;
2952 { 2972 {
2953 QPDFJob j; 2973 QPDFJob j;
  2974 +#ifdef _MSC_VER
  2975 +# pragma warning(disable : 4996)
  2976 +#endif
  2977 +#if (defined(__GNUC__) || defined(__clang__))
  2978 +# pragma GCC diagnostic push
  2979 +# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  2980 +#endif
2954 j.setOutputStreams(&cout, &cerr); 2981 j.setOutputStreams(&cout, &cerr);
  2982 +#if (defined(__GNUC__) || defined(__clang__))
  2983 +# pragma GCC diagnostic pop
  2984 +#endif
2955 j.config() 2985 j.config()
2956 ->inputFile("bad2.pdf") 2986 ->inputFile("bad2.pdf")
2957 ->showObject("4,0") 2987 ->showObject("4,0")