Commit 83be2191b4f3eb8906160d61f61cae48532ee651
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.
Showing
7 changed files
with
60 additions
and
23 deletions
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 | ... | ... |
TODO
| ... | ... | @@ -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& 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& 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& pdf, QPDFWriter& 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& 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<Pipeline> 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<Pipeline> 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