From 182639217f3ca359c1b5e10610231cb1bea77212 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Tue, 17 Oct 2023 08:07:59 -0400 Subject: [PATCH] LibPDF: Implement GoTo action for outline Outline items can contain either a /Dest key or an /A key. The /Dest key points to a "Destination" (various ways to reference a page in the same document). The /A key points to an "Action" which can have several types. One type, the /GoTo type, just also points to a Destination. Implement GoTo actions. This makes clicking "Contents" in the outline of https://developer.apple.com/library/archive/documentation/mac/pdf/Text.pdf work. (Almost all other items in this file's outline use /Dest. "Contents" could too, but it uses /A /GoTo for some reason.) (Other action types are things like opening a hyperlink, opening a different file, playing a sound, submitting a form, etc. Actions are also used for in-page links, not just in outlines. Many of these action types we'll likely never want to implement.) --- Userland/Libraries/LibPDF/CommonNames.h | 2 ++ Userland/Libraries/LibPDF/Document.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/Userland/Libraries/LibPDF/CommonNames.h b/Userland/Libraries/LibPDF/CommonNames.h index 7362983eb6..3c20a492af 100644 --- a/Userland/Libraries/LibPDF/CommonNames.h +++ b/Userland/Libraries/LibPDF/CommonNames.h @@ -9,6 +9,7 @@ #include #define ENUMERATE_COMMON_NAMES(X) \ + X(A) \ X(AIS) \ X(Alternate) \ X(ASCII85Decode) \ @@ -134,6 +135,7 @@ X(Root) \ X(Rotate) \ X(RunLengthDecode) \ + X(S) \ X(SA) \ X(SM) \ X(SMask) \ diff --git a/Userland/Libraries/LibPDF/Document.cpp b/Userland/Libraries/LibPDF/Document.cpp index 1c2402780a..fee1df2ab7 100644 --- a/Userland/Libraries/LibPDF/Document.cpp +++ b/Userland/Libraries/LibPDF/Document.cpp @@ -540,6 +540,22 @@ PDFErrorOr> Document::build_outline_item(NonnullRefPt if (outline_item_dict->contains(CommonNames::Dest)) { auto dest_obj = TRY(outline_item_dict->get_object(this, CommonNames::Dest)); outline_item->dest = TRY(create_destination_from_object(dest_obj, page_number_by_index_ref)); + } else if (outline_item_dict->contains(CommonNames::A)) { + // PDF 1.7 spec, "8.5 Actions" + auto action_dict = TRY(outline_item_dict->get_dict(this, CommonNames::A)); + if (action_dict->contains(CommonNames::S)) { + // PDF 1.7 spec, "TABLE 8.48 Action types" + auto action_type = TRY(action_dict->get_name(this, CommonNames::S))->name(); + if (action_type == "GoTo") { + // PDF 1.7 spec, "Go-To Actions" + if (action_dict->contains(CommonNames::D)) { + auto dest_obj = TRY(action_dict->get_object(this, CommonNames::D)); + outline_item->dest = TRY(create_destination_from_object(dest_obj, page_number_by_index_ref)); + } + } else { + dbgln("Unhandled action type {}", action_type); + } + } } if (outline_item_dict->contains(CommonNames::C)) {