Commit 6c7bf114dc0402dfbfaef4586f05dfd398e57e16
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.
Showing
7 changed files
with
58 additions
and
10 deletions
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
qpdf/qtest/qpdf.test
| ... | ... | @@ -199,7 +199,7 @@ $td->runtest("remove page we don't have", |
| 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->runtest("content stream errors", |
| 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
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 ") + | ... | ... |