Add FPDFPath_GetPoint() API

Combined with the previously added FPDFPath_CountPoint(), this allows
getting the coordinates of all points of a path.

Change-Id: Ic969723d4b01ee427498d38ce323c74147b87a9c
Reviewed-on: https://pdfium-review.googlesource.com/14111
Commit-Queue: dsinclair <dsinclair@chromium.org>
Reviewed-by: dsinclair <dsinclair@chromium.org>
diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp
index ca2a457..6826317 100644
--- a/fpdfsdk/fpdfedit_embeddertest.cpp
+++ b/fpdfsdk/fpdfedit_embeddertest.cpp
@@ -259,6 +259,39 @@
   // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4
   // FXPT_TYPE::LineTo).
   ASSERT_EQ(5, FPDFPath_CountPoint(green_rect));
+  // Verify actual coordinates.
+  FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
+  float x;
+  float y;
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(100, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(100, x);
+  EXPECT_EQ(140, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 2);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(140, x);
+  EXPECT_EQ(140, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 3);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(140, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(green_rect, 4);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(100, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
 
   EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
   FPDFPage_InsertObject(page, green_rect);
@@ -277,6 +310,27 @@
   // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2
   // FXPT_TYPE::LineTo).
   ASSERT_EQ(3, FPDFPath_CountPoint(black_path));
+  // Verify actual coordinates.
+  segment = FPDFPath_GetPathSegment(black_path, 0);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(400, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(black_path, 1);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(400, x);
+  EXPECT_EQ(200, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+  segment = FPDFPath_GetPathSegment(black_path, 2);
+  EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+  EXPECT_EQ(300, x);
+  EXPECT_EQ(100, y);
+  EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+  EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
+  // Make sure out of bounds index access fails properly.
+  EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3));
 
   FPDFPage_InsertObject(page, black_path);
   page_bitmap = RenderPage(page);
@@ -317,6 +371,21 @@
   // This should fail gracefully, even if path is NULL.
   ASSERT_EQ(-1, FPDFPath_CountPoint(nullptr));
 
+  // FPDFPath_GetPathSegment() with a non-path.
+  ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(img, 0));
+  // FPDFPath_GetPathSegment() with a NULL path.
+  ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(nullptr, 0));
+  float x;
+  float y;
+  // FPDFPathSegment_GetPoint() with a NULL segment.
+  EXPECT_FALSE(FPDFPathSegment_GetPoint(nullptr, &x, &y));
+
+  // FPDFPathSegment_GetType() with a NULL segment.
+  ASSERT_EQ(FPDF_SEGMENT_UNKNOWN, FPDFPathSegment_GetType(nullptr));
+
+  // FPDFPathSegment_GetClose() with a NULL segment.
+  EXPECT_FALSE(FPDFPathSegment_GetClose(nullptr));
+
   FPDFPageObj_Destroy(img);
 }
 
diff --git a/fpdfsdk/fpdfeditpath.cpp b/fpdfsdk/fpdfeditpath.cpp
index 164ab70..a91dfdb 100644
--- a/fpdfsdk/fpdfeditpath.cpp
+++ b/fpdfsdk/fpdfeditpath.cpp
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <vector>
+
 #include "public/fpdf_edit.h"
 
 #include "core/fpdfapi/page/cpdf_path.h"
@@ -26,6 +28,13 @@
 static_assert(CFX_GraphStateData::LineJoinBevel == FPDF_LINEJOIN_BEVEL,
               "CFX_GraphStateData::LineJoinBevel value mismatch");
 
+static_assert(static_cast<int>(FXPT_TYPE::LineTo) == FPDF_SEGMENT_LINETO,
+              "FXPT_TYPE::LineTo value mismatch");
+static_assert(static_cast<int>(FXPT_TYPE::BezierTo) == FPDF_SEGMENT_BEZIERTO,
+              "FXPT_TYPE::BezierTo value mismatch");
+static_assert(static_cast<int>(FXPT_TYPE::MoveTo) == FPDF_SEGMENT_MOVETO,
+              "FXPT_TYPE::MoveTo value mismatch");
+
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPageObj_CreateNewPath(float x,
                                                                     float y) {
   auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
@@ -125,6 +134,16 @@
   return pdfium::CollectionSize<int>(pPathObj->m_Path.GetPoints());
 }
 
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index) {
+  auto* pPathObj = CPDFPathObjectFromFPDFPageObject(path);
+  if (!pPathObj)
+    return nullptr;
+
+  const std::vector<FX_PATHPOINT>& points = pPathObj->m_Path.GetPoints();
+  return pdfium::IndexInBounds(points, index) ? &points[index] : nullptr;
+}
+
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPath_MoveTo(FPDF_PAGEOBJECT path,
                                                     float x,
                                                     float y) {
@@ -229,3 +248,30 @@
   pPathObj->m_GraphState.SetLineCap(lineCap);
   pPathObj->SetDirty(true);
 }
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y) {
+  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
+  if (!pPathPoint || !x || !y)
+    return false;
+
+  *x = pPathPoint->m_Point.x;
+  *y = pPathPoint->m_Point.y;
+
+  return true;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFPathSegment_GetType(FPDF_PATHSEGMENT segment) {
+  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
+
+  return pPathPoint ? static_cast<int>(pPathPoint->m_Type)
+                    : FPDF_SEGMENT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPathSegment_GetClose(FPDF_PATHSEGMENT segment) {
+  auto* pPathPoint = FXPathPointFromFPDFPathSegment(segment);
+
+  return pPathPoint ? pPathPoint->m_CloseFigure : false;
+}
diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp
index 9e9c31d..c52c598 100644
--- a/fpdfsdk/fpdfview.cpp
+++ b/fpdfsdk/fpdfview.cpp
@@ -357,6 +357,10 @@
   return static_cast<CFX_DIBitmap*>(bitmap);
 }
 
+const FX_PATHPOINT* FXPathPointFromFPDFPathSegment(FPDF_PATHSEGMENT segment) {
+  return static_cast<const FX_PATHPOINT*>(segment);
+}
+
 unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                   void* buffer,
                                                   unsigned long buflen) {
diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c
index 415543d..b3fe303 100644
--- a/fpdfsdk/fpdfview_c_api_test.c
+++ b/fpdfsdk/fpdfview_c_api_test.c
@@ -149,6 +149,10 @@
     CHK(FPDFPath_SetFillColor);
     CHK(FPDFPath_GetFillColor);
     CHK(FPDFPath_CountPoint);
+    CHK(FPDFPath_GetPathSegment);
+    CHK(FPDFPathSegment_GetPoint);
+    CHK(FPDFPathSegment_GetType);
+    CHK(FPDFPathSegment_GetClose);
     CHK(FPDFPath_MoveTo);
     CHK(FPDFPath_LineTo);
     CHK(FPDFPath_BezierTo);
diff --git a/fpdfsdk/fsdk_define.h b/fpdfsdk/fsdk_define.h
index e58ddb1..68ba585 100644
--- a/fpdfsdk/fsdk_define.h
+++ b/fpdfsdk/fsdk_define.h
@@ -28,6 +28,7 @@
 class CPDF_PathObject;
 class CPDF_Stream;
 class IFSDK_PAUSE_Adapter;
+class FX_PATHPOINT;
 
 // Layering prevents fxcrt from knowing about FPDF_FILEACCESS, so this can't
 // be a static method of IFX_SeekableReadStream.
@@ -74,6 +75,8 @@
 
 CFX_DIBitmap* CFXBitmapFromFPDFBitmap(FPDF_BITMAP bitmap);
 
+const FX_PATHPOINT* FXPathPointFromFPDFPathSegment(FPDF_PATHSEGMENT segment);
+
 unsigned long Utf16EncodeMaybeCopyAndReturnLength(const WideString& text,
                                                   void* buffer,
                                                   unsigned long buflen);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 9ee11cb..d73a740 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -42,6 +42,12 @@
 #define FPDF_PAGEOBJ_SHADING 4
 #define FPDF_PAGEOBJ_FORM 5
 
+// The path segment constants.
+#define FPDF_SEGMENT_UNKNOWN -1
+#define FPDF_SEGMENT_LINETO 0
+#define FPDF_SEGMENT_BEZIERTO 1
+#define FPDF_SEGMENT_MOVETO 2
+
 #define FPDF_FILLMODE_ALTERNATE 1
 #define FPDF_FILLMODE_WINDING 2
 
@@ -552,6 +558,7 @@
                                                           unsigned int* B,
                                                           unsigned int* A);
 
+// Experimental API.
 // Get number of point objects inside |path|.
 //
 //   path - handle to a path.
@@ -562,6 +569,45 @@
 // Returns the number of objects in |path| or -1 on failure.
 FPDF_EXPORT int FPDF_CALLCONV FPDFPath_CountPoint(FPDF_PAGEOBJECT path);
 
+// Experimental API.
+// Get segment in |path| at |index|.
+//
+//   path  - handle to a path.
+//   index - the index of a segment.
+//
+// Returns the handle to the segment, or NULL on faiure.
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFPath_GetPathSegment(FPDF_PAGEOBJECT path, int index);
+
+// Experimental API.
+// Get coordinates of |segment|.
+//
+//   segment  - handle to a segment.
+//   x      - the horizontal position of the segment.
+//   y      - the vertical position of the segment.
+//
+// Returns TRUE on success, otherwise |x| and |y| is not set.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPathSegment_GetPoint(FPDF_PATHSEGMENT segment, float* x, float* y);
+
+// Experimental API.
+// Get type of |segment|.
+//
+//   segment - handle to a segment.
+//
+// Returns one of the FPDF_SEGMENT_* values on success,
+// FPDF_SEGMENT_UNKNOWN on error.
+FPDF_EXPORT int FPDF_CALLCONV FPDFPathSegment_GetType(FPDF_PATHSEGMENT segment);
+
+// Experimental API.
+// Gets if the |segment| closes the current subpath of a given path.
+//
+//   segment - handle to a segment.
+//
+// Returns close flag for non-NULL segment, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPathSegment_GetClose(FPDF_PATHSEGMENT segment);
+
 // Move a path's current point.
 //
 // path   - the handle to the path object.
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 135d00a..3f18033 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -52,6 +52,7 @@
 typedef void* FPDF_STRUCTELEMENT;
 typedef void* FPDF_STRUCTTREE;
 typedef void* FPDF_TEXTPAGE;
+typedef void const* FPDF_PATHSEGMENT;
 
 #ifdef PDF_ENABLE_XFA
 typedef void* FPDF_STRINGHANDLE;