Commit 3dd27a1dd6c1f05b0931ec47fc430ab60e0a87e7
1 parent
206c2fc1
Detect and warn about outline loops during structure traversal in `--check`. Upd…
…ate tests and adjust exit status accordingly.
Showing
5 changed files
with
18 additions
and
6 deletions
libqpdf/QPDFOutlineDocumentHelper.cc
| ... | ... | @@ -53,7 +53,11 @@ QPDFOutlineDocumentHelper::validate(bool repair) |
| 53 | 53 | } |
| 54 | 54 | QPDFObjectHandle cur = outlines.getKey("/First"); |
| 55 | 55 | QPDFObjGen::set seen; |
| 56 | - while (!cur.null() && seen.add(cur)) { | |
| 56 | + while (!cur.null()) { | |
| 57 | + if (!seen.add(cur)) { | |
| 58 | + cur.warn("Loop detected loop in /Outlines tree"); | |
| 59 | + return; | |
| 60 | + } | |
| 57 | 61 | m->outlines.emplace_back(QPDFOutlineObjectHelper::Accessor::create(cur, *this, 1)); |
| 58 | 62 | cur = cur.getKey("/Next"); |
| 59 | 63 | } | ... | ... |
libqpdf/QPDFOutlineObjectHelper.cc
| ... | ... | @@ -20,15 +20,20 @@ QPDFOutlineObjectHelper::QPDFOutlineObjectHelper( |
| 20 | 20 | return; |
| 21 | 21 | } |
| 22 | 22 | if (QPDFOutlineDocumentHelper::Accessor::checkSeen(m->dh, a_oh.getObjGen())) { |
| 23 | + a_oh.warn("Loop detected loop in /Outlines tree"); | |
| 23 | 24 | return; |
| 24 | 25 | } |
| 25 | 26 | |
| 26 | 27 | QPDFObjGen::set children; |
| 27 | 28 | QPDFObjectHandle cur = a_oh.getKey("/First"); |
| 28 | - while (!cur.null() && cur.isIndirect() && children.add(cur)) { | |
| 29 | + while (!cur.null() && cur.isIndirect()) { | |
| 30 | + if (!children.add(cur)) { | |
| 31 | + cur.warn("Loop detected loop in /Outlines tree"); | |
| 32 | + break; | |
| 33 | + } | |
| 29 | 34 | QPDFOutlineObjectHelper new_ooh(cur, dh, 1 + depth); |
| 30 | 35 | new_ooh.m->parent = std::make_shared<QPDFOutlineObjectHelper>(*this); |
| 31 | - m->kids.push_back(new_ooh); | |
| 36 | + m->kids.emplace_back(new_ooh); | |
| 32 | 37 | cur = cur.getKey("/Next"); |
| 33 | 38 | } |
| 34 | 39 | } | ... | ... |
qpdf/qtest/outlines.test
| ... | ... | @@ -32,7 +32,7 @@ foreach my $f (@outline_files) |
| 32 | 32 | |
| 33 | 33 | $td->runtest("outlines: outlines-with-loop --check", |
| 34 | 34 | {$td->COMMAND => "qpdf --check outlines-with-loop.pdf"}, |
| 35 | - {$td->FILE => "outlines-with-loop-check.out", $td->EXIT_STATUS => 0}, | |
| 35 | + {$td->FILE => "outlines-with-loop-check.out", $td->EXIT_STATUS => 3}, | |
| 36 | 36 | $td->NORMALIZE_NEWLINES); |
| 37 | 37 | |
| 38 | 38 | cleanup(); | ... | ... |
qpdf/qtest/qpdf/outlines-with-loop-check.out
| ... | ... | @@ -2,5 +2,6 @@ checking outlines-with-loop.pdf |
| 2 | 2 | PDF Version: 1.3 |
| 3 | 3 | File is not encrypted |
| 4 | 4 | File is not linearized |
| 5 | -No syntax or stream encoding errors found; the file may still contain | |
| 6 | -errors that qpdf cannot detect | |
| 5 | +WARNING: outlines-with-loop.pdf, object 4 0 at offset 637: Loop detected loop in /Outlines tree | |
| 6 | +WARNING: outlines-with-loop.pdf, object 5 0 at offset 855: Loop detected loop in /Outlines tree | |
| 7 | +qpdf: operation succeeded with warnings | ... | ... |
qpdf/qtest/qpdf/outlines-with-loop.out
| 1 | +WARNING: outlines-with-loop.pdf, object 4 0 at offset 637: Loop detected loop in /Outlines tree | |
| 2 | +WARNING: outlines-with-loop.pdf, object 5 0 at offset 855: Loop detected loop in /Outlines tree | |
| 1 | 3 | page 5: Potato 1 -> 5: /XYZ null null null -> [ 11 0 R /XYZ null null null ] |
| 2 | 4 | page 5: Potato 1 -> 5: /XYZ null null null -> [ 11 0 R /XYZ null null null ] |
| 3 | 5 | page 11: Mern 1.1 -> 11: /Fit -> [ 17 0 R /Fit ] | ... | ... |