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.
This commit is contained in:
Jay Berkenbilt 2013-02-23 17:41:27 -05:00
parent 7e7c93951f
commit 6c7bf114dc
7 changed files with 58 additions and 10 deletions

View File

@ -1,3 +1,13 @@
2013-02-23 Jay Berkenbilt <ejb@ql.org>
* 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 <ejb@ql.org>
* Do not remove libtool's .la file during the make install step.

View File

@ -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<int, int>::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");
}
}
}

View File

@ -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

View File

@ -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 ---");

View File

@ -0,0 +1,5 @@
(orig-1)
(override-2)
(override-3)
(orig-4)
test 38 done

Binary file not shown.

View File

@ -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 ") +