From f37d399d825fc70155a3634c26463a24a2e17035 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 29 Jul 2017 12:18:03 -0400 Subject: [PATCH] Add newline-before-endstream option (fixes #103) --- ChangeLog | 6 ++++++ include/qpdf/QPDFWriter.hh | 6 ++++++ libqpdf/QPDFWriter.cc | 9 ++++++++- qpdf/qpdf.cc | 10 ++++++++++ qpdf/qtest/qpdf.test | 12 +++++++++++- qpdf/qtest/qpdf/newline-before-endstream.pdf | Bin 0 -> 800 bytes 6 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 qpdf/qtest/qpdf/newline-before-endstream.pdf diff --git a/ChangeLog b/ChangeLog index f2e4551e..264b0826 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2017-07-29 Jay Berkenbilt + * Add --newline-before-endstream command-line option and + setNewlineBeforeEndstream method to QPDFWriter. This forces qpdf + to always add a newline before the endstream keyword. It is a + necessary but not sufficient condition for PDF/A compliance. Fixes + #103. + * Handle zlib data errors when decoding streams. Fixes #106. * Improve handling of files where the "stream" keyword is not diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index fd35fecd..2519ed12 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -161,6 +161,11 @@ class QPDFWriter QPDF_DLL void setPreserveUnreferencedObjects(bool); + // Always write a newline before the endstream keyword. This helps + // with PDF/A compliance, though it is not sufficient for it. + QPDF_DLL + void setNewlineBeforeEndstream(bool); + // Set the minimum PDF version. If the PDF version of the input // file (or previously set minimum version) is less than the // version passed to this method, the PDF version of the output @@ -434,6 +439,7 @@ class QPDFWriter bool qdf_mode; bool precheck_streams; bool preserve_unreferenced_objects; + bool newline_before_endstream; bool static_id; bool suppress_original_object_ids; bool direct_stream_lengths; diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index a652b1cf..8ee322af 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -59,6 +59,7 @@ QPDFWriter::init() qdf_mode = false; precheck_streams = false; preserve_unreferenced_objects = false; + newline_before_endstream = false; static_id = false; suppress_original_object_ids = false; direct_stream_lengths = true; @@ -190,6 +191,12 @@ QPDFWriter::setPreserveUnreferencedObjects(bool val) this->preserve_unreferenced_objects = val; } +void +QPDFWriter::setNewlineBeforeEndstream(bool val) +{ + this->newline_before_endstream = val; +} + void QPDFWriter::setMinimumPDFVersion(std::string const& version) { @@ -1580,7 +1587,7 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, char last_char = this->pipeline->getLastChar(); popPipelineStack(); - if (this->qdf_mode) + if (this->qdf_mode || this->newline_before_endstream) { if (last_char != '\n') { diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 65a6de1e..1882f7c9 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -204,6 +204,7 @@ familiar with the PDF file format or who are PDF developers.\n\ --ignore-xref-streams tells qpdf to ignore any cross-reference streams\n\ --precheck-streams precheck ability to decode streams\n\ --preserve-unreferenced preserve unreferenced objects\n\ +--newline-before-endstream always put a newline before endstream\n\ --qdf turns on \"QDF mode\" (below)\n\ --min-version=version sets the minimum PDF version of the output file\n\ --force-version=version forces this to be the PDF version of the output file\n\ @@ -1032,6 +1033,7 @@ int main(int argc, char* argv[]) bool qdf_mode = false; bool precheck_streams = false; bool preserve_unreferenced_objects = false; + bool newline_before_endstream = false; std::string min_version; std::string force_version; @@ -1225,6 +1227,10 @@ int main(int argc, char* argv[]) { preserve_unreferenced_objects = true; } + else if (strcmp(arg, "newline-before-endstream") == 0) + { + newline_before_endstream = true; + } else if (strcmp(arg, "min-version") == 0) { if (parameter == 0) @@ -1724,6 +1730,10 @@ int main(int argc, char* argv[]) { w.setPreserveUnreferencedObjects(true); } + if (newline_before_endstream) + { + w.setNewlineBeforeEndstream(true); + } if (normalize_set) { w.setContentNormalization(normalize); diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 3530035c..81c69025 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -206,7 +206,7 @@ $td->runtest("remove page we don't have", show_ntests(); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 89; +$n_tests += 91; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -618,6 +618,16 @@ $td->runtest("split content stream errors", $td->EXIT_STATUS => 3}, $td->NORMALIZE_NEWLINES); +$td->runtest("newline before endstream", + {$td->COMMAND => + "qpdf --static-id --newline-before-endstream" . + " minimal.pdf a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "newline-before-endstream.pdf"}); + show_ntests(); # ---------- diff --git a/qpdf/qtest/qpdf/newline-before-endstream.pdf b/qpdf/qtest/qpdf/newline-before-endstream.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cde4693b40f5640aa244e137b6f292b6eb781ab8 GIT binary patch literal 800 zcmah{&2G~`5C$rwuoCYuNaP0C-u15(Sg`;7JwIDLzIXrj>U(SD^d4OP7_}~cmUpji-S~3z&dS<$O}#eI>a5G|axJPJoC8t3 z6DFT>aP}qE%;g-M*SUNmb>09k_%RM8L&t}ULsL!2wub-f4y#;^;no0KYK)$AGYk?- z1Cx4^%TgQ>7b4obS=i{6=ai-*X zf%e;>4wSOnRjuPNKNS#x)9b>x!#v*$5*nh`IEfNQS?q^2^cjm8jglb!f5gNtf;PM~ Ol??%