Commit ff42ea4e6cb8b03badf31993fcb4d9f57bb2ad1e

Authored by Jay Berkenbilt
Committed by Jay Berkenbilt
1 parent 1d9209ee

Fix logic for fixDanglingReferences

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