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 11 2013-01-31 Jay Berkenbilt <ejb@ql.org>
2 12  
3 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 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 1546 for (std::map<int, int>::iterator iter = offsets.begin();
1542 1547 iter != offsets.end(); ++iter)
1543 1548 {
1544 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 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 261 qpdf-c called qpdf_set_r6_encryption_parameters 0
262 262 QPDFObjectHandle EOF in inline image 0
263 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 199 show_ntests();
200 200 # ----------
201 201 $td->notify("--- Miscellaneous Tests ---");
202   -$n_tests += 60;
  202 +$n_tests += 61;
203 203  
204 204 $td->runtest("qpdf version",
205 205 {$td->COMMAND => "qpdf --version"},
... ... @@ -484,6 +484,18 @@ $td-&gt;runtest(&quot;content stream errors&quot;,
484 484 $td->EXIT_STATUS => 2},
485 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 499 show_ntests();
488 500 # ----------
489 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 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 1314 else
1306 1315 {
1307 1316 throw std::runtime_error(std::string("invalid test ") +
... ...