From 6c7bf114dc0402dfbfaef4586f05dfd398e57e16 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 23 Feb 2013 17:41:27 -0500 Subject: [PATCH] Bug fix: properly handle overridden compressed objects When caching objects in an object stream, only cache objects that still resolve to that stream. See Changelog mod from this commit for details. --- ChangeLog | 10 ++++++ libqpdf/QPDF.cc | 29 ++++++++++++------ qpdf/qpdf.testcov | 1 + qpdf/qtest/qpdf.test | 14 ++++++++- .../qtest/qpdf/override-compressed-object.out | 5 +++ .../qtest/qpdf/override-compressed-object.pdf | Bin 0 -> 1817 bytes qpdf/test_driver.cc | 9 ++++++ 7 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 qpdf/qtest/qpdf/override-compressed-object.out create mode 100644 qpdf/qtest/qpdf/override-compressed-object.pdf diff --git a/ChangeLog b/ChangeLog index 1265467c..7f4d64f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2013-02-23 Jay Berkenbilt + + * Bug fix: properly handle overridden compressed objects. When + caching objects from an object stream, only cache objects that, + based on the xref table, would actually be resolved into this + stream. Prior to this fix, if an object stream A contained an + object B that was overridden by an appended section of the file, + qpdf would cache the old value of B if any non-overridden member + of A was accessed before B. This commit fixes that bug. + 2013-01-31 Jay Berkenbilt * Do not remove libtool's .la file during the make install step. diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 5ee043b9..5860fb11 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1538,20 +1538,31 @@ QPDF::resolveObjectsInStream(int obj_stream_number) offsets[num] = offset + first; } + // To avoid having to read the object stream multiple times, store + // all objects that would be found here in the cache. Remember + // that some objects stored here might have been overridden by new + // objects appended to the file, so it is necessary to recheck the + // xref table and only cache what would actually be resolved here. for (std::map::iterator iter = offsets.begin(); iter != offsets.end(); ++iter) { int obj = (*iter).first; - int offset = (*iter).second; - input->seek(offset, SEEK_SET); - QPDFObjectHandle oh = readObject(input, "", obj, 0, true); - - // Store in cache ObjGen og(obj, 0); - - this->obj_cache[og] = - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), - end_before_space, end_after_space); + QPDFXRefEntry const& entry = this->xref_table[og]; + if ((entry.getType() == 2) && + (entry.getObjStreamNumber() == obj_stream_number)) + { + int offset = (*iter).second; + input->seek(offset, SEEK_SET); + QPDFObjectHandle oh = readObject(input, "", obj, 0, true); + this->obj_cache[og] = + ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), + end_before_space, end_after_space); + } + else + { + QTC::TC("qpdf", "QPDF not caching overridden objstm object"); + } } } diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index b09e966c..31e15495 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -261,3 +261,4 @@ qpdf-c called qpdf_set_r5_encryption_parameters 0 qpdf-c called qpdf_set_r6_encryption_parameters 0 QPDFObjectHandle EOF in inline image 0 QPDFObjectHandle inline image token 0 +QPDF not caching overridden objstm object 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index f447bd83..8375e5f2 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 += 60; +$n_tests += 61; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -484,6 +484,18 @@ $td->runtest("content stream errors", $td->EXIT_STATUS => 2}, $td->NORMALIZE_NEWLINES); +# The file override-compressed-object.pdf contains an object stream +# with four strings in it. The file is then appended. The appended +# section overrides one of the four strings with a string in another +# object stream and another one in an uncompressed object. The other +# two strings are left alone. The test case exercises that all four +# objects have the correct value. +$td->runtest("overridden compressed objects", + {$td->COMMAND => "test_driver 38 override-compressed-object.pdf"}, + {$td->FILE => "override-compressed-object.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + show_ntests(); # ---------- $td->notify("--- Numeric range parsing tests ---"); diff --git a/qpdf/qtest/qpdf/override-compressed-object.out b/qpdf/qtest/qpdf/override-compressed-object.out new file mode 100644 index 00000000..a3fbd51d --- /dev/null +++ b/qpdf/qtest/qpdf/override-compressed-object.out @@ -0,0 +1,5 @@ +(orig-1) +(override-2) +(override-3) +(orig-4) +test 38 done diff --git a/qpdf/qtest/qpdf/override-compressed-object.pdf b/qpdf/qtest/qpdf/override-compressed-object.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0647b7fe71f6782724350398fb2940573e702562 GIT binary patch literal 1817 zcma)7OHbQC5Kf@9)gF51HkU{smBzc?UE5U@sl3#xEu`^R6_+o&(bI5?0;r))4_0R73g z3j_WA==8vz)Bc0WruHlNo}k?x1oYTVi_(H0u;GxEwlLv26W}lxxa%+pLI|cnO2srN z-(?-5Za2DkYPPA09el$rMZsymGN2dz9zH3gbU8h> z)Yz2yWf5cYDhEU!x@b4F<)CKS!isYqdxUpkc&ZK0ZGzeiOH+C6 zzcpu9#-?$IZu&b}oF{2Eh5lihy~)b7J)oC+;W^gfZk6wY%g9z`N%U1GHIHO-Arjpp zk&J4kk1artiMh7y_+jq7_A|xS@*o7TGJCl2SSLVoxl=Qa3!>LPe9M@ER85=U*juWM6a|lO(j!~-89p^cAep7M% zgY-MLg(SUq+i*09BVPxSD@UnVsDLK{7u*#}N7_q7$cFp|oLPuL}&2 za}gjPJNvtg(`1;>!v6eNRW8N1SC5r?s!Hb;N}Onqs63A4ab=1kP0R*X-`x_SaY$Hh z)UfVmd{O()Bc0=mKbEgbZ&a8o9AKCWBCnd`M>w33X#8)exqc+2%8yQj@FLE`APOBV oL*LcDr(_(+$P2>%C|&Bj=yq5BAbB~Gw|Wm%x0q6zGD}ST15*Oe(EtDd literal 0 HcmV?d00001 diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 799ea4ba..d6534abf 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -1302,6 +1302,15 @@ void runtest(int n, char const* filename1, char const* arg2) QPDFObjectHandle::parseContentStream(contents, &cb); } } + else if (n == 38) + { + // Designed for override-compressed-object.pdf + QPDFObjectHandle qtest = pdf.getRoot().getKey("/QTest"); + for (int i = 0; i < qtest.getArrayNItems(); ++i) + { + std::cout << qtest.getArrayItem(i).unparseResolved() << std::endl; + } + } else { throw std::runtime_error(std::string("invalid test ") +