Add FPDFAnnot_GetVertices() API

This follows the same pattern as FPDF_GetTrailerEnds(), so the client
has to call this function twice, but allocation of the buffer happens
outside pdfium.

Change-Id: Ic733083eba0b110310d6bbdc48f874bac4c7f2d6
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/76050
Commit-Queue: Miklos V <vmiklos@collabora.co.uk>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/constants/annotation_common.h b/constants/annotation_common.h
index 471d244..6f96e62 100644
--- a/constants/annotation_common.h
+++ b/constants/annotation_common.h
@@ -26,6 +26,9 @@
 constexpr char kStructParent[] = "StructParent";
 constexpr char kOC[] = "OC";
 
+// Entries for polygon and polyline annotations.
+constexpr char kVertices[] = "Vertices";
+
 }  // namespace annotation
 }  // namespace pdfium
 
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index 28dbe14..13c73f6 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -809,6 +809,34 @@
   return true;
 }
 
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetVertices(FPDF_ANNOTATION annot,
+                      FS_POINTF* buffer,
+                      unsigned long length) {
+  FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
+  if (subtype != FPDF_ANNOT_POLYGON && subtype != FPDF_ANNOT_POLYLINE)
+    return 0;
+
+  CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+  if (!annot_dict)
+    return 0;
+
+  CPDF_Array* vertices = annot_dict->GetArrayFor(pdfium::annotation::kVertices);
+  if (!vertices)
+    return 0;
+
+  // Truncate to an even number.
+  unsigned long points_len = vertices->size() / 2;
+  if (buffer && length >= points_len) {
+    for (unsigned long i = 0; i < points_len; ++i) {
+      buffer[i].x = vertices->GetNumberAt(i * 2);
+      buffer[i].y = vertices->GetNumberAt(i * 2 + 1);
+    }
+  }
+
+  return points_len;
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
                                                      FPDF_BYTESTRING key) {
   CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 89e38e7..2b66e32 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -3221,3 +3221,70 @@
 
   UnloadPage(page);
 }
+
+TEST_F(FPDFAnnotEmbedderTest, PolygonAnnotation) {
+  ASSERT_TRUE(OpenDocument("polygon_annot.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // FPDFSignatureObj_GetTime() positive testing.
+    unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
+    const size_t kExpectedSize = 3;
+    ASSERT_EQ(kExpectedSize, size);
+    std::vector<FS_POINTF> vertices_buffer(size);
+    EXPECT_EQ(size,
+              FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size));
+    EXPECT_FLOAT_EQ(159.0f, vertices_buffer[0].x);
+    EXPECT_FLOAT_EQ(296.0f, vertices_buffer[0].y);
+    EXPECT_FLOAT_EQ(350.0f, vertices_buffer[1].x);
+    EXPECT_FLOAT_EQ(411.0f, vertices_buffer[1].y);
+    EXPECT_FLOAT_EQ(472.0f, vertices_buffer[2].x);
+    EXPECT_FLOAT_EQ(243.42f, vertices_buffer[2].y);
+
+    // FPDFAnnot_GetVertices() negative testing.
+    EXPECT_EQ(0U, FPDFAnnot_GetVertices(nullptr, nullptr, 0));
+
+    // vertices_buffer is not overwritten if it is too small.
+    vertices_buffer.resize(1);
+    vertices_buffer[0].x = 42;
+    vertices_buffer[0].y = 43;
+    size = FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(),
+                                 vertices_buffer.size());
+    EXPECT_EQ(kExpectedSize, size);
+    EXPECT_FLOAT_EQ(42, vertices_buffer[0].x);
+    EXPECT_FLOAT_EQ(43, vertices_buffer[0].y);
+  }
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // This has an odd number of elements in the vertices array, ignore the last
+    // element.
+    unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
+    const size_t kExpectedSize = 3;
+    ASSERT_EQ(kExpectedSize, size);
+    std::vector<FS_POINTF> vertices_buffer(size);
+    EXPECT_EQ(size,
+              FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size));
+    EXPECT_FLOAT_EQ(259.0f, vertices_buffer[0].x);
+    EXPECT_FLOAT_EQ(396.0f, vertices_buffer[0].y);
+    EXPECT_FLOAT_EQ(450.0f, vertices_buffer[1].x);
+    EXPECT_FLOAT_EQ(511.0f, vertices_buffer[1].y);
+    EXPECT_FLOAT_EQ(572.0f, vertices_buffer[2].x);
+    EXPECT_FLOAT_EQ(343.0f, vertices_buffer[2].y);
+  }
+
+  {
+    // Wrong annotation type.
+    ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
+    EXPECT_EQ(0U, FPDFAnnot_GetVertices(ink_annot.get(), nullptr, 0));
+  }
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 1142725..f3155d6 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -70,6 +70,7 @@
     CHK(FPDFAnnot_GetStringValue);
     CHK(FPDFAnnot_GetSubtype);
     CHK(FPDFAnnot_GetValueType);
+    CHK(FPDFAnnot_GetVertices);
     CHK(FPDFAnnot_HasAttachmentPoints);
     CHK(FPDFAnnot_HasKey);
     CHK(FPDFAnnot_IsChecked);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index 9306456..7159602 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -396,6 +396,22 @@
                                                       FS_RECTF* rect);
 
 // Experimental API.
+// Get the vertices of a polygon or polyline annotation. |buffer| is an array of
+// points of the annotation. If |length| is less than the returned length, or
+// |annot| or |buffer| is NULL, |buffer| will not be modified.
+//
+//   annot  - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+//   buffer - buffer for holding the points.
+//   length - length of the buffer in points.
+//
+// Returns the number of points if the annotation is of type polygon or
+// polyline, 0 otherwise.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetVertices(FPDF_ANNOTATION annot,
+                      FS_POINTF* buffer,
+                      unsigned long length);
+
+// Experimental API.
 // Check if |annot|'s dictionary has |key| as a key.
 //
 //   annot  - handle to an annotation.
diff --git a/testing/resources/polygon_annot.in b/testing/resources/polygon_annot.in
new file mode 100644
index 0000000..069ddc1
--- /dev/null
+++ b/testing/resources/polygon_annot.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-1)
+  /F 4
+  /Vertices [159 296 350 411 472 243.42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-2)
+  /F 4
+  /Vertices [259 396 450 511 572 343 42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/polygon_annot.pdf b/testing/resources/polygon_annot.pdf
new file mode 100644
index 0000000..8efaa27
--- /dev/null
+++ b/testing/resources/polygon_annot.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 612 792]
+  /Annots [
+    4 0 R 5 0 R
+  ]
+  /Tabs /R
+>>
+endobj
+4 0 obj <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-1)
+  /F 4
+  /Vertices [159 296 350 411 472 243.42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+5 0 obj <<
+  /Type /Annot
+  /Subtype /Polygon
+  /NM (Polygon-2)
+  /F 4
+  /Vertices [259 396 450 511 572 343 42]
+  /P 3 0 R
+  /C [1 0.90196 0]
+  /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000131 00000 n 
+0000000251 00000 n 
+0000000429 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+607
+%%EOF