Add experimental FPDFAnnot_SetURI() API.
Allow the creation of link annots. FPDFAnnot_SetURI() can then set the
URI for a newly created link annot.
Change FPDFAnnot_IsSupportedSubtype() to return true for link annots.
Change-Id: I0298d26e6b99f8f3df97d88814e99fd419935b99
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/79233
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index 338ea96..ddedcf1 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -316,10 +316,10 @@
// The supported subtypes must also be communicated in the user doc.
return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_FREETEXT ||
subtype == FPDF_ANNOT_HIGHLIGHT || subtype == FPDF_ANNOT_INK ||
- subtype == FPDF_ANNOT_POPUP || subtype == FPDF_ANNOT_SQUARE ||
- subtype == FPDF_ANNOT_SQUIGGLY || subtype == FPDF_ANNOT_STAMP ||
- subtype == FPDF_ANNOT_STRIKEOUT || subtype == FPDF_ANNOT_TEXT ||
- subtype == FPDF_ANNOT_UNDERLINE;
+ subtype == FPDF_ANNOT_LINK || subtype == FPDF_ANNOT_POPUP ||
+ subtype == FPDF_ANNOT_SQUARE || subtype == FPDF_ANNOT_SQUIGGLY ||
+ subtype == FPDF_ANNOT_STAMP || subtype == FPDF_ANNOT_STRIKEOUT ||
+ subtype == FPDF_ANNOT_TEXT || subtype == FPDF_ANNOT_UNDERLINE;
}
FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
@@ -1380,3 +1380,16 @@
return Utf16EncodeMaybeCopyAndReturnLength(pWidget->GetExportValue(), buffer,
buflen);
}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot,
+ const char* uri) {
+ if (!uri || FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK)
+ return false;
+
+ CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+ CPDF_Dictionary* action = annot_dict->SetNewFor<CPDF_Dictionary>("A");
+ action->SetNewFor<CPDF_Name>("Type", "Action");
+ action->SetNewFor<CPDF_Name>("S", "URI");
+ action->SetNewFor<CPDF_String>("URI", uri, /*bHex=*/false);
+ return true;
+}
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 03e6773..c5fef24 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -659,6 +659,84 @@
UnloadPage(page);
}
+TEST_F(FPDFAnnotEmbedderTest, AddAndSaveLinkAnnotation) {
+ ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+ {
+ ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
+ CompareBitmap(bitmap.get(), 200, 200, pdfium::kHelloWorldChecksum);
+ }
+ EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
+
+ constexpr char kUri[] = "https://pdfium.org/";
+
+ {
+ // Add a link annotation to the page and set its URI.
+ ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_LINK));
+ ASSERT_TRUE(annot);
+ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+ EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
+ EXPECT_TRUE(FPDFAnnot_SetURI(annot.get(), kUri));
+ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+
+ // Negative tests:
+ EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, nullptr));
+ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+ EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), nullptr));
+ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+ EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, kUri));
+ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+
+ // Position the link on top of "Hello, world!" without a border.
+ const FS_RECTF kRect = {19.0f, 48.0f, 85.0f, 60.0f};
+ EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &kRect));
+ EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/0.0f,
+ /*vertical_radius=*/0.0f,
+ /*border_width=*/0.0f));
+
+ VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0),
+ kUri);
+ }
+
+ {
+ // Add an ink annotation to the page. Trying to add a link to it fails.
+ ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
+ ASSERT_TRUE(annot);
+ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+ EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot.get()));
+ EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), kUri));
+ }
+
+ // Remove the ink annotation added above for negative testing.
+ EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
+ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+ // Save the document, closing the page.
+ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+ UnloadPage(page);
+
+ // Reopen the document and make sure it still renders the same. Since the link
+ // does not have a border, it does not affect the rendering.
+ ASSERT_TRUE(OpenSavedDocument());
+ page = LoadSavedPage(0);
+ ASSERT_TRUE(page);
+ VerifySavedRendering(page, 200, 200, pdfium::kHelloWorldChecksum);
+ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+ {
+ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+ ASSERT_TRUE(annot);
+ EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
+ VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
+ VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0),
+ kUri);
+ }
+
+ CloseSavedPage(page);
+ CloseSavedDocument();
+}
+
TEST_F(FPDFAnnotEmbedderTest, AddAndSaveUnderlineAnnotation) {
// Open a file with one annotation and load its first page.
ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index bb92322..2a68e11 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -91,6 +91,7 @@
CHK(FPDFAnnot_SetFocusableSubtypes);
CHK(FPDFAnnot_SetRect);
CHK(FPDFAnnot_SetStringValue);
+ CHK(FPDFAnnot_SetURI);
CHK(FPDFAnnot_UpdateObject);
CHK(FPDFPage_CloseAnnot);
CHK(FPDFPage_CreateAnnot);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index 06b1de3..e4729ce 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -91,7 +91,7 @@
// Experimental API.
// Check if an annotation subtype is currently supported for creation.
-// Currently supported subtypes: circle, highlight, ink, popup, square,
+// Currently supported subtypes: circle, highlight, ink, link, popup, square,
// squiggly, stamp, strikeout, text, and underline.
//
// subtype - the subtype to be checked.
@@ -891,6 +891,16 @@
FPDF_WCHAR* buffer,
unsigned long buflen);
+// Experimental API.
+// Add a URI action to |annot|, overwriting the existing action, if any.
+//
+// annot - handle to a link annotation.
+// uri - the URI to be set, encoded in 7-bit ASCII.
+//
+// Returns true if successful.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot,
+ const char* uri);
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus