Add FPDFAnnot_GetFormAdditionalActionJavaScript() API
This is similar to FPDFAnnot_GetFormFieldType() and allows getting the
JavaScript of a given event. Such JavaScripts are used e.g. on a text
form which wants to accept dates.
Bug: pdfium:1885
Change-Id: Ieceb3042a309b9578e8a6751a60918c7e8d8f91d
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/97950
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index 3493485..75bdf6c 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -164,6 +164,21 @@
FPDF_OBJECT_REFERENCE,
"CPDF_Object::kReference value mismatch");
+// These checks ensure the consistency of annotation additional action event
+// values across core/ and public.
+static_assert(static_cast<int>(CPDF_AAction::kKeyStroke) ==
+ FPDF_ANNOT_AACTION_KEY_STROKE,
+ "CPDF_AAction::kKeyStroke value mismatch");
+static_assert(static_cast<int>(CPDF_AAction::kFormat) ==
+ FPDF_ANNOT_AACTION_FORMAT,
+ "CPDF_AAction::kFormat value mismatch");
+static_assert(static_cast<int>(CPDF_AAction::kValidate) ==
+ FPDF_ANNOT_AACTION_VALIDATE,
+ "CPDF_AAction::kValidate value mismatch");
+static_assert(static_cast<int>(CPDF_AAction::kCalculate) ==
+ FPDF_ANNOT_AACTION_CALCULATE,
+ "CPDF_AAction::kCalculate value mismatch");
+
bool HasAPStream(CPDF_Dictionary* pAnnotDict) {
return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::kNormal);
}
@@ -1224,6 +1239,28 @@
}
FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle,
+ FPDF_ANNOTATION annot,
+ int event,
+ FPDF_WCHAR* buffer,
+ unsigned long buflen) {
+ const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
+ if (!pFormField)
+ return 0;
+
+ if (event < FPDF_ANNOT_AACTION_KEY_STROKE ||
+ event > FPDF_ANNOT_AACTION_CALCULATE) {
+ return 0;
+ }
+
+ auto type = static_cast<CPDF_AAction::AActionType>(event);
+ CPDF_AAction additional_action = pFormField->GetAdditionalAction();
+ CPDF_Action action = additional_action.GetAction(type);
+ return Utf16EncodeMaybeCopyAndReturnLength(action.GetJavaScript(), buffer,
+ buflen);
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle,
FPDF_ANNOTATION annot,
FPDF_WCHAR* buffer,
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 39c3d5e..3d07911 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -3612,6 +3612,42 @@
UnloadPage(page);
}
+TEST_F(FPDFAnnotEmbedderTest, AnnotationJavaScript) {
+ ASSERT_TRUE(OpenDocument("annot_javascript.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+ EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
+
+ {
+ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+ ASSERT_TRUE(annot);
+
+ // FPDFAnnot_GetFormAdditionalActionJavaScript() positive testing.
+ unsigned long length_bytes = FPDFAnnot_GetFormAdditionalActionJavaScript(
+ form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT, nullptr, 0);
+ ASSERT_EQ(62u, length_bytes);
+ std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
+ EXPECT_EQ(62u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+ form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT,
+ buf.data(), length_bytes));
+ EXPECT_EQ(L"AFDate_FormatEx(\"yyyy-mm-dd\");",
+ GetPlatformWString(buf.data()));
+
+ // FPDFAnnot_GetFormAdditionalActionJavaScript() negative testing.
+ EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+ form_handle(), nullptr, 0, nullptr, 0));
+ EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+ nullptr, annot.get(), 0, nullptr, 0));
+ EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+ form_handle(), annot.get(), 0, nullptr, 0));
+ EXPECT_EQ(2u, FPDFAnnot_GetFormAdditionalActionJavaScript(
+ form_handle(), annot.get(), FPDF_ANNOT_AACTION_KEY_STROKE,
+ nullptr, 0));
+ }
+
+ UnloadPage(page);
+}
+
// Due to https://crbug.com/pdfium/570, the AnnotationBorder test above cannot
// actually render the line annotations inside line_annot.pdf. For now, use a
// square annotation in annots.pdf for testing.
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 6e47876..7951525 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -52,6 +52,7 @@
CHK(FPDFAnnot_GetFocusableSubtypes);
CHK(FPDFAnnot_GetFocusableSubtypesCount);
CHK(FPDFAnnot_GetFontSize);
+ CHK(FPDFAnnot_GetFormAdditionalActionJavaScript);
CHK(FPDFAnnot_GetFormControlCount);
CHK(FPDFAnnot_GetFormControlIndex);
CHK(FPDFAnnot_GetFormFieldAtPoint);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index ccfbb0f..0c0302c 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -82,6 +82,16 @@
#define FPDF_FORMFLAG_CHOICE_EDIT (1 << 18)
#define FPDF_FORMFLAG_CHOICE_MULTI_SELECT (1 << 21)
+// Additional actions type of form field:
+// K, on key stroke, JavaScript action.
+// F, on format, JavaScript action.
+// V, on validate, JavaScript action.
+// C, on calculate, JavaScript action.
+#define FPDF_ANNOT_AACTION_KEY_STROKE 12
+#define FPDF_ANNOT_AACTION_FORMAT 13
+#define FPDF_ANNOT_AACTION_VALIDATE 14
+#define FPDF_ANNOT_AACTION_CALCULATE 15
+
typedef enum FPDFANNOT_COLORTYPE {
FPDFANNOT_COLORTYPE_Color = 0,
FPDFANNOT_COLORTYPE_InteriorColor
@@ -495,6 +505,31 @@
float* border_width);
// Experimental API.
+// Get the JavaScript of an event of the annotation's additional actions.
+// |buffer| is only modified if |buflen| is large enough to hold the whole
+// JavaScript string. If |buflen| is smaller, the total size of the JavaScript
+// is still returned, but nothing is copied. If there is no JavaScript for
+// |event| in |annot|, an empty string is written to |buf| and 2 is returned,
+// denoting the size of the null terminator in the buffer. On other errors,
+// nothing is written to |buffer| and 0 is returned.
+//
+// hHandle - handle to the form fill module, returned by
+// FPDFDOC_InitFormFillEnvironment().
+// annot - handle to an interactive form annotation.
+// event - event type, one of the FPDF_ANNOT_AACTION_* values.
+// buffer - buffer for holding the value string, encoded in UTF-16LE.
+// buflen - length of the buffer in bytes.
+//
+// Returns the length of the string value in bytes, including the 2-byte
+// null terminator.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle,
+ FPDF_ANNOTATION annot,
+ int event,
+ FPDF_WCHAR* buffer,
+ unsigned long buflen);
+
+// Experimental API.
// Check if |annot|'s dictionary has |key| as a key.
//
// annot - handle to an annotation.
diff --git a/testing/resources/annot_javascript.in b/testing/resources/annot_javascript.in
new file mode 100644
index 0000000..fe9ac46
--- /dev/null
+++ b/testing/resources/annot_javascript.in
@@ -0,0 +1,42 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 612 792]
+ /Kids [3 0 R]
+ /Count 1
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /Rect [85 721 153 735]
+ /FT /Tx
+ /P 3 0 R
+ /T (Widget)
+ /AA <<
+ /F <<
+ /JS (AFDate_FormatEx\("yyyy-mm-dd"\);)
+ /S /JavaScript
+ >>
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/annot_javascript.pdf b/testing/resources/annot_javascript.pdf
new file mode 100644
index 0000000..fbeb5df
--- /dev/null
+++ b/testing/resources/annot_javascript.pdf
@@ -0,0 +1,53 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 612 792]
+ /Kids [3 0 R]
+ /Count 1
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /Rect [85 721 153 735]
+ /FT /Tx
+ /P 3 0 R
+ /T (Widget)
+ /AA <<
+ /F <<
+ /JS (AFDate_FormatEx\("yyyy-mm-dd"\);)
+ /S /JavaScript
+ >>
+ >>
+>>
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000108 00000 n
+0000000197 00000 n
+0000000292 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+504
+%%EOF