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 | 2022-06-05 Jay Berkenbilt <ejb@ql.org> | 7 | 2022-06-05 Jay Berkenbilt <ejb@ql.org> |
| 2 | 8 | ||
| 3 | * QPDFJob: API breaking change: QPDFJob::doIfVerbose passes a | 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,9 +79,6 @@ Find all places in the library that write to stdout/stderr/cout/cerr. | ||
| 79 | Also find places that raise exceptions if unable to warn. These should | 79 | Also find places that raise exceptions if unable to warn. These should |
| 80 | use the global output writer. | 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 | QPDFPagesTree | 83 | QPDFPagesTree |
| 87 | ============= | 84 | ============= |
libqpdf/QPDFJob.cc
| @@ -691,22 +691,21 @@ QPDFJob::checkConfiguration() | @@ -691,22 +691,21 @@ QPDFJob::checkConfiguration() | ||
| 691 | " before the -- that follows --encrypt."); | 691 | " before the -- that follows --encrypt."); |
| 692 | } | 692 | } |
| 693 | 693 | ||
| 694 | + bool save_to_stdout = false; | ||
| 694 | if (m->require_outfile && m->outfilename && | 695 | if (m->require_outfile && m->outfilename && |
| 695 | (strcmp(m->outfilename.get(), "-") == 0)) { | 696 | (strcmp(m->outfilename.get(), "-") == 0)) { |
| 696 | if (m->split_pages) { | 697 | if (m->split_pages) { |
| 697 | usage("--split-pages may not be used when" | 698 | usage("--split-pages may not be used when" |
| 698 | " writing to standard output"); | 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 | if ((!m->split_pages) && | 709 | if ((!m->split_pages) && |
| 711 | QUtil::same_file(m->infilename.get(), m->outfilename.get())) { | 710 | QUtil::same_file(m->infilename.get(), m->outfilename.get())) { |
| 712 | QTC::TC("qpdf", "QPDFJob same file error"); | 711 | QTC::TC("qpdf", "QPDFJob same file error"); |
| @@ -918,10 +917,11 @@ QPDFJob::doShowObj(QPDF& pdf) | @@ -918,10 +917,11 @@ QPDFJob::doShowObj(QPDF& pdf) | ||
| 918 | obj.warnIfPossible("unable to filter stream data"); | 917 | obj.warnIfPossible("unable to filter stream data"); |
| 919 | error = true; | 918 | error = true; |
| 920 | } else { | 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 | obj.pipeStreamData( | 923 | obj.pipeStreamData( |
| 924 | - &out, | 924 | + this->m->log->getSave().get(), |
| 925 | (filter && m->normalize) ? qpdf_ef_normalize : 0, | 925 | (filter && m->normalize) ? qpdf_ef_normalize : 0, |
| 926 | filter ? qpdf_dl_all : qpdf_dl_none); | 926 | filter ? qpdf_dl_all : qpdf_dl_none); |
| 927 | } | 927 | } |
| @@ -1023,9 +1023,10 @@ QPDFJob::doShowAttachment(QPDF& pdf) | @@ -1023,9 +1023,10 @@ QPDFJob::doShowAttachment(QPDF& pdf) | ||
| 1023 | "attachment " + m->attachment_to_show + " not found"); | 1023 | "attachment " + m->attachment_to_show + " not found"); |
| 1024 | } | 1024 | } |
| 1025 | auto efs = fs->getEmbeddedFileStream(); | 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 | void | 1032 | void |
| @@ -3138,14 +3139,17 @@ QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w) | @@ -3138,14 +3139,17 @@ QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w) | ||
| 3138 | parse_version(m->force_version, version, extension_level); | 3139 | parse_version(m->force_version, version, extension_level); |
| 3139 | w.forcePDFVersion(version, extension_level); | 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 | w.registerProgressReporter( | 3146 | w.registerProgressReporter( |
| 3143 | std::shared_ptr<QPDFWriter::ProgressReporter>( | 3147 | std::shared_ptr<QPDFWriter::ProgressReporter>( |
| 3144 | // line-break | 3148 | // line-break |
| 3145 | new ProgressReporter( | 3149 | new ProgressReporter( |
| 3146 | *this->m->log->getInfo(), | 3150 | *this->m->log->getInfo(), |
| 3147 | this->m->message_prefix, | 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,7 +3277,15 @@ QPDFJob::writeOutfile(QPDF& pdf) | ||
| 3273 | } else { | 3277 | } else { |
| 3274 | // QPDFWriter must have block scope so the output file will be | 3278 | // QPDFWriter must have block scope so the output file will be |
| 3275 | // closed after write() finishes. | 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 | setWriterOptions(pdf, w); | 3289 | setWriterOptions(pdf, w); |
| 3278 | w.write(); | 3290 | w.write(); |
| 3279 | } | 3291 | } |
libqpdf/QPDFLogger.cc
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | 2 | ||
| 3 | #include <qpdf/Pl_Discard.hh> | 3 | #include <qpdf/Pl_Discard.hh> |
| 4 | #include <qpdf/Pl_OStream.hh> | 4 | #include <qpdf/Pl_OStream.hh> |
| 5 | +#include <qpdf/QUtil.hh> | ||
| 5 | #include <iostream> | 6 | #include <iostream> |
| 6 | #include <stdexcept> | 7 | #include <stdexcept> |
| 7 | 8 | ||
| @@ -182,6 +183,9 @@ QPDFLogger::setError(std::shared_ptr<Pipeline> p) | @@ -182,6 +183,9 @@ QPDFLogger::setError(std::shared_ptr<Pipeline> p) | ||
| 182 | void | 183 | void |
| 183 | QPDFLogger::setSave(std::shared_ptr<Pipeline> p) | 184 | QPDFLogger::setSave(std::shared_ptr<Pipeline> p) |
| 184 | { | 185 | { |
| 186 | + if (this->m->p_save == p) { | ||
| 187 | + return; | ||
| 188 | + } | ||
| 185 | if (p == this->m->p_stdout) { | 189 | if (p == this->m->p_stdout) { |
| 186 | auto pt = dynamic_cast<Pl_Track*>(p.get()); | 190 | auto pt = dynamic_cast<Pl_Track*>(p.get()); |
| 187 | if (pt->getUsed()) { | 191 | if (pt->getUsed()) { |
| @@ -192,6 +196,7 @@ QPDFLogger::setSave(std::shared_ptr<Pipeline> p) | @@ -192,6 +196,7 @@ QPDFLogger::setSave(std::shared_ptr<Pipeline> p) | ||
| 192 | if (this->m->p_info == this->m->p_stdout) { | 196 | if (this->m->p_info == this->m->p_stdout) { |
| 193 | this->m->p_info = this->m->p_stderr; | 197 | this->m->p_info = this->m->p_stderr; |
| 194 | } | 198 | } |
| 199 | + QUtil::binary_stdout(); | ||
| 195 | } | 200 | } |
| 196 | this->m->p_save = p; | 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,6 +133,10 @@ For a detailed list of changes, please see the file | ||
| 133 | user password is not recoverable from the owner password when | 133 | user password is not recoverable from the owner password when |
| 134 | 256-bit keys are in use. | 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 | - Library Enhancements | 140 | - Library Enhancements |
| 137 | 141 | ||
| 138 | - New methods ``insertItemAndGet``, ``appendItemAndGet``, | 142 | - New methods ``insertItemAndGet``, ``appendItemAndGet``, |
qpdf/qtest/progress-reporting.test
| @@ -14,13 +14,23 @@ cleanup(); | @@ -14,13 +14,23 @@ cleanup(); | ||
| 14 | 14 | ||
| 15 | my $td = new TestDriver('progress-reporting'); | 15 | my $td = new TestDriver('progress-reporting'); |
| 16 | 16 | ||
| 17 | -my $n_tests = 1; | 17 | +my $n_tests = 3; |
| 18 | 18 | ||
| 19 | $td->runtest("progress report on small file", | 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 | $td->FILTER => "perl filter-progress.pl"}, | 22 | $td->FILTER => "perl filter-progress.pl"}, |
| 22 | {$td->FILE => "small-progress.out", $td->EXIT_STATUS => 0}, | 23 | {$td->FILE => "small-progress.out", $td->EXIT_STATUS => 0}, |
| 23 | $td->NORMALIZE_NEWLINES); | 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 | cleanup(); | 35 | cleanup(); |
| 26 | $td->report($n_tests); | 36 | $td->report($n_tests); |
qpdf/qtest/qpdf/small-stdout-progress.out
0 → 100644