diff --git a/ChangeLog b/ChangeLog index 90b110e2..11d99646 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2013-03-03 Jay Berkenbilt + + * Add protected terminateParsing method to + QPDFObjectHandle::ParserCallbacks that implementor can call to + terminate parsing of a content stream. + 2013-02-28 Jay Berkenbilt * Favor fopen_s and strerror_s on MSVC to avoid CRT security diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index e2c8b529..c42fa719 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -83,6 +83,13 @@ class QPDFObjectHandle } virtual void handleObject(QPDFObjectHandle) = 0; virtual void handleEOF() = 0; + + protected: + // Implementors may call this method during parsing to + // terminate parsing early. This method throws an exception + // that is caught by parseContentStream, so its effect is + // immediate. + void terminateParsing(); }; diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index e6f53d08..0c6b0a9d 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -23,6 +23,16 @@ #include #include +class TerminateParsing +{ +}; + +void +QPDFObjectHandle::ParserCallbacks::terminateParsing() +{ + throw TerminateParsing(); +} + QPDFObjectHandle::QPDFObjectHandle() : initialized(false), objid(0), @@ -728,7 +738,14 @@ QPDFObjectHandle::parseContentStream(QPDFObjectHandle stream_or_array, throw std::logic_error( "QPDFObjectHandle: parseContentStream called on non-stream"); } - parseContentStream_internal(stream, callbacks); + try + { + parseContentStream_internal(stream, callbacks); + } + catch (TerminateParsing&) + { + return; + } } callbacks->handleEOF(); } diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 8375e5f2..5ae2ae3c 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -199,7 +199,7 @@ $td->runtest("remove page we don't have", show_ntests(); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 61; +$n_tests += 62; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -478,6 +478,11 @@ $td->runtest("tokenize content streams", {$td->FILE => "tokenize-content-streams.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("terminate parsing", + {$td->COMMAND => "test_driver 37 terminate-parsing.pdf"}, + {$td->FILE => "terminate-parsing.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); $td->runtest("content stream errors", {$td->COMMAND => "qpdf --check content-stream-errors.pdf"}, {$td->FILE => "content-stream-errors.out", diff --git a/qpdf/qtest/qpdf/terminate-parsing.out b/qpdf/qtest/qpdf/terminate-parsing.out new file mode 100644 index 00000000..a0050974 --- /dev/null +++ b/qpdf/qtest/qpdf/terminate-parsing.out @@ -0,0 +1,86 @@ +name: /potato +test suite: terminating parsing +real: 0.1 +integer: 0 +integer: 0 +real: 0.1 +integer: 0 +integer: 0 +operator: cm +operator: q +integer: 0 +real: 1.1999 +real: -1.1999 +integer: 0 +real: 121.19 +real: 150.009 +operator: cm +operator: BI +name: /CS +name: /G +name: /W +integer: 1 +name: /H +integer: 1 +name: /BPC +integer: 8 +name: /F +name: /Fl +name: /DP +dictionary: << /Columns 1 /Predictor 15 >> +operator: ID +inline-image: 789c63fc0f0001030101 +operator: EI +operator: Q +operator: q +integer: 0 +real: 35.997 +real: -128.389 +integer: 0 +real: 431.964 +real: 7269.02 +operator: cm +operator: BI +name: /CS +name: /G +name: /W +integer: 30 +name: /H +integer: 107 +name: /BPC +integer: 8 +name: /F +name: /Fl +name: /DP +dictionary: << /Columns 30 /Predictor 15 >> +operator: ID +inline-image: 789cedd1a11100300800b1b2ffd06503148283bc8dfcf8af2a306ee352eff2e06318638c31c63b3801627b620a +operator: EI +operator: Q +operator: q +integer: 0 +real: 38.3968 +real: -93.5922 +integer: 0 +real: 431.964 +real: 7567.79 +operator: cm +operator: BI +name: /CS +name: /G +name: /W +integer: 32 +name: /H +integer: 78 +name: /BPC +integer: 8 +name: /F +name: /Fl +name: /DP +dictionary: << /Columns 32 /Predictor 15 >> +operator: ID +inline-image: 789c63fccf801f308e2a185530aa60882a20203faa605401890a0643aa1e5530aa6054010d140000bdd03c13 +operator: EI +operator: Q +-EOF- +test 37 done diff --git a/qpdf/qtest/qpdf/terminate-parsing.pdf b/qpdf/qtest/qpdf/terminate-parsing.pdf new file mode 100644 index 00000000..ec848f82 Binary files /dev/null and b/qpdf/qtest/qpdf/terminate-parsing.pdf differ diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 75f5b973..3863cdbe 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -72,6 +72,11 @@ class ParserCallbacks: public QPDFObjectHandle::ParserCallbacks void ParserCallbacks::handleObject(QPDFObjectHandle obj) { + if (obj.isName() && (obj.getName() == "/Abort")) + { + std::cout << "test suite: terminating parsing" << std::endl; + terminateParsing(); + } std::cout << obj.getTypeName() << ": "; if (obj.isInlineImage()) {