Commit 641e92c6a7662a01f488947c3791f3b77e85517f

Authored by Jay Berkenbilt
1 parent f1f71196

QPDF, QPDFJob: use QPDFLogger instead of custom output streams

ChangeLog
1 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 7 * Add integer types to pipeline's operator<<: short, int, long,
4 8 long long, unsigned short, unsigned int, unsigned long, unsigned
5 9 long long.
... ...
README-maintainer
... ... @@ -512,6 +512,28 @@ to the owner of the parent directory source tree.
512 512 Note: this will leave some extra files (like .bash_history) in the
513 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 538 LOCAL WINDOWS TESTING PROCEDURE
517 539  
... ...
include/qpdf/QPDF.hh
... ... @@ -48,6 +48,7 @@
48 48 class QPDF_Stream;
49 49 class BitStream;
50 50 class BitWriter;
  51 +class QPDFLogger;
51 52  
52 53 class QPDF
53 54 {
... ... @@ -209,20 +210,30 @@ class QPDF
209 210  
210 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 238 // If true, ignore any cross-reference streams in a hybrid file
228 239 // (one that contains both cross-reference streams and
... ... @@ -618,18 +629,17 @@ class QPDF
618 629  
619 630 // Performs various sanity checks on a linearized file. Return
620 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 635 QPDF_DLL
625 636 bool checkLinearization();
626 637  
627 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 643 QPDF_DLL
634 644 void showLinearizationData();
635 645  
... ... @@ -1661,6 +1671,7 @@ class QPDF
1661 1671 Members();
1662 1672 Members(Members const&) = delete;
1663 1673  
  1674 + std::shared_ptr<QPDFLogger> log;
1664 1675 unsigned long long unique_id;
1665 1676 QPDFTokenizer tokenizer;
1666 1677 std::shared_ptr<InputSource> file;
... ... @@ -1668,8 +1679,6 @@ class QPDF
1668 1679 bool provided_password_is_hex_key;
1669 1680 bool ignore_xref_streams;
1670 1681 bool suppress_warnings;
1671   - std::ostream* out_stream;
1672   - std::ostream* err_stream;
1673 1682 bool attempt_recovery;
1674 1683 std::shared_ptr<EncryptionParameters> encp;
1675 1684 std::string pdf_version;
... ...
include/qpdf/QPDFJob.hh
... ... @@ -41,6 +41,7 @@
41 41  
42 42 class QPDFWriter;
43 43 class Pipeline;
  44 +class QPDFLogger;
44 45  
45 46 class QPDFJob
46 47 {
... ... @@ -108,10 +109,28 @@ class QPDFJob
108 109 QPDF_DLL
109 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 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 135 // Check to make sure no contradictory options have been
117 136 // specified. This is called automatically after initializing from
... ... @@ -393,8 +412,8 @@ class QPDFJob
393 412 // If in verbose mode, call the given function, passing in the
394 413 // output stream and message prefix.
395 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 418 // Provide a string that is the help information ("schema" for the
400 419 // qpdf-specific JSON object) for version 1 of the JSON output.
... ... @@ -548,10 +567,9 @@ class QPDFJob
548 567 Members();
549 568 Members(Members const&) = delete;
550 569  
  570 + std::shared_ptr<QPDFLogger> log;
551 571 std::string message_prefix;
552 572 bool warnings;
553   - std::ostream* cout;
554   - std::ostream* cerr;
555 573 unsigned long encryption_status;
556 574 bool verbose;
557 575 std::shared_ptr<char> password;
... ...
libqpdf/QPDF.cc
... ... @@ -18,14 +18,15 @@
18 18 #include <qpdf/OffsetInputSource.hh>
19 19 #include <qpdf/Pipeline.hh>
20 20 #include <qpdf/Pl_Discard.hh>
21   -#include <qpdf/QTC.hh>
22   -#include <qpdf/QUtil.hh>
23   -
  21 +#include <qpdf/Pl_OStream.hh>
24 22 #include <qpdf/QPDFExc.hh>
  23 +#include <qpdf/QPDFLogger.hh>
25 24 #include <qpdf/QPDF_Array.hh>
26 25 #include <qpdf/QPDF_Dictionary.hh>
27 26 #include <qpdf/QPDF_Null.hh>
28 27 #include <qpdf/QPDF_Stream.hh>
  28 +#include <qpdf/QTC.hh>
  29 +#include <qpdf/QUtil.hh>
29 30  
30 31 // This must be a fixed value. This API returns a const reference to
31 32 // it, and the C API relies on its being static as well.
... ... @@ -212,13 +213,12 @@ QPDF::EncryptionParameters::EncryptionParameters() :
212 213 }
213 214  
214 215 QPDF::Members::Members() :
  216 + log(QPDFLogger::defaultLogger()),
215 217 unique_id(0),
216 218 file(new InvalidInputSource()),
217 219 provided_password_is_hex_key(false),
218 220 ignore_xref_streams(false),
219 221 suppress_warnings(false),
220   - out_stream(&std::cout),
221   - err_stream(&std::cerr),
222 222 attempt_recovery(true),
223 223 encp(new EncryptionParameters),
224 224 pushed_inherited_attributes_to_pages(false),
... ... @@ -339,11 +339,23 @@ QPDF::setIgnoreXRefStreams(bool val)
339 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 354 void
343 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 361 void
... ... @@ -533,8 +545,8 @@ QPDF::warn(QPDFExc const&amp; e)
533 545 {
534 546 this->m->warnings.push_back(e);
535 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 1357 void
1346 1358 QPDF::showXRefTable()
1347 1359 {
  1360 + auto& cout = *this->m->log->getInfo();
1348 1361 for (auto const& iter: this->m->xref_table) {
1349 1362 QPDFObjGen const& og = iter.first;
1350 1363 QPDFXRefEntry const& entry = iter.second;
1351   - *this->m->out_stream << og.getObj() << "/" << og.getGen() << ": ";
  1364 + cout << og.getObj() << "/" << og.getGen() << ": ";
1352 1365 switch (entry.getType()) {
1353 1366 case 1:
1354   - *this->m->out_stream << "uncompressed; offset = "
1355   - << entry.getOffset();
  1367 + cout << "uncompressed; offset = " << entry.getOffset();
1356 1368 break;
1357 1369  
1358 1370 case 2:
1359   - *this->m->out_stream
  1371 + *this->m->log->getInfo()
1360 1372 << "compressed; stream = " << entry.getObjStreamNumber()
1361 1373 << ", index = " << entry.getObjStreamIndex();
1362 1374 break;
... ... @@ -1366,7 +1378,7 @@ QPDF::showXRefTable()
1366 1378 " showing xref_table");
1367 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 17 #include <qpdf/Pl_OStream.hh>
18 18 #include <qpdf/Pl_StdioFile.hh>
19 19 #include <qpdf/Pl_String.hh>
20   -#include <qpdf/QTC.hh>
21   -#include <qpdf/QUtil.hh>
22   -
23 20 #include <qpdf/QIntC.hh>
24 21 #include <qpdf/QPDF.hh>
25 22 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
... ... @@ -27,6 +24,7 @@
27 24 #include <qpdf/QPDFCryptoProvider.hh>
28 25 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
29 26 #include <qpdf/QPDFExc.hh>
  27 +#include <qpdf/QPDFLogger.hh>
30 28 #include <qpdf/QPDFOutlineDocumentHelper.hh>
31 29 #include <qpdf/QPDFPageDocumentHelper.hh>
32 30 #include <qpdf/QPDFPageLabelDocumentHelper.hh>
... ... @@ -34,6 +32,8 @@
34 32 #include <qpdf/QPDFSystemError.hh>
35 33 #include <qpdf/QPDFUsage.hh>
36 34 #include <qpdf/QPDFWriter.hh>
  35 +#include <qpdf/QTC.hh>
  36 +#include <qpdf/QUtil.hh>
37 37  
38 38 #include <qpdf/auto_job_schema.hh> // JOB_SCHEMA_DATA
39 39  
... ... @@ -93,10 +93,8 @@ namespace
93 93 {
94 94 public:
95 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 98 prefix(prefix),
101 99 filename(filename)
102 100 {
... ... @@ -105,7 +103,7 @@ namespace
105 103 virtual void reportProgress(int);
106 104  
107 105 private:
108   - std::ostream& cout;
  106 + Pipeline& p;
109 107 std::string prefix;
110 108 std::string filename;
111 109 };
... ... @@ -135,10 +133,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
135 133 QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace");
136 134 if (!(w_obj.isNumber() && h_obj.isNumber())) {
137 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 142 return result;
... ... @@ -147,10 +145,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
147 145 if (!(components_obj.isInteger() && (components_obj.getIntValue() == 8))) {
148 146 QTC::TC("qpdf", "QPDFJob image optimize bits per component");
149 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 154 return result;
... ... @@ -185,10 +183,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
185 183 } else {
186 184 QTC::TC("qpdf", "QPDFJob image optimize colorspace");
187 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 192 return result;
... ... @@ -198,11 +196,10 @@ ImageOptimizer::makePipeline(std::string const&amp; description, Pipeline* next)
198 196 ((this->oi_min_area > 0) && ((w * h) <= this->oi_min_area))) {
199 197 QTC::TC("qpdf", "QPDFJob image optimize too small");
200 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 205 return result;
... ... @@ -217,10 +214,10 @@ ImageOptimizer::evaluate(std::string const&amp; description)
217 214 {
218 215 if (!image.pipeStreamData(0, 0, qpdf_dl_specialized, true)) {
219 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 222 return false;
226 223 }
... ... @@ -237,17 +234,17 @@ ImageOptimizer::evaluate(std::string const&amp; description)
237 234 long long orig_length = image.getDict().getKey("/Length").getIntValue();
238 235 if (c.getCount() >= orig_length) {
239 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 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 249 return true;
253 250 }
... ... @@ -304,8 +301,8 @@ QPDFPageData::QPDFPageData(QPDFPageData const&amp; other, int page) :
304 301 void
305 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 308 // These default values are duplicated in help and docs.
... ... @@ -316,10 +313,9 @@ static int constexpr DEFAULT_OI_MIN_AREA = 16384;
316 313 static int constexpr DEFAULT_II_MIN_BYTES = 1024;
317 314  
318 315 QPDFJob::Members::Members() :
  316 + log(QPDFLogger::defaultLogger()),
319 317 message_prefix("qpdf"),
320 318 warnings(false),
321   - cout(&std::cout),
322   - cerr(&std::cerr),
323 319 encryption_status(0),
324 320 verbose(false),
325 321 password(0),
... ... @@ -442,19 +438,31 @@ QPDFJob::setMessagePrefix(std::string const&amp; message_prefix)
442 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 453 void
446 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 460 void
453 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 595 }
588 596 if (this->m->warnings && (!this->m->suppress_warnings)) {
589 597 if (createsOutput()) {
590   - (*this->m->cerr)
  598 + *this->m->log->getWarn()
591 599 << this->m->message_prefix
592 600 << ": operation succeeded with warnings;"
593   - << " resulting file may have some problems" << std::endl;
  601 + << " resulting file may have some problems\n";
594 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 729 void
723 730 QPDFJob::setQPDFOptions(QPDF& pdf)
724 731 {
725   - pdf.setOutputStreams(this->m->cout, this->m->cerr);
  732 + pdf.setLogger(this->m->log);
726 733 if (m->ignore_xref_streams) {
727 734 pdf.setIgnoreXRefStreams(true);
728 735 }
... ... @@ -778,50 +785,47 @@ QPDFJob::showEncryption(QPDF&amp; pdf)
778 785 QPDF::encryption_method_e stream_method = QPDF::e_unknown;
779 786 QPDF::encryption_method_e string_method = QPDF::e_unknown;
780 787 QPDF::encryption_method_e file_method = QPDF::e_unknown;
781   - auto& cout = *this->m->cout;
  788 + auto& cout = *this->m->log->getInfo();
782 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 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 794 std::string user_password = pdf.getTrimmedUserPassword();
788 795 std::string encryption_key = pdf.getEncryptionKey();
789   - cout << "User password = " << user_password << std::endl;
  796 + cout << "User password = " << user_password << "\n";
790 797 if (m->show_encryption_key) {
791 798 cout << "Encryption key = " << QUtil::hex_encode(encryption_key)
792   - << std::endl;
  799 + << "\n";
793 800 }
794 801 if (pdf.ownerPasswordMatched()) {
795   - cout << "Supplied password is owner password" << std::endl;
  802 + cout << "Supplied password is owner password\n";
796 803 }
797 804 if (pdf.userPasswordMatched()) {
798   - cout << "Supplied password is user password" << std::endl;
  805 + cout << "Supplied password is user password\n";
799 806 }
800 807 cout << "extract for accessibility: "
801   - << show_bool(pdf.allowAccessibility()) << std::endl
  808 + << show_bool(pdf.allowAccessibility()) << "\n"
802 809 << "extract for any purpose: " << show_bool(pdf.allowExtractAll())
803   - << std::endl
  810 + << "\n"
804 811 << "print low resolution: " << show_bool(pdf.allowPrintLowRes())
805   - << std::endl
  812 + << "\n"
806 813 << "print high resolution: " << show_bool(pdf.allowPrintHighRes())
807   - << std::endl
  814 + << "\n"
808 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 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 822 if (V >= 4) {
819 823 cout << "stream encryption method: "
820   - << show_encryption_method(stream_method) << std::endl
  824 + << show_encryption_method(stream_method) << "\n"
821 825 << "string encryption method: "
822   - << show_encryption_method(string_method) << std::endl
  826 + << show_encryption_method(string_method) << "\n"
823 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 839 // errors.
836 840 bool okay = true;
837 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 844 try {
841 845 int extension_level = pdf.getExtensionLevel();
842 846 cout << "PDF Version: " << pdf.getPDFVersion();
843 847 if (extension_level > 0) {
844 848 cout << " extension level " << pdf.getExtensionLevel();
845 849 }
846   - cout << std::endl;
  850 + cout << "\n";
847 851 showEncryption(pdf);
848 852 if (pdf.isLinearized()) {
849 853 cout << "File is linearized\n";
... ... @@ -875,12 +879,12 @@ QPDFJob::doCheck(QPDF&amp; pdf)
875 879 page.parseContents(&discard_contents);
876 880 } catch (QPDFExc& e) {
877 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 886 } catch (std::exception& e) {
883   - (*this->m->cerr) << "ERROR: " << e.what() << std::endl;
  887 + *this->m->log->getError() << "ERROR: " << e.what() << "\n";
884 888 okay = false;
885 889 }
886 890 if (!okay) {
... ... @@ -890,9 +894,9 @@ QPDFJob::doCheck(QPDF&amp; pdf)
890 894 if ((!pdf.getWarnings().empty()) || warnings) {
891 895 this->m->warnings = true;
892 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 926 filter ? qpdf_dl_all : qpdf_dl_none);
923 927 }
924 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 932 } else {
929   - *(this->m->cout) << obj.unparseResolved() << std::endl;
  933 + *this->m->log->getInfo() << obj.unparseResolved() << "\n";
930 934 }
931 935 if (error) {
932 936 throw std::runtime_error(
... ... @@ -938,17 +942,17 @@ void
938 942 QPDFJob::doShowPages(QPDF& pdf)
939 943 {
940 944 int pageno = 0;
941   - auto& cout = *this->m->cout;
  945 + auto& cout = *this->m->log->getInfo();
942 946 for (auto& ph: QPDFPageDocumentHelper(pdf).getAllPages()) {
943 947 QPDFObjectHandle page = ph.getObjectHandle();
944 948 ++pageno;
945 949  
946 950 cout << "page " << pageno << ": " << page.getObjectID() << " "
947   - << page.getGeneration() << " R" << std::endl;
  951 + << page.getGeneration() << " R\n";
948 952 if (m->show_page_images) {
949 953 std::map<std::string, QPDFObjectHandle> images = ph.getImages();
950 954 if (!images.empty()) {
951   - cout << " images:" << std::endl;
  955 + cout << " images:\n";
952 956 for (auto const& iter2: images) {
953 957 std::string const& name = iter2.first;
954 958 QPDFObjectHandle image = iter2.second;
... ... @@ -956,14 +960,14 @@ QPDFJob::doShowPages(QPDF&amp; pdf)
956 960 int width = dict.getKey("/Width").getIntValueAsInt();
957 961 int height = dict.getKey("/Height").getIntValueAsInt();
958 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 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 980 for (auto const& i: efdh.getEmbeddedFiles()) {
977 981 std::string const& key = i.first;
978 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 987 auto desc = efoh->getDescription();
984 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 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 997 for (auto i2: efoh->getEmbeddedFileStreams().ditems()) {
996 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 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 1817 std::list<std::string> errors;
1817 1818 JSON captured = JSON::parse(captured_json);
1818 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 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 1829 void
1830 1830 QPDFJob::doInspection(QPDF& pdf)
1831 1831 {
  1832 + auto& cout = *this->m->log->getInfo();
1832 1833 if (m->check) {
1833 1834 doCheck(pdf);
1834 1835 }
1835 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 1839 if (m->show_npages) {
1841 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 1844 if (m->show_encryption) {
1847 1845 showEncryption(pdf);
1848 1846 }
1849 1847 if (m->check_linearization) {
1850 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 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 1852 } else {
1857 1853 this->m->warnings = true;
1858 1854 }
... ... @@ -1861,8 +1857,7 @@ QPDFJob::doInspection(QPDF&amp; pdf)
1861 1857 if (pdf.isLinearized()) {
1862 1858 pdf.showLinearizationData();
1863 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 1863 if (m->show_xref) {
... ... @@ -1979,11 +1974,10 @@ QPDFJob::doProcess(
1979 1974 }
1980 1975 if (!warned) {
1981 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 2091 "/Resources", QPDFObjectHandle::newDictionary());
2098 2092 }
2099 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 2097 auto from_page = pages.at(QIntC::to_size(from_pageno - 1));
2104 2098 if (0 == fo.count(from_pageno)) {
... ... @@ -2187,12 +2181,12 @@ QPDFJob::handleUnderOverlay(QPDF&amp; pdf)
2187 2181 QPDFPageDocumentHelper main_pdh(pdf);
2188 2182 std::vector<QPDFPageObjectHelper> main_pages = main_pdh.getAllPages();
2189 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 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 2191 doUnderOverlayForPage(
2198 2192 pdf,
... ... @@ -2248,9 +2242,9 @@ QPDFJob::addAttachments(QPDF&amp; pdf)
2248 2242 }
2249 2243  
2250 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 2270 QPDFEmbeddedFileDocumentHelper efdh(pdf);
2277 2271 std::vector<std::string> duplicates;
2278 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 2277 auto other = processFile(
2284 2278 to_copy.path.c_str(), to_copy.password.c_str(), false, false);
... ... @@ -2294,9 +2288,8 @@ QPDFJob::copyAttachments(QPDF&amp; pdf)
2294 2288 pdf.copyForeignObject(iter.second->getObjectHandle());
2295 2289 efdh.replaceEmbeddedFile(
2296 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 2391 QPDFEmbeddedFileDocumentHelper efdh(pdf);
2399 2392 for (auto const& key: m->attachments_to_remove) {
2400 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 2397 } else {
2406 2398 throw std::runtime_error("attachment " + key + " not found");
... ... @@ -2437,9 +2429,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2437 2429 std::set<QPDFObjGen> resources_seen; // shared resources detection
2438 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 2437 std::list<QPDFObjectHandle> queue;
... ... @@ -2458,9 +2450,9 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2458 2450 // This is a non-leaf node.
2459 2451 if (dict.hasKey("/Resources")) {
2460 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 2457 return true;
2466 2458 }
... ... @@ -2475,13 +2467,12 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2475 2467 QPDFObjGen resources_og = resources.getObjGen();
2476 2468 if (resources_seen.count(resources_og)) {
2477 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 2476 return true;
2486 2477 }
2487 2478 resources_seen.insert(resources_og);
... ... @@ -2493,13 +2484,12 @@ QPDFJob::shouldRemoveUnreferencedResources(QPDF&amp; pdf)
2493 2484 QPDFObjGen xobject_og = xobject.getObjGen();
2494 2485 if (resources_seen.count(xobject_og)) {
2495 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 2493 return true;
2504 2494 }
2505 2495 resources_seen.insert(xobject_og);
... ... @@ -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 2511 return false;
2522 2512 }
... ... @@ -2567,9 +2557,9 @@ QPDFJob::handlePageSpecs(
2567 2557 "qpdf",
2568 2558 "QPDFJob automatically set keep files open",
2569 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 2587 QTC::TC("qpdf", "QPDFJob pages encryption password");
2598 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 2593 std::shared_ptr<InputSource> is;
2605 2594 ClosedFileInputSource* cis = 0;
... ... @@ -2660,9 +2649,8 @@ QPDFJob::handlePageSpecs(
2660 2649 // without changing their object numbers. This enables other
2661 2650 // things in the original file, such as outlines, to continue to
2662 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 2655 QPDFPageDocumentHelper dh(pdf);
2668 2656 std::vector<QPDFPageObjectHelper> orig_pages = dh.getAllPages();
... ... @@ -2720,9 +2708,8 @@ QPDFJob::handlePageSpecs(
2720 2708 if (pldh.hasPageLabels()) {
2721 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 2714 for (auto pageno_iter: page_data.selected_pages) {
2728 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 2897 std::string encoded;
2911 2898 if (QUtil::utf8_to_pdf_doc(password, encoded)) {
2912 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 2907 password = encoded;
2922 2908 } else {
2923 2909 QTC::TC("qpdf", "QPDFJob bytes fallback warning");
2924   - *(this->m->cerr)
  2910 + *this->m->log->getError()
2925 2911 << this->m->message_prefix << ": WARNING: "
2926 2912 << "supplied password looks like a Unicode"
2927 2913 << " password with characters not allowed in"
... ... @@ -2929,8 +2915,7 @@ QPDFJob::maybeFixWritePassword(int R, std::string&amp; password)
2929 2915 << " most readers will not be able to open this"
2930 2916 << " file with the supplied password."
2931 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 2920 } else if ((R >= 5) && (!is_valid_utf8)) {
2936 2921 QTC::TC("qpdf", "QPDFJob invalid utf-8 in auto");
... ... @@ -2968,26 +2953,24 @@ QPDFJob::setEncryptionOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
2968 2953 throw std::logic_error("bad encryption keylen");
2969 2954 }
2970 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 2960 maybeFixWritePassword(R, m->user_password);
2976 2961 maybeFixWritePassword(R, m->owner_password);
2977 2962 if ((R < 4) || ((R == 4) && (!m->use_aes))) {
2978 2963 if (!m->allow_weak_crypto) {
2979 2964 QTC::TC("qpdf", "QPDFJob weak crypto error");
2980   - *(this->m->cerr)
  2965 + *this->m->log->getError()
2981 2966 << this->m->message_prefix
2982 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 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 2974 throw std::runtime_error(
2992 2975 "refusing to write a file with weak crypto");
2993 2976 }
... ... @@ -3160,7 +3143,7 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
3160 3143 std::shared_ptr<QPDFWriter::ProgressReporter>(
3161 3144 // line-break
3162 3145 new ProgressReporter(
3163   - *(this->m->cout),
  3146 + *this->m->log->getInfo(),
3164 3147 this->m->message_prefix,
3165 3148 m->outfilename.get())));
3166 3149 }
... ... @@ -3260,8 +3243,8 @@ QPDFJob::doSplitPages(QPDF&amp; pdf, bool&amp; warnings)
3260 3243 QPDFWriter w(outpdf, outfile.c_str());
3261 3244 setWriterOptions(outpdf, w);
3262 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 3249 if (outpdf.anyWarnings()) {
3267 3250 warnings = true;
... ... @@ -3295,8 +3278,8 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3295 3278 w.write();
3296 3279 }
3297 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 3285 if (m->replace_input) {
... ... @@ -3313,18 +3296,19 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3313 3296 QUtil::rename_file(m->infilename.get(), backup.c_str());
3314 3297 QUtil::rename_file(temp_out.get(), m->infilename.get());
3315 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 3303 } else {
3320 3304 try {
3321 3305 QUtil::remove_file(backup.c_str());
3322 3306 } catch (QPDFSystemError& e) {
3323   - *(this->m->cerr)
  3307 + *this->m->log->getError()
3324 3308 << this->m->message_prefix
3325 3309 << ": unable to delete original file (" << e.what() << ");"
3326 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 3337 "name is unknown");
3354 3338 } else {
3355 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 3342 std::set<std::string> json_objects;
3359 3343 if (this->m->json_objects.count("trailer")) {
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -8,6 +8,7 @@
8 8 #include <qpdf/Pl_Count.hh>
9 9 #include <qpdf/Pl_Flate.hh>
10 10 #include <qpdf/QPDFExc.hh>
  11 +#include <qpdf/QPDFLogger.hh>
11 12 #include <qpdf/QTC.hh>
12 13 #include <qpdf/QUtil.hh>
13 14  
... ... @@ -72,9 +73,9 @@ QPDF::checkLinearization()
72 73 readLinearizationData();
73 74 result = checkLinearizationInternal();
74 75 } catch (std::runtime_error& e) {
75   - *this->m->err_stream
  76 + *this->m->log->getError()
76 77 << "WARNING: error encountered while checking linearization data: "
77   - << e.what() << std::endl;
  78 + << e.what() << "\n";
78 79 }
79 80 return result;
80 81 }
... ... @@ -360,9 +361,9 @@ QPDF::readHintStream(Pipeline&amp; pl, qpdf_offset_t offset, size_t length)
360 361 }
361 362 qpdf_offset_t computed_end = offset + toO(length);
362 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 367 throw QPDFExc(
367 368 qpdf_e_damaged_pdf,
368 369 this->m->file->getName(),
... ... @@ -643,14 +644,14 @@ QPDF::checkLinearizationInternal()
643 644 if (!errors.empty()) {
644 645 result = false;
645 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 651 if (!warnings.empty()) {
651 652 result = false;
652 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 1014 checkLinearizationInternal();
1014 1015 dumpLinearizationDataInternal();
1015 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 1021 void
1021 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 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 1041 dumpHSharedObject();
1044 1042  
1045 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 1045 dumpHGeneric(this->m->outline_hints);
1050 1046 }
1051 1047 }
... ... @@ -1066,42 +1062,41 @@ void
1066 1062 QPDF::dumpHPageOffset()
1067 1063 {
1068 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 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 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 1082 for (size_t i1 = 0; i1 < toS(this->m->linp.npages); ++i1) {
1088 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 1087 << " length: " << pe.delta_page_length + t.min_page_length
1093   - << std::endl
  1088 + << "\n"
1094 1089 // content offset is relative to page, not file
1095 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 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 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 1105 QPDF::dumpHSharedObject()
1111 1106 {
1112 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 1118 for (size_t i = 0; i < toS(t.nshared_total); ++i) {
1127 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 1122 << " group length: " << se.delta_group_length + t.min_group_length
1131   - << std::endl;
  1123 + << "\n";
1132 1124 // PDF spec says signature present nobjects_minus_one are
1133 1125 // always 0, so print them only if they have a non-zero value.
1134 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 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 1136 void
1145 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 1147 QPDFObjectHandle
... ...
manual/release-notes.rst
... ... @@ -103,6 +103,10 @@ For a detailed list of changes, please see the file
103 103 ``QPDFNumberTreeObjectHelper`` constructors that don't take a
104 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 110 - Intentionally break API to call attention to operations that
107 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 570 static void
571 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 580 pdf.setOutputStreams(0, 0);
  581 +#if (defined(__GNUC__) || defined(__clang__))
  582 +# pragma GCC diagnostic pop
  583 +#endif
574 584 pdf.showLinearizationData();
575 585 }
576 586  
... ... @@ -579,7 +589,17 @@ test_13(QPDF&amp; pdf, char const* arg2)
579 589 {
580 590 std::ostringstream out;
581 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 599 pdf.setOutputStreams(&out, &err);
  600 +#if (defined(__GNUC__) || defined(__clang__))
  601 +# pragma GCC diagnostic pop
  602 +#endif
583 603 pdf.showLinearizationData();
584 604 std::cout << "---output---" << std::endl
585 605 << out.str() << "---error---" << std::endl
... ... @@ -2951,7 +2971,17 @@ test_84(QPDF&amp; pdf, char const* arg2)
2951 2971 std::ostringstream cerr;
2952 2972 {
2953 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 2981 j.setOutputStreams(&cout, &cerr);
  2982 +#if (defined(__GNUC__) || defined(__clang__))
  2983 +# pragma GCC diagnostic pop
  2984 +#endif
2955 2985 j.config()
2956 2986 ->inputFile("bad2.pdf")
2957 2987 ->showObject("4,0")
... ...