GetFontSize for Variable Text Annotations.

Part of feature work to make Pdfium more usable by mobile platforms that
wish to render comboboxes and textfields using custom displays. Will
return Tf font size for the annotation if applicable. This is obtained
from CPDFSDK_Widget because it is not guaranteed to be set on
CPDF_FormField which only sets font information if DR entry is present.

R=thestig@chromium.org

Bug: b/130297350
Change-Id: Iba866bb47cad78e2aa16343ae8d055887714e6f6
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/53051
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Ryan Smith <rycsmith@google.com>
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index c6214dc..a829028 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -923,3 +923,31 @@
   WideString ws = pFormField->GetOptionLabel(index);
   return Utf16EncodeMaybeCopyAndReturnLength(ws, buffer, buflen);
 }
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
+                      FPDF_ANNOTATION annot,
+                      float* value) {
+  if (!value)
+    return false;
+
+  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
+  if (!pForm)
+    return false;
+
+  CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!pAnnotDict)
+    return false;
+
+  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
+  CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict);
+  if (!pFormControl)
+    return false;
+
+  CPDFSDK_Widget* pWidget = pForm->GetWidget(pFormControl);
+  if (!pWidget)
+    return false;
+
+  *value = pWidget->GetFontSize();
+  return true;
+}
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 281eaa0..f64cd3b 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -1911,3 +1911,133 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeCombobox) {
+  // Open a file with combobox annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // All 3 widgets have Tf font size 12.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+    EXPECT_EQ(12.0, value);
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    float value_two;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_two));
+    EXPECT_EQ(12.0, value_two);
+
+    annot.reset(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    float value_three;
+    ASSERT_TRUE(
+        FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_three));
+    EXPECT_EQ(12.0, value_three);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeTextField) {
+  // Open a file with textfield annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // All 3 widgets have Tf font size 12.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+    EXPECT_EQ(12.0, value);
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    float value_two;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_two));
+    EXPECT_EQ(12.0, value_two);
+
+    annot.reset(FPDFPage_GetAnnot(page, 2));
+    ASSERT_TRUE(annot);
+
+    float value_three;
+    ASSERT_TRUE(
+        FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_three));
+    EXPECT_EQ(12.0, value_three);
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeInvalidAnnotationTypes) {
+  // Open a file with ink annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Annotations that do not have variable text and will return -1.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeInvalidArguments) {
+  // Open a file with combobox annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Check bad form handle / annot.
+    float value;
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(nullptr, annot.get(), &value));
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), nullptr, &value));
+    ASSERT_FALSE(FPDFAnnot_GetFontSize(nullptr, nullptr, &value));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, GetFontSizeNegative) {
+  // Open a file with textfield annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("text_form_negative_fontsize.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Obtain the first annotation, a text field with negative font size, -12.
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    float value;
+    ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
+    EXPECT_EQ(-12.0, value);
+  }
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 6788ef9..0af6df7 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -44,6 +44,7 @@
     CHK(FPDFAnnot_GetAttachmentPoints);
     CHK(FPDFAnnot_GetColor);
     CHK(FPDFAnnot_GetFlags);
+    CHK(FPDFAnnot_GetFontSize);
     CHK(FPDFAnnot_GetFormFieldAtPoint);
     CHK(FPDFAnnot_GetFormFieldFlags);
     CHK(FPDFAnnot_GetLinkedAnnot);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index f2622e6..7fb2405 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -591,6 +591,23 @@
                          FPDF_WCHAR* buffer,
                          unsigned long buflen);
 
+// Experimental API.
+// Get the float value of the font size for an |annot| with variable text.
+// If 0, the font is to be auto-sized: its size is computed as a function of
+// the height of the annotation rectangle.
+//
+//   hHandle - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//   value   - Required. Float which will be set to font size on success.
+//
+// Returns true if the font size was set in |value|, false on error or if
+// |value| not provided.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
+                      FPDF_ANNOTATION annot,
+                      float* value);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/testing/resources/text_form_negative_fontsize.in b/testing/resources/text_form_negative_fontsize.in
new file mode 100644
index 0000000..056b483
--- /dev/null
+++ b/testing/resources/text_form_negative_fontsize.in
@@ -0,0 +1,66 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [ 4 0 R ]
+    /DR 5 0 R
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}}
+<<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 300 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 -12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Subtype /Widget
+  /V (Mountain Lion)
+>>
+endobj
+{{object 5 0}} <<
+  /Font 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+  /F1 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+  {{streamlen}}
+>>
+stream
+  BT
+  0 0 0 rg
+  /F1 -12 Tf
+  250 150 Td
+  (Test Form with Negative Font Size) Tj
+  ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_form_negative_fontsize.pdf b/testing/resources/text_form_negative_fontsize.pdf
new file mode 100644
index 0000000..b4efcc0
--- /dev/null
+++ b/testing/resources/text_form_negative_fontsize.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [ 4 0 R ]
+    /DR 5 0 R
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj
+<<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources 5 0 R
+  /MediaBox [ 0 0 300 300 ]
+  /Contents 8 0 R
+  /Annots [ 4 0 R ]
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /FT /Tx
+  /T (Text Box)
+  /DA (0 0 0 rg /F1 -12 Tf)
+  /Rect [ 100 100 200 130 ]
+  /Subtype /Widget
+  /V (Mountain Lion)
+>>
+endobj
+5 0 obj <<
+  /Font 6 0 R
+>>
+endobj
+6 0 obj <<
+  /F1 7 0 R
+>>
+endobj
+7 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+  /Length 88
+>>
+stream
+  BT
+  0 0 0 rg
+  /F1 -12 Tf
+  250 150 Td
+  (Test Form with Negative Font Size) Tj
+  ET
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f 
+0000000015 00000 n 
+0000000124 00000 n 
+0000000189 00000 n 
+0000000325 00000 n 
+0000000483 00000 n 
+0000000518 00000 n 
+0000000551 00000 n 
+0000000627 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 9
+>>
+startxref
+766
+%%EOF