Commit 83be2191b4f3eb8906160d61f61cae48532ee651

Authored by Jay Berkenbilt
1 parent 641e92c6

Use "save" logger when saving data to standard output

This includes the output PDF, streams from --show-object and
attachments from --save-attachment. This also enables --verbose and
--progress to work with saving to stdout.
ChangeLog
  1 +2022-06-18 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * When --progress or --verbose is combined with writing to
  4 + standard output, progress reporting and verbose messages go to
  5 + standard error. Previously it was disabled in this case.
  6 +
1 7 2022-06-05 Jay Berkenbilt <ejb@ql.org>
2 8  
3 9 * QPDFJob: API breaking change: QPDFJob::doIfVerbose passes a
... ...
... ... @@ -79,9 +79,6 @@ Find all places in the library that write to stdout/stderr/cout/cerr.
79 79 Also find places that raise exceptions if unable to warn. These should
80 80 use the global output writer.
81 81  
82   -Figure out a good way to use the save pipeline for QPDFWriter as well
83   -as saving attachments, showing stream data, etc.
84   -
85 82  
86 83 QPDFPagesTree
87 84 =============
... ...
libqpdf/QPDFJob.cc
... ... @@ -691,22 +691,21 @@ QPDFJob::checkConfiguration()
691 691 " before the -- that follows --encrypt.");
692 692 }
693 693  
  694 + bool save_to_stdout = false;
694 695 if (m->require_outfile && m->outfilename &&
695 696 (strcmp(m->outfilename.get(), "-") == 0)) {
696 697 if (m->split_pages) {
697 698 usage("--split-pages may not be used when"
698 699 " writing to standard output");
699 700 }
700   - if (this->m->verbose) {
701   - usage("--verbose may not be used when"
702   - " writing to standard output");
703   - }
704   - if (m->progress) {
705   - usage("--progress may not be used when"
706   - " writing to standard output");
707   - }
  701 + save_to_stdout = true;
  702 + }
  703 + if (!m->attachment_to_show.empty()) {
  704 + save_to_stdout = true;
  705 + }
  706 + if (save_to_stdout) {
  707 + this->m->log->saveToStandardOutput();
708 708 }
709   -
710 709 if ((!m->split_pages) &&
711 710 QUtil::same_file(m->infilename.get(), m->outfilename.get())) {
712 711 QTC::TC("qpdf", "QPDFJob same file error");
... ... @@ -918,10 +917,11 @@ QPDFJob::doShowObj(QPDF&amp; pdf)
918 917 obj.warnIfPossible("unable to filter stream data");
919 918 error = true;
920 919 } else {
921   - QUtil::binary_stdout();
922   - Pl_StdioFile out("stdout", stdout);
  920 + // If anything has been written to standard output,
  921 + // this will fail.
  922 + this->m->log->saveToStandardOutput();
923 923 obj.pipeStreamData(
924   - &out,
  924 + this->m->log->getSave().get(),
925 925 (filter && m->normalize) ? qpdf_ef_normalize : 0,
926 926 filter ? qpdf_dl_all : qpdf_dl_none);
927 927 }
... ... @@ -1023,9 +1023,10 @@ QPDFJob::doShowAttachment(QPDF&amp; pdf)
1023 1023 "attachment " + m->attachment_to_show + " not found");
1024 1024 }
1025 1025 auto efs = fs->getEmbeddedFileStream();
1026   - QUtil::binary_stdout();
1027   - Pl_StdioFile out("stdout", stdout);
1028   - efs.pipeStreamData(&out, 0, qpdf_dl_all);
  1026 + // saveToStandardOutput has already been called, but it's harmless
  1027 + // to call it again, so do as defensive coding.
  1028 + this->m->log->saveToStandardOutput();
  1029 + efs.pipeStreamData(this->m->log->getSave().get(), 0, qpdf_dl_all);
1029 1030 }
1030 1031  
1031 1032 void
... ... @@ -3138,14 +3139,17 @@ QPDFJob::setWriterOptions(QPDF&amp; pdf, QPDFWriter&amp; w)
3138 3139 parse_version(m->force_version, version, extension_level);
3139 3140 w.forcePDFVersion(version, extension_level);
3140 3141 }
3141   - if (m->progress && m->outfilename) {
  3142 + if (m->progress) {
  3143 + char const* outfilename = this->m->outfilename
  3144 + ? this->m->outfilename.get()
  3145 + : "standard output";
3142 3146 w.registerProgressReporter(
3143 3147 std::shared_ptr<QPDFWriter::ProgressReporter>(
3144 3148 // line-break
3145 3149 new ProgressReporter(
3146 3150 *this->m->log->getInfo(),
3147 3151 this->m->message_prefix,
3148   - m->outfilename.get())));
  3152 + outfilename)));
3149 3153 }
3150 3154 }
3151 3155  
... ... @@ -3273,7 +3277,15 @@ QPDFJob::writeOutfile(QPDF&amp; pdf)
3273 3277 } else {
3274 3278 // QPDFWriter must have block scope so the output file will be
3275 3279 // closed after write() finishes.
3276   - QPDFWriter w(pdf, m->outfilename.get());
  3280 + QPDFWriter w(pdf);
  3281 + if (this->m->outfilename) {
  3282 + w.setOutputFilename(m->outfilename.get());
  3283 + } else {
  3284 + // saveToStandardOutput has already been called, but
  3285 + // calling it again is defensive and harmless.
  3286 + this->m->log->saveToStandardOutput();
  3287 + w.setOutputPipeline(this->m->log->getSave().get());
  3288 + }
3277 3289 setWriterOptions(pdf, w);
3278 3290 w.write();
3279 3291 }
... ...
libqpdf/QPDFLogger.cc
... ... @@ -2,6 +2,7 @@
2 2  
3 3 #include <qpdf/Pl_Discard.hh>
4 4 #include <qpdf/Pl_OStream.hh>
  5 +#include <qpdf/QUtil.hh>
5 6 #include <iostream>
6 7 #include <stdexcept>
7 8  
... ... @@ -182,6 +183,9 @@ QPDFLogger::setError(std::shared_ptr&lt;Pipeline&gt; p)
182 183 void
183 184 QPDFLogger::setSave(std::shared_ptr<Pipeline> p)
184 185 {
  186 + if (this->m->p_save == p) {
  187 + return;
  188 + }
185 189 if (p == this->m->p_stdout) {
186 190 auto pt = dynamic_cast<Pl_Track*>(p.get());
187 191 if (pt->getUsed()) {
... ... @@ -192,6 +196,7 @@ QPDFLogger::setSave(std::shared_ptr&lt;Pipeline&gt; p)
192 196 if (this->m->p_info == this->m->p_stdout) {
193 197 this->m->p_info = this->m->p_stderr;
194 198 }
  199 + QUtil::binary_stdout();
195 200 }
196 201 this->m->p_save = p;
197 202 }
... ...
manual/release-notes.rst
... ... @@ -133,6 +133,10 @@ For a detailed list of changes, please see the file
133 133 user password is not recoverable from the owner password when
134 134 256-bit keys are in use.
135 135  
  136 + - ``--verbose`` and ``--progress`` may be now used when writing
  137 + the output PDF to standard output. In that case, the verbose and
  138 + progress messages are written to standard error.
  139 +
136 140 - Library Enhancements
137 141  
138 142 - New methods ``insertItemAndGet``, ``appendItemAndGet``,
... ...
qpdf/qtest/progress-reporting.test
... ... @@ -14,13 +14,23 @@ cleanup();
14 14  
15 15 my $td = new TestDriver('progress-reporting');
16 16  
17   -my $n_tests = 1;
  17 +my $n_tests = 3;
18 18  
19 19 $td->runtest("progress report on small file",
20   - {$td->COMMAND => "qpdf --progress minimal.pdf a.pdf",
  20 + {$td->COMMAND =>
  21 + "qpdf --progress --deterministic-id minimal.pdf a.pdf",
21 22 $td->FILTER => "perl filter-progress.pl"},
22 23 {$td->FILE => "small-progress.out", $td->EXIT_STATUS => 0},
23 24 $td->NORMALIZE_NEWLINES);
  25 +$td->runtest("progress report to stdout",
  26 + {$td->COMMAND =>
  27 + "qpdf --progress --deterministic-id minimal.pdf - > b.pdf",
  28 + $td->FILTER => "perl filter-progress.pl"},
  29 + {$td->FILE => "small-stdout-progress.out", $td->EXIT_STATUS => 0},
  30 + $td->NORMALIZE_NEWLINES);
  31 +$td->runtest("compare",
  32 + {$td->FILE => "a.pdf"},
  33 + {$td->FILE => "b.pdf"});
24 34  
25 35 cleanup();
26 36 $td->report($n_tests);
... ...
qpdf/qtest/qpdf/small-stdout-progress.out 0 → 100644
  1 +qpdf: standard output: write progress: 0%
  2 +....other write progress....
  3 +qpdf: standard output: write progress: 100%
... ...