From 119f2a4b684aae7cec8841412a5fc89bcbae404d Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sun, 3 Mar 2013 15:48:31 -0500 Subject: [PATCH] Add method to terminate content stream parsing --- ChangeLog | 6 ++ include/qpdf/QPDFObjectHandle.hh | 7 +++ libqpdf/QPDFObjectHandle.cc | 19 +++++- qpdf/qtest/qpdf.test | 7 ++- qpdf/qtest/qpdf/terminate-parsing.out | 86 ++++++++++++++++++++++++++ qpdf/qtest/qpdf/terminate-parsing.pdf | Bin 0 -> 1539 bytes qpdf/test_driver.cc | 5 ++ 7 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 qpdf/qtest/qpdf/terminate-parsing.out create mode 100644 qpdf/qtest/qpdf/terminate-parsing.pdf 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 0000000000000000000000000000000000000000..ec848f82e550651d07088f77f911c09a1b9bb15b GIT binary patch literal 1539 zcmbW1&ubGw6vq<;kp&UaLr?lHy%gM;+1X!1L&PMF7JtRI;-LtgY$i35?8e=xRK18^ z6x6@KiynIsJuBY4TK@yVvj|@FP}Db@Zc|eQahL3F-oE$d^L_L7O}84&6U^q;^xJRG ze^?ACB<<@~r2?cSS5*cMS{8sT-5M$&b*W`PS%qrVQgO#Pn%6}@Jz0x229UG8P6o@s z^H4XxUM*6LvW^oNr*VQhxqSS;t%yPoFsu2nR4%OQz^pz%8gOSxY0N zHQdbv-{cPr{5(;$iVHCmJ%P;#tUaG90Q73}u@2 z;#J#h*ml(R)|uKA$!juGdELkv)xW89FOop!;wb50ERZX`_*9(r#)b=WfbGy^Ko-~9 zI`0i50RNA4IA{DwHp&bq!aG9x}x(K(~iGNImJV z4dM(Kvd=eQ{b{sys8rrpE?aZ+)}_5QG#xR4&e?8s!^u5chyXdA(B>fmQ*b%6h3`V( z_`;^nBq^r`l?ML?%V|OT`NOlrC3>Lr^3~5xweRS?`>!8weSa}S<4+4;zJ83RqDSoA zY*=pJXq#4PaQ7zsoB#*fgw17pV?By3J>lp7!OzFfb453PRDt> z*t@MzCk}}QELW7|%$K4YhO)~#+^0V`pE2&XMO-H0(i dbzVfqL}*P<&s}U