From 6df9aa8f2c51b685eefc944f5b01176b42812f39 Mon Sep 17 00:00:00 2001 From: Rodrigo Tobar Date: Sat, 17 Dec 2022 13:32:35 +0800 Subject: [PATCH] LibPDF: Store page number, not Value, in OutlineItem The Value previously stored corresponded to a Reference to a Page object in the PDF document. This isn't useful information, since what we want to display at the end of the day is the page an outline item refers to. This commit changes the page member on OutlineItem to be a Optional (some destinations don't necessarily refer to a Page), which we resolve while building OutlineItems. --- Userland/Libraries/LibPDF/Document.cpp | 28 +++++++++++++++----------- Userland/Libraries/LibPDF/Document.h | 22 ++++++++++++-------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/Userland/Libraries/LibPDF/Document.cpp b/Userland/Libraries/LibPDF/Document.cpp index 6b0fd04973..54b67d3c0d 100644 --- a/Userland/Libraries/LibPDF/Document.cpp +++ b/Userland/Libraries/LibPDF/Document.cpp @@ -210,20 +210,24 @@ PDFErrorOr Document::build_outline() if (!outline_dict->contains(CommonNames::Last)) return {}; + HashMap page_number_by_index_ref; + for (u32 page_number = 0; page_number < m_page_object_indices.size(); ++page_number) { + page_number_by_index_ref.set(m_page_object_indices[page_number], page_number); + } + auto first_ref = outline_dict->get_value(CommonNames::First); - auto children = TRY(build_outline_item_chain(first_ref)); + auto children = TRY(build_outline_item_chain(first_ref, page_number_by_index_ref)); m_outline = adopt_ref(*new OutlineDict()); m_outline->children = move(children); - if (outline_dict->contains(CommonNames::Count)) m_outline->count = outline_dict->get_value(CommonNames::Count).get(); return {}; } -PDFErrorOr Document::create_destination_from_parameters(NonnullRefPtr array) +PDFErrorOr Document::create_destination_from_parameters(NonnullRefPtr array, HashMap const& page_number_by_index_ref) { auto page_ref = array->at(0); auto type_name = TRY(array->get_name_at(this, 1))->name(); @@ -253,7 +257,7 @@ PDFErrorOr Document::create_destination_from_parameters(NonnullRefP VERIFY_NOT_REACHED(); } - return Destination { type, page_ref, parameters }; + return Destination { type, page_number_by_index_ref.get(page_ref.as_ref_index()), parameters }; } PDFErrorOr> Document::get_inheritable_object(FlyString const& name, NonnullRefPtr object) @@ -265,14 +269,14 @@ PDFErrorOr> Document::get_inheritable_object(FlyString con return object->get_object(this, name); } -PDFErrorOr> Document::build_outline_item(NonnullRefPtr const& outline_item_dict) +PDFErrorOr> Document::build_outline_item(NonnullRefPtr const& outline_item_dict, HashMap const& page_number_by_index_ref) { auto outline_item = adopt_ref(*new OutlineItem {}); 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 children = TRY(build_outline_item_chain(first_ref)); + auto children = TRY(build_outline_item_chain(first_ref, page_number_by_index_ref)); for (auto& child : children) { child.parent = outline_item; } @@ -289,7 +293,7 @@ PDFErrorOr> Document::build_outline_item(NonnullRefPt if (dest_obj->is()) { auto dest_arr = dest_obj->cast(); - outline_item->dest = TRY(create_destination_from_parameters(dest_arr)); + outline_item->dest = TRY(create_destination_from_parameters(dest_arr, page_number_by_index_ref)); } else if (dest_obj->is()) { auto dest_name = dest_obj->cast()->name(); if (auto dests_value = m_catalog->get(CommonNames::Dests); dests_value.has_value()) { @@ -297,11 +301,11 @@ PDFErrorOr> Document::build_outline_item(NonnullRefPt auto entry = MUST(dests->get_object(this, dest_name)); if (entry->is()) { auto entry_array = entry->cast(); - outline_item->dest = TRY(create_destination_from_parameters(entry_array)); + outline_item->dest = TRY(create_destination_from_parameters(entry_array, page_number_by_index_ref)); } else { auto entry_dictionary = entry->cast(); auto d_array = MUST(entry_dictionary->get_array(this, CommonNames::D)); - outline_item->dest = TRY(create_destination_from_parameters(d_array)); + outline_item->dest = TRY(create_destination_from_parameters(d_array, page_number_by_index_ref)); } } else { return Error { Error::Type::MalformedPDF, "Malformed outline destination" }; @@ -326,7 +330,7 @@ PDFErrorOr> Document::build_outline_item(NonnullRefPt return outline_item; } -PDFErrorOr> Document::build_outline_item_chain(Value const& first_ref) +PDFErrorOr> Document::build_outline_item_chain(Value const& first_ref, HashMap const& page_number_by_index_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 @@ -339,7 +343,7 @@ PDFErrorOr> Document::build_outline_item_chain( auto first_value = TRY(get_or_load_value(first_ref.as_ref_index())).get>(); auto first_dict = first_value->cast(); - auto first = TRY(build_outline_item(first_dict)); + auto first = TRY(build_outline_item(first_dict, page_number_by_index_ref)); children.append(first); auto current_child_dict = first_dict; @@ -350,7 +354,7 @@ PDFErrorOr> Document::build_outline_item_chain( current_child_index = next_child_dict_ref.as_ref_index(); auto next_child_value = TRY(get_or_load_value(current_child_index)).get>(); auto next_child_dict = next_child_value->cast(); - auto next_child = TRY(build_outline_item(next_child_dict)); + auto next_child = TRY(build_outline_item(next_child_dict, page_number_by_index_ref)); children.append(next_child); current_child_dict = move(next_child_dict); diff --git a/Userland/Libraries/LibPDF/Document.h b/Userland/Libraries/LibPDF/Document.h index e2968b36bd..d2d54949ed 100644 --- a/Userland/Libraries/LibPDF/Document.h +++ b/Userland/Libraries/LibPDF/Document.h @@ -50,7 +50,7 @@ struct Destination { }; Type type; - Value page; + Optional page; Vector parameters; }; @@ -145,10 +145,10 @@ private: PDFErrorOr add_page_tree_node_to_page_tree(NonnullRefPtr const& page_tree); PDFErrorOr build_outline(); - PDFErrorOr> build_outline_item(NonnullRefPtr const& outline_item_dict); - PDFErrorOr> build_outline_item_chain(Value const& first_ref); + PDFErrorOr> build_outline_item(NonnullRefPtr const& outline_item_dict, HashMap const&); + PDFErrorOr> build_outline_item_chain(Value const& first_ref, HashMap const&); - PDFErrorOr create_destination_from_parameters(NonnullRefPtr); + PDFErrorOr create_destination_from_parameters(NonnullRefPtr, HashMap const&); PDFErrorOr> get_inheritable_object(FlyString const& name, NonnullRefPtr); @@ -227,10 +227,16 @@ struct Formatter : Formatter { } StringBuilder param_builder; - for (auto& param : destination.parameters) - param_builder.appendff("{} ", param); - - return Formatter::format(builder, "{{ type={} page={} params={} }}"sv, type_str, destination.page, param_builder.to_deprecated_string()); + TRY(Formatter::format(builder, "{{ type={} page="sv, type_str)); + if (destination.page.has_value()) + TRY(builder.put_literal("{}"sv)); + else + TRY(builder.put_u64(destination.page.value())); + for (auto& param : destination.parameters) { + TRY(builder.put_f64(double(param))); + TRY(builder.put_literal(" "sv)); + } + return builder.put_literal("}}"sv); } };