Commit ff42ea4e6cb8b03badf31993fcb4d9f57bb2ad1e
Committed by
Jay Berkenbilt
1 parent
1d9209ee
Fix logic for fixDanglingReferences
Showing
3 changed files
with
33 additions
and
40 deletions
libqpdf/QPDF.cc
| @@ -1292,48 +1292,48 @@ QPDF::showXRefTable() | @@ -1292,48 +1292,48 @@ QPDF::showXRefTable() | ||
| 1292 | } | 1292 | } |
| 1293 | } | 1293 | } |
| 1294 | 1294 | ||
| 1295 | -// Ensure all objects in the pdf file, including those in indirect references, | ||
| 1296 | -// appear in the object cache. | ||
| 1297 | void | 1295 | void |
| 1298 | QPDF::fixDanglingReferences(bool force) | 1296 | QPDF::fixDanglingReferences(bool force) |
| 1299 | { | 1297 | { |
| 1298 | + // Ensure all objects in the pdf file, including those in indirect | ||
| 1299 | + // references, appear in the object cache. | ||
| 1300 | if (this->m->fixed_dangling_refs && !force) { | 1300 | if (this->m->fixed_dangling_refs && !force) { |
| 1301 | return; | 1301 | return; |
| 1302 | } | 1302 | } |
| 1303 | 1303 | ||
| 1304 | - if (!this->m->fixed_dangling_refs) { | ||
| 1305 | - // First pass is only run if the the xref table has not been | ||
| 1306 | - // reconstructed. It will be terminated as soon as reconstruction is | ||
| 1307 | - // triggered. | ||
| 1308 | - if (!this->m->reconstructed_xref) { | ||
| 1309 | - for (auto const& iter: this->m->xref_table) { | ||
| 1310 | - auto og = iter.first; | ||
| 1311 | - if (!isCached(og)) { | ||
| 1312 | - m->obj_cache[og] = | ||
| 1313 | - ObjCache(QPDF_Unresolved::create(this, og), -1, -1); | ||
| 1314 | - if (this->m->reconstructed_xref) { | ||
| 1315 | - break; | ||
| 1316 | - } | ||
| 1317 | - } | ||
| 1318 | - } | ||
| 1319 | - } | ||
| 1320 | - // Second pass is skipped if the first pass did not trigger | ||
| 1321 | - // reconstruction of the xref table. | ||
| 1322 | - if (this->m->reconstructed_xref) { | ||
| 1323 | - for (auto const& iter: this->m->xref_table) { | ||
| 1324 | - auto og = iter.first; | ||
| 1325 | - if (!isCached(og)) { | ||
| 1326 | - m->obj_cache[og] = | ||
| 1327 | - ObjCache(QPDF_Unresolved::create(this, og), -1, -1); | ||
| 1328 | - } | ||
| 1329 | - } | 1304 | + // Make sure everything in the xref table appears in the object |
| 1305 | + // cache. | ||
| 1306 | + for (auto const& iter: this->m->xref_table) { | ||
| 1307 | + auto og = iter.first; | ||
| 1308 | + if (!isCached(og)) { | ||
| 1309 | + m->obj_cache[og] = | ||
| 1310 | + ObjCache(QPDF_Unresolved::create(this, og), -1, -1); | ||
| 1330 | } | 1311 | } |
| 1331 | } | 1312 | } |
| 1332 | - // Final pass adds all indirect references to the object cache. | 1313 | + |
| 1314 | + // Resolve all known objects. The parser inserts any indirect | ||
| 1315 | + // reference into the object cache, including dangling references. | ||
| 1316 | + bool orig_reconstructed_xref = this->m->reconstructed_xref; | ||
| 1317 | + bool triggered_xref_reconstruction = false; | ||
| 1333 | for (auto const& iter: this->m->obj_cache) { | 1318 | for (auto const& iter: this->m->obj_cache) { |
| 1334 | resolve(iter.first); | 1319 | resolve(iter.first); |
| 1320 | + if (!orig_reconstructed_xref && this->m->reconstructed_xref) { | ||
| 1321 | + triggered_xref_reconstruction = true; | ||
| 1322 | + // We triggered xref reconstruction. We'll have to start | ||
| 1323 | + // over. | ||
| 1324 | + break; | ||
| 1325 | + } | ||
| 1326 | + } | ||
| 1327 | + if (triggered_xref_reconstruction) { | ||
| 1328 | + // Resolving objects triggered xref reconstruction. This may | ||
| 1329 | + // cause new objects to appear in the xref. Start over again. | ||
| 1330 | + // This recursive call can never go more than two deep since | ||
| 1331 | + // we never clear this->m->reconstructed_xref. | ||
| 1332 | + QTC::TC("qpdf", "QPDF fix dangling triggered xref reconstruction"); | ||
| 1333 | + fixDanglingReferences(force); | ||
| 1334 | + } else { | ||
| 1335 | + this->m->fixed_dangling_refs = true; | ||
| 1335 | } | 1336 | } |
| 1336 | - this->m->fixed_dangling_refs = true; | ||
| 1337 | } | 1337 | } |
| 1338 | 1338 | ||
| 1339 | size_t | 1339 | size_t |
qpdf/qpdf.testcov
| @@ -678,3 +678,4 @@ QPDF_json bad pushedinheritedpageresources 0 | @@ -678,3 +678,4 @@ QPDF_json bad pushedinheritedpageresources 0 | ||
| 678 | QPDFPageObjectHelper copied fallback 0 | 678 | QPDFPageObjectHelper copied fallback 0 |
| 679 | QPDFPageObjectHelper used fallback without copying 0 | 679 | QPDFPageObjectHelper used fallback without copying 0 |
| 680 | QPDF skipping cache for known unchecked object 0 | 680 | QPDF skipping cache for known unchecked object 0 |
| 681 | +QPDF fix dangling triggered xref reconstruction 0 |
qpdf/qtest/dangling-refs.test
| @@ -19,21 +19,13 @@ my $n_tests = 2 * scalar(@dangling); | @@ -19,21 +19,13 @@ my $n_tests = 2 * scalar(@dangling); | ||
| 19 | 19 | ||
| 20 | foreach my $f (@dangling) | 20 | foreach my $f (@dangling) |
| 21 | { | 21 | { |
| 22 | - # TEMPORARY | ||
| 23 | - my $xflags = 0; | ||
| 24 | - if ($f eq 'dangling-bad-xref') | ||
| 25 | - { | ||
| 26 | - $xflags = $td->EXPECT_FAILURE; | ||
| 27 | - } | ||
| 28 | - # END TEMPORARY | ||
| 29 | $td->runtest("dangling refs: $f", | 22 | $td->runtest("dangling refs: $f", |
| 30 | {$td->COMMAND => "test_driver 53 $f.pdf"}, | 23 | {$td->COMMAND => "test_driver 53 $f.pdf"}, |
| 31 | {$td->FILE => "$f-dangling.out", $td->EXIT_STATUS => 0}, | 24 | {$td->FILE => "$f-dangling.out", $td->EXIT_STATUS => 0}, |
| 32 | - $td->NORMALIZE_NEWLINES | $xflags); | 25 | + $td->NORMALIZE_NEWLINES); |
| 33 | $td->runtest("check output", | 26 | $td->runtest("check output", |
| 34 | {$td->FILE => "a.pdf"}, | 27 | {$td->FILE => "a.pdf"}, |
| 35 | - {$td->FILE => "$f-dangling-out.pdf"}, | ||
| 36 | - $xflags); | 28 | + {$td->FILE => "$f-dangling-out.pdf"}); |
| 37 | } | 29 | } |
| 38 | cleanup(); | 30 | cleanup(); |
| 39 | $td->report($n_tests); | 31 | $td->report($n_tests); |