1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:27:35 +00:00

LibPDF: Simplify outline construction

While the Outline Items making up the document's Outline have all sorts
of cross-references (parent, first/last chlid, next/previous sibling,
etc), not all documents out there have fully-consistent references. Our
implementation already discarded some of that information too (e.g.,
/Parent and /Prev were never read), and trusted that /First and /Next
were good enough to traverse the whole hierarchy.

Where the current implementation failed was in assuming that /Last was
also a good source of information. There are documents out there were
/Last also points to dead ends, and were therefore causing a crash when
we verified that the last child found on a chain was the /Last child
declared by the parent. To fix this I'm simply removing the check, and
simplifying the function call to remove any references to /Last. This
way we affirm our commitment to /First and /Next as the main sources of
information.
This commit is contained in:
Rodrigo Tobar 2022-12-16 00:14:40 +08:00 committed by Andrew Kaster
parent 41bd304a7f
commit cb1a7cc721
2 changed files with 9 additions and 10 deletions

View file

@ -211,9 +211,8 @@ PDFErrorOr<void> Document::build_outline()
return {};
auto first_ref = outline_dict->get_value(CommonNames::First);
auto last_ref = outline_dict->get_value(CommonNames::Last);
auto children = TRY(build_outline_item_chain(first_ref, last_ref));
auto children = TRY(build_outline_item_chain(first_ref));
m_outline = adopt_ref(*new OutlineDict());
m_outline->children = move(children);
@ -273,9 +272,7 @@ PDFErrorOr<NonnullRefPtr<OutlineItem>> Document::build_outline_item(NonnullRefPt
if (outline_item_dict->contains(CommonNames::First)) {
VERIFY(outline_item_dict->contains(CommonNames::Last));
auto first_ref = outline_item_dict->get_value(CommonNames::First);
auto last_ref = outline_item_dict->get_value(CommonNames::Last);
auto children = TRY(build_outline_item_chain(first_ref, last_ref));
auto children = TRY(build_outline_item_chain(first_ref));
outline_item->children = move(children);
}
@ -326,10 +323,14 @@ PDFErrorOr<NonnullRefPtr<OutlineItem>> Document::build_outline_item(NonnullRefPt
return outline_item;
}
PDFErrorOr<NonnullRefPtrVector<OutlineItem>> Document::build_outline_item_chain(Value const& first_ref, Value const& last_ref)
PDFErrorOr<NonnullRefPtrVector<OutlineItem>> Document::build_outline_item_chain(Value const& first_ref)
{
// We used to receive a last_ref parameter, which was what the parent of this chain
// thought was this chain's last child. There are documents out there in the wild
// where this cross-references don't match though, and it seems like simply following
// the /First and /Next links is the way to go to construct the whole Outline
// (we already ignore the /Parent attribute too, which can also be out of sync).
VERIFY(first_ref.has<Reference>());
VERIFY(last_ref.has<Reference>());
NonnullRefPtrVector<OutlineItem> children;
@ -352,8 +353,6 @@ PDFErrorOr<NonnullRefPtrVector<OutlineItem>> Document::build_outline_item_chain(
current_child_dict = move(next_child_dict);
}
VERIFY(last_ref.as_ref_index() == current_child_index);
return children;
}