Commit 6c7bf114dc0402dfbfaef4586f05dfd398e57e16

Authored by Jay Berkenbilt
1 parent 7e7c9395

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
  1 +2013-02-23 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * Bug fix: properly handle overridden compressed objects. When
  4 + caching objects from an object stream, only cache objects that,
  5 + based on the xref table, would actually be resolved into this
  6 + stream. Prior to this fix, if an object stream A contained an
  7 + object B that was overridden by an appended section of the file,
  8 + qpdf would cache the old value of B if any non-overridden member
  9 + of A was accessed before B. This commit fixes that bug.
  10 +
1 2013-01-31 Jay Berkenbilt <ejb@ql.org> 11 2013-01-31 Jay Berkenbilt <ejb@ql.org>
2 12
3 * Do not remove libtool's .la file during the make install step. 13 * Do not remove libtool's .la file during the make install step.
libqpdf/QPDF.cc
@@ -1538,20 +1538,31 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1538,20 +1538,31 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1538 offsets[num] = offset + first; 1538 offsets[num] = offset + first;
1539 } 1539 }
1540 1540
  1541 + // To avoid having to read the object stream multiple times, store
  1542 + // all objects that would be found here in the cache. Remember
  1543 + // that some objects stored here might have been overridden by new
  1544 + // objects appended to the file, so it is necessary to recheck the
  1545 + // xref table and only cache what would actually be resolved here.
1541 for (std::map<int, int>::iterator iter = offsets.begin(); 1546 for (std::map<int, int>::iterator iter = offsets.begin();
1542 iter != offsets.end(); ++iter) 1547 iter != offsets.end(); ++iter)
1543 { 1548 {
1544 int obj = (*iter).first; 1549 int obj = (*iter).first;
1545 - int offset = (*iter).second;  
1546 - input->seek(offset, SEEK_SET);  
1547 - QPDFObjectHandle oh = readObject(input, "", obj, 0, true);  
1548 -  
1549 - // Store in cache  
1550 ObjGen og(obj, 0); 1550 ObjGen og(obj, 0);
1551 -  
1552 - this->obj_cache[og] =  
1553 - ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh),  
1554 - end_before_space, end_after_space); 1551 + QPDFXRefEntry const& entry = this->xref_table[og];
  1552 + if ((entry.getType() == 2) &&
  1553 + (entry.getObjStreamNumber() == obj_stream_number))
  1554 + {
  1555 + int offset = (*iter).second;
  1556 + input->seek(offset, SEEK_SET);
  1557 + QPDFObjectHandle oh = readObject(input, "", obj, 0, true);
  1558 + this->obj_cache[og] =
  1559 + ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh),
  1560 + end_before_space, end_after_space);
  1561 + }
  1562 + else
  1563 + {
  1564 + QTC::TC("qpdf", "QPDF not caching overridden objstm object");
  1565 + }
1555 } 1566 }
1556 } 1567 }
1557 1568
qpdf/qpdf.testcov
@@ -261,3 +261,4 @@ qpdf-c called qpdf_set_r5_encryption_parameters 0 @@ -261,3 +261,4 @@ qpdf-c called qpdf_set_r5_encryption_parameters 0
261 qpdf-c called qpdf_set_r6_encryption_parameters 0 261 qpdf-c called qpdf_set_r6_encryption_parameters 0
262 QPDFObjectHandle EOF in inline image 0 262 QPDFObjectHandle EOF in inline image 0
263 QPDFObjectHandle inline image token 0 263 QPDFObjectHandle inline image token 0
  264 +QPDF not caching overridden objstm object 0
qpdf/qtest/qpdf.test
@@ -199,7 +199,7 @@ $td-&gt;runtest(&quot;remove page we don&#39;t have&quot;, @@ -199,7 +199,7 @@ $td-&gt;runtest(&quot;remove page we don&#39;t have&quot;,
199 show_ntests(); 199 show_ntests();
200 # ---------- 200 # ----------
201 $td->notify("--- Miscellaneous Tests ---"); 201 $td->notify("--- Miscellaneous Tests ---");
202 -$n_tests += 60; 202 +$n_tests += 61;
203 203
204 $td->runtest("qpdf version", 204 $td->runtest("qpdf version",
205 {$td->COMMAND => "qpdf --version"}, 205 {$td->COMMAND => "qpdf --version"},
@@ -484,6 +484,18 @@ $td-&gt;runtest(&quot;content stream errors&quot;, @@ -484,6 +484,18 @@ $td-&gt;runtest(&quot;content stream errors&quot;,
484 $td->EXIT_STATUS => 2}, 484 $td->EXIT_STATUS => 2},
485 $td->NORMALIZE_NEWLINES); 485 $td->NORMALIZE_NEWLINES);
486 486
  487 +# The file override-compressed-object.pdf contains an object stream
  488 +# with four strings in it. The file is then appended. The appended
  489 +# section overrides one of the four strings with a string in another
  490 +# object stream and another one in an uncompressed object. The other
  491 +# two strings are left alone. The test case exercises that all four
  492 +# objects have the correct value.
  493 +$td->runtest("overridden compressed objects",
  494 + {$td->COMMAND => "test_driver 38 override-compressed-object.pdf"},
  495 + {$td->FILE => "override-compressed-object.out",
  496 + $td->EXIT_STATUS => 0},
  497 + $td->NORMALIZE_NEWLINES);
  498 +
487 show_ntests(); 499 show_ntests();
488 # ---------- 500 # ----------
489 $td->notify("--- Numeric range parsing tests ---"); 501 $td->notify("--- Numeric range parsing tests ---");
qpdf/qtest/qpdf/override-compressed-object.out 0 → 100644
  1 +(orig-1)
  2 +(override-2)
  3 +(override-3)
  4 +(orig-4)
  5 +test 38 done
qpdf/qtest/qpdf/override-compressed-object.pdf 0 → 100644
No preview for this file type
qpdf/test_driver.cc
@@ -1302,6 +1302,15 @@ void runtest(int n, char const* filename1, char const* arg2) @@ -1302,6 +1302,15 @@ void runtest(int n, char const* filename1, char const* arg2)
1302 QPDFObjectHandle::parseContentStream(contents, &cb); 1302 QPDFObjectHandle::parseContentStream(contents, &cb);
1303 } 1303 }
1304 } 1304 }
  1305 + else if (n == 38)
  1306 + {
  1307 + // Designed for override-compressed-object.pdf
  1308 + QPDFObjectHandle qtest = pdf.getRoot().getKey("/QTest");
  1309 + for (int i = 0; i < qtest.getArrayNItems(); ++i)
  1310 + {
  1311 + std::cout << qtest.getArrayItem(i).unparseResolved() << std::endl;
  1312 + }
  1313 + }
1305 else 1314 else
1306 { 1315 {
1307 throw std::runtime_error(std::string("invalid test ") + 1316 throw std::runtime_error(std::string("invalid test ") +