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 | 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->runtest("remove page we don't have", | @@ -199,7 +199,7 @@ $td->runtest("remove page we don't have", | ||
| 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->runtest("content stream errors", | @@ -484,6 +484,18 @@ $td->runtest("content stream errors", | ||
| 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
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 ") + |