Add CPDF_PageContentGenerator::ProcessPath supporting path construction

See Table 4.9 for path construction operators. Ignoring v, y, because those can
be replaced with the more powerful c operator. The code added will be called by
public methods that do not exist yet.

BUG=pdfium:661

Change-Id: I9a1ad3fd0a601e4e3a292b55f3e5708fe0c9c8fb
Reviewed-on: https://pdfium-review.googlesource.com/2495
Commit-Queue: Nicolás Peña <npm@chromium.org>
Reviewed-by: dsinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index c7266d3..d0946a4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1734,6 +1734,7 @@
 test("pdfium_unittests") {
   sources = [
     "core/fdrm/crypto/fx_crypt_unittest.cpp",
+    "core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp",
     "core/fpdfapi/font/fpdf_font_cid_unittest.cpp",
     "core/fpdfapi/font/fpdf_font_unittest.cpp",
     "core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp",
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index a65d564..e2354e1 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -10,6 +10,8 @@
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
 #include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_path.h"
+#include "core/fpdfapi/page/cpdf_pathobject.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_name.h"
@@ -40,9 +42,10 @@
 void CPDF_PageContentGenerator::GenerateContent() {
   CFX_ByteTextBuf buf;
   for (CPDF_PageObject* pPageObj : m_pageObjects) {
-    CPDF_ImageObject* pImageObject = pPageObj->AsImage();
-    if (pImageObject)
+    if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
       ProcessImage(&buf, pImageObject);
+    else if (CPDF_PathObject* pPathObj = pPageObj->AsPath())
+      ProcessPath(&buf, pPathObj);
   }
   CPDF_Dictionary* pPageDict = m_pPage->m_pFormDict;
   CPDF_Object* pContent =
@@ -109,3 +112,59 @@
 
   *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
 }
+
+// Processing path with operators from Tables 4.9 and 4.10 of PDF spec 1.7:
+// "re" appends a rectangle (here, used only if the whole path is a rectangle)
+// "m" moves current point to the given coordinates
+// "l" creates a line from current point to the new point
+// "c" adds a Bezier curve from current to last point, using the two other
+// points as the Bezier control points
+// Note: "l", "c" change the current point
+// "h" closes the subpath (appends a line from current to starting point)
+// Path painting operators: "S", "n", "B", "f", "B*", "f*", depending on
+// the filling mode and whether we want stroking the path or not.
+void CPDF_PageContentGenerator::ProcessPath(CFX_ByteTextBuf* buf,
+                                            CPDF_PathObject* pPathObj) {
+  const FX_PATHPOINT* pPoints = pPathObj->m_Path.GetPoints();
+  if (pPathObj->m_Path.IsRect()) {
+    *buf << pPoints[0].m_PointX << " " << pPoints[0].m_PointY << " "
+         << (pPoints[2].m_PointX - pPoints[0].m_PointX) << " "
+         << (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re";
+  } else {
+    int numPoints = pPathObj->m_Path.GetPointCount();
+    for (int i = 0; i < numPoints; i++) {
+      if (i > 0)
+        *buf << " ";
+      *buf << pPoints[i].m_PointX << " " << pPoints[i].m_PointY;
+      int pointFlag = pPoints[i].m_Flag;
+      if (pointFlag == FXPT_MOVETO) {
+        *buf << " m";
+      } else if (pointFlag & FXPT_LINETO) {
+        *buf << " l";
+      } else if (pointFlag & FXPT_BEZIERTO) {
+        if (i + 2 >= numPoints || pPoints[i].m_Flag != FXPT_BEZIERTO ||
+            pPoints[i + 1].m_Flag != FXPT_BEZIERTO ||
+            (pPoints[i + 2].m_Flag & FXPT_BEZIERTO) == 0) {
+          // If format is not supported, close the path and paint
+          *buf << " h";
+          break;
+        }
+        *buf << " " << pPoints[i + 1].m_PointX << " " << pPoints[i + 1].m_PointY
+             << " " << pPoints[i + 2].m_PointX << " " << pPoints[i + 2].m_PointY
+             << " c";
+        if (pPoints[i + 2].m_Flag & FXPT_CLOSEFIGURE)
+          *buf << " h";
+        i += 2;
+      }
+      if (pointFlag & FXPT_CLOSEFIGURE)
+        *buf << " h";
+    }
+  }
+  if (pPathObj->m_FillType == 0)
+    *buf << (pPathObj->m_bStroke ? " S" : " n");
+  else if (pPathObj->m_FillType == FXFILL_WINDING)
+    *buf << (pPathObj->m_bStroke ? " B" : " f");
+  else if (pPathObj->m_FillType == FXFILL_ALTERNATE)
+    *buf << (pPathObj->m_bStroke ? " B*" : " f*");
+  *buf << "\n";
+}
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
index ac06dcb..c74652e 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
@@ -16,6 +16,7 @@
 class CPDF_ImageObject;
 class CPDF_Page;
 class CPDF_PageObject;
+class CPDF_PathObject;
 
 class CPDF_PageContentGenerator {
  public:
@@ -25,6 +26,10 @@
   void GenerateContent();
 
  private:
+  friend class cpdf_pagecontentgenerator_ProcessRect_Test;
+  friend class cpdf_pagecontentgenerator_ProcessPath_Test;
+
+  void ProcessPath(CFX_ByteTextBuf* buf, CPDF_PathObject* pPathObj);
   void ProcessImage(CFX_ByteTextBuf* buf, CPDF_ImageObject* pImageObj);
   CFX_ByteString RealizeResource(uint32_t dwResourceObjNum,
                                  const CFX_ByteString& bsType);
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
new file mode 100644
index 0000000..8812c0e
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -0,0 +1,89 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
+
+#include "core/fpdfapi/page/cpdf_page.h"
+#include "core/fpdfapi/page/cpdf_pathobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+TEST(cpdf_pagecontentgenerator, ProcessRect) {
+  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  pPathObj->m_Path.AppendRect(10, 5, 13, 30);
+  pPathObj->m_FillType = FXFILL_ALTERNATE;
+  pPathObj->m_bStroke = true;
+  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(nullptr, nullptr, false);
+  CPDF_PageContentGenerator generator(pTestPage.get());
+  CFX_ByteTextBuf buf;
+  generator.ProcessPath(&buf, pPathObj.get());
+  EXPECT_EQ("10 5 3 25 re B*\n", buf.MakeString());
+
+  pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  pPathObj->m_Path.SetPointCount(4);
+  FX_PATHPOINT* pPoints = pPathObj->m_Path.GetMutablePoints();
+  pPoints[0].m_PointX = 0;
+  pPoints[0].m_PointY = 0;
+  pPoints[0].m_Flag = FXPT_MOVETO;
+  pPoints[1].m_PointX = 5.2f;
+  pPoints[1].m_PointY = 0;
+  pPoints[1].m_Flag = FXPT_LINETO;
+  pPoints[2].m_PointX = 5.2f;
+  pPoints[2].m_PointY = 3.78f;
+  pPoints[2].m_Flag = FXPT_LINETO;
+  pPoints[3].m_PointX = 0;
+  pPoints[3].m_PointY = 3.78f;
+  pPoints[3].m_Flag = FXPT_LINETO | FXPT_CLOSEFIGURE;
+  pPathObj->m_FillType = 0;
+  pPathObj->m_bStroke = false;
+  buf.Clear();
+  generator.ProcessPath(&buf, pPathObj.get());
+  EXPECT_EQ("0 0 5.2 3.78 re n\n", buf.MakeString());
+}
+
+TEST(cpdf_pagecontentgenerator, ProcessPath) {
+  auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+  pPathObj->m_Path.SetPointCount(10);
+  FX_PATHPOINT* pPoints = pPathObj->m_Path.GetMutablePoints();
+  pPoints[0].m_PointX = 3.102f;
+  pPoints[0].m_PointY = 4.67f;
+  pPoints[0].m_Flag = FXPT_MOVETO;
+  pPoints[1].m_PointX = 5.45f;
+  pPoints[1].m_PointY = 0.29f;
+  pPoints[1].m_Flag = FXPT_LINETO;
+  pPoints[2].m_PointX = 4.24f;
+  pPoints[2].m_PointY = 3.15f;
+  pPoints[2].m_Flag = FXPT_BEZIERTO;
+  pPoints[3].m_PointX = 4.65f;
+  pPoints[3].m_PointY = 2.98f;
+  pPoints[3].m_Flag = FXPT_BEZIERTO;
+  pPoints[4].m_PointX = 3.456f;
+  pPoints[4].m_PointY = 0.24f;
+  pPoints[4].m_Flag = FXPT_BEZIERTO;
+  pPoints[5].m_PointX = 10.6f;
+  pPoints[5].m_PointY = 11.15f;
+  pPoints[5].m_Flag = FXPT_LINETO;
+  pPoints[6].m_PointX = 11;
+  pPoints[6].m_PointY = 12.5f;
+  pPoints[6].m_Flag = FXPT_LINETO;
+  pPoints[7].m_PointX = 11.46f;
+  pPoints[7].m_PointY = 12.67f;
+  pPoints[7].m_Flag = FXPT_BEZIERTO;
+  pPoints[8].m_PointX = 11.84f;
+  pPoints[8].m_PointY = 12.96f;
+  pPoints[8].m_Flag = FXPT_BEZIERTO;
+  pPoints[9].m_PointX = 12;
+  pPoints[9].m_PointY = 13.64f;
+  pPoints[9].m_Flag = FXPT_BEZIERTO | FXPT_CLOSEFIGURE;
+  pPathObj->m_FillType = FXFILL_WINDING;
+  pPathObj->m_bStroke = false;
+  auto pTestPage = pdfium::MakeUnique<CPDF_Page>(nullptr, nullptr, false);
+  CPDF_PageContentGenerator generator(pTestPage.get());
+  CFX_ByteTextBuf buf;
+  generator.ProcessPath(&buf, pPathObj.get());
+  EXPECT_EQ(
+      "3.102 4.67 m 5.45 0.29 l 4.24 3.15 4.65 2.98 3.456 0.24 c 10.6 11.15 l "
+      "11 12.5 l 11.46 12.67 11.84 12.96 12 13.64 c h f\n",
+      buf.MakeString());
+}