diff options
author | Jay Berkenbilt <ejb@ql.org> | 2022-11-26 22:43:41 +0100 |
---|---|---|
committer | Jay Berkenbilt <jberkenbilt@users.noreply.github.com> | 2022-11-27 00:13:46 +0100 |
commit | ff42ea4e6cb8b03badf31993fcb4d9f57bb2ad1e (patch) | |
tree | e975aad05cbc029419ab75a090821e6a1a1d045d | |
parent | 1d9209ee743a34597de6965edf40333a3eca47b8 (diff) | |
download | qpdf-ff42ea4e6cb8b03badf31993fcb4d9f57bb2ad1e.tar.zst |
Fix logic for fixDanglingReferences
-rw-r--r-- | libqpdf/QPDF.cc | 60 | ||||
-rw-r--r-- | qpdf/qpdf.testcov | 1 | ||||
-rw-r--r-- | qpdf/qtest/dangling-refs.test | 12 |
3 files changed, 33 insertions, 40 deletions
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 7e3326e8..89487ee1 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1292,48 +1292,48 @@ QPDF::showXRefTable() } } -// Ensure all objects in the pdf file, including those in indirect references, -// appear in the object cache. void QPDF::fixDanglingReferences(bool force) { + // Ensure all objects in the pdf file, including those in indirect + // references, appear in the object cache. if (this->m->fixed_dangling_refs && !force) { return; } - if (!this->m->fixed_dangling_refs) { - // First pass is only run if the the xref table has not been - // reconstructed. It will be terminated as soon as reconstruction is - // triggered. - if (!this->m->reconstructed_xref) { - for (auto const& iter: this->m->xref_table) { - auto og = iter.first; - if (!isCached(og)) { - m->obj_cache[og] = - ObjCache(QPDF_Unresolved::create(this, og), -1, -1); - if (this->m->reconstructed_xref) { - break; - } - } - } - } - // Second pass is skipped if the first pass did not trigger - // reconstruction of the xref table. - if (this->m->reconstructed_xref) { - for (auto const& iter: this->m->xref_table) { - auto og = iter.first; - if (!isCached(og)) { - m->obj_cache[og] = - ObjCache(QPDF_Unresolved::create(this, og), -1, -1); - } - } + // Make sure everything in the xref table appears in the object + // cache. + for (auto const& iter: this->m->xref_table) { + auto og = iter.first; + if (!isCached(og)) { + m->obj_cache[og] = + ObjCache(QPDF_Unresolved::create(this, og), -1, -1); } } - // Final pass adds all indirect references to the object cache. + + // Resolve all known objects. The parser inserts any indirect + // reference into the object cache, including dangling references. + bool orig_reconstructed_xref = this->m->reconstructed_xref; + bool triggered_xref_reconstruction = false; for (auto const& iter: this->m->obj_cache) { resolve(iter.first); + if (!orig_reconstructed_xref && this->m->reconstructed_xref) { + triggered_xref_reconstruction = true; + // We triggered xref reconstruction. We'll have to start + // over. + break; + } + } + if (triggered_xref_reconstruction) { + // Resolving objects triggered xref reconstruction. This may + // cause new objects to appear in the xref. Start over again. + // This recursive call can never go more than two deep since + // we never clear this->m->reconstructed_xref. + QTC::TC("qpdf", "QPDF fix dangling triggered xref reconstruction"); + fixDanglingReferences(force); + } else { + this->m->fixed_dangling_refs = true; } - this->m->fixed_dangling_refs = true; } size_t diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index b1c9e697..3cef71d2 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -678,3 +678,4 @@ QPDF_json bad pushedinheritedpageresources 0 QPDFPageObjectHelper copied fallback 0 QPDFPageObjectHelper used fallback without copying 0 QPDF skipping cache for known unchecked object 0 +QPDF fix dangling triggered xref reconstruction 0 diff --git a/qpdf/qtest/dangling-refs.test b/qpdf/qtest/dangling-refs.test index c63d5acd..18c2cf6b 100644 --- a/qpdf/qtest/dangling-refs.test +++ b/qpdf/qtest/dangling-refs.test @@ -19,21 +19,13 @@ my $n_tests = 2 * scalar(@dangling); foreach my $f (@dangling) { - # TEMPORARY - my $xflags = 0; - if ($f eq 'dangling-bad-xref') - { - $xflags = $td->EXPECT_FAILURE; - } - # END TEMPORARY $td->runtest("dangling refs: $f", {$td->COMMAND => "test_driver 53 $f.pdf"}, {$td->FILE => "$f-dangling.out", $td->EXIT_STATUS => 0}, - $td->NORMALIZE_NEWLINES | $xflags); + $td->NORMALIZE_NEWLINES); $td->runtest("check output", {$td->FILE => "a.pdf"}, - {$td->FILE => "$f-dangling-out.pdf"}, - $xflags); + {$td->FILE => "$f-dangling-out.pdf"}); } cleanup(); $td->report($n_tests); |