Add public API for setting the fill color on a text object

BUG=pdfium:719
R=npm@chromium.org

Change-Id: Ifd9330de265f8419d588b65fbd6a6187f17badd1
Reviewed-on: https://pdfium-review.googlesource.com/5950
Reviewed-by: Nicolás Peña <npm@chromium.org>
Commit-Queue: Nicolás Peña <npm@chromium.org>
diff --git a/AUTHORS b/AUTHORS
index 7499c10..9b63c71 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -31,7 +31,8 @@
 Nico Weber <thakis@chromium.org>
 Peter Kasting <pkasting@chromium.org>
 Raymes Khoury <raymes@chromium.org>
-Reid Kleckner <rnk@chromium.org>
+Reid Kleckner <rnk@chromium.org>

+Ryan Wiley <wileyrr@gmail.com>
 Robert Sesek <rsesek@chromium.org>
 Sam Clegg <sbc@chromium.org>
 Thomas Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 6643035..e9ea064 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -289,5 +289,7 @@
     if (charcode != CPDF_Font::kInvalidCharCode)
       pFont->AppendChar(&text, charcode);
   }
-  *buf << PDF_EncodeString(text, true) << " Tj ET\n";
+  ProcessGraphics(buf, pTextObj);
+  *buf << PDF_EncodeString(text, true) << " Tj ET";
+  *buf << " Q\n";
 }
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index 1f2d216..8368e8d 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -167,16 +167,46 @@
   CPDF_Font* pFont = CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman");
   pTextObj->m_TextState.SetFont(pFont);
   pTextObj->m_TextState.SetFontSize(10.0f);
+  float rgb[3] = {0.5f, 0.7f, 0.35f};
+  CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+  pTextObj->m_ColorState.SetFillColor(pCS, rgb, 3);
+
+  float rgb2[3] = {1, 0.9f, 0};
+  pTextObj->m_ColorState.SetStrokeColor(pCS, rgb2, 3);
+  pTextObj->m_GeneralState.SetFillAlpha(0.5f);
+  pTextObj->m_GeneralState.SetStrokeAlpha(0.8f);
   pTextObj->Transform(CFX_Matrix(1, 0, 0, 1, 100, 100));
   pTextObj->SetText("Hello World");
   CFX_ByteTextBuf buf;
   TestProcessText(&generator, &buf, pTextObj.get());
   CFX_ByteString textString = buf.MakeString();
-  EXPECT_LT(61, textString.GetLength());
-  EXPECT_EQ("BT 1 0 0 1 100 100 Tm /", textString.Left(23));
-  EXPECT_EQ(" 10 Tf <48656C6C6F20576F726C64> Tj ET\n", textString.Right(38));
+  int firstResourceAt = textString.Find('/') + 1;
+  int secondResourceAt = textString.ReverseFind('/') + 1;
+  CFX_ByteString firstString = textString.Left(firstResourceAt);
+  CFX_ByteString midString =
+      textString.Mid(firstResourceAt, secondResourceAt - firstResourceAt);
+  CFX_ByteString lastString =
+      textString.Right(textString.GetLength() - secondResourceAt);
+  CFX_ByteString compareString1 = "BT 1 0 0 1 100 100 Tm /";
+  // Color RGB values used are integers divided by 255.
+  CFX_ByteString compareString2 =
+      " 10 Tf q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /";
+  CFX_ByteString compareString3 = " gs <48656C6C6F20576F726C64> Tj ET Q\n";
+  EXPECT_LT(compareString1.GetLength() + compareString2.GetLength() +
+                compareString3.GetLength(),
+            textString.GetLength());
+  EXPECT_EQ(compareString1, firstString.Left(compareString1.GetLength()));
+  EXPECT_EQ(compareString2, midString.Right(compareString2.GetLength()));
+  EXPECT_EQ(compareString3, lastString.Right(compareString3.GetLength()));
+  CPDF_Dictionary* externalGS = TestGetResource(
+      &generator, "ExtGState",
+      lastString.Left(lastString.GetLength() - compareString3.GetLength()));
+  ASSERT_TRUE(externalGS);
+  EXPECT_EQ(0.5f, externalGS->GetNumberFor("ca"));
+  EXPECT_EQ(0.8f, externalGS->GetNumberFor("CA"));
   CPDF_Dictionary* fontDict = TestGetResource(
-      &generator, "Font", textString.Mid(23, textString.GetLength() - 61));
+      &generator, "Font",
+      midString.Left(midString.GetLength() - compareString2.GetLength()));
   ASSERT_TRUE(fontDict);
   EXPECT_EQ("Font", fontDict->GetStringFor("Type"));
   EXPECT_EQ("Type1", fontDict->GetStringFor("Subtype"));
@@ -216,12 +246,22 @@
   }
 
   CFX_ByteString textString = buf.MakeString();
-  EXPECT_LT(63, textString.GetLength());
-  EXPECT_EQ("BT 1 0 0 1 0 0 Tm /", textString.Left(19));
-  EXPECT_EQ(" 15.5 Tf <4920616D20696E646972656374> Tj ET\n",
-            textString.Right(44));
+  int firstResourceAt = textString.Find('/') + 1;
+  CFX_ByteString firstString = textString.Left(firstResourceAt);
+  CFX_ByteString lastString =
+      textString.Right(textString.GetLength() - firstResourceAt);
+  CFX_ByteString compareString1 = "BT 1 0 0 1 0 0 Tm /";
+  CFX_ByteString compareString2 =
+      " 15.5 Tf q <4920616D20696E646972656374> Tj ET Q\n";
+  EXPECT_LT(compareString1.GetLength() + compareString2.GetLength(),
+            textString.GetLength());
+  EXPECT_EQ(compareString1, textString.Left(compareString1.GetLength()));
+  EXPECT_EQ(compareString2, textString.Right(compareString2.GetLength()));
   CPDF_Dictionary* fontDict = TestGetResource(
-      &generator, "Font", textString.Mid(19, textString.GetLength() - 63));
+      &generator, "Font",
+      textString.Mid(compareString1.GetLength(),
+                     textString.GetLength() - compareString1.GetLength() -
+                         compareString2.GetLength()));
   ASSERT_TRUE(fontDict);
   EXPECT_TRUE(fontDict->GetObjNum());
   EXPECT_EQ("Font", fontDict->GetStringFor("Type"));
diff --git a/fpdfsdk/fpdfeditpage.cpp b/fpdfsdk/fpdfeditpage.cpp
index 6ed3758..2c58caf 100644
--- a/fpdfsdk/fpdfeditpage.cpp
+++ b/fpdfsdk/fpdfeditpage.cpp
@@ -304,3 +304,19 @@
   rotate %= 4;
   pPage->m_pFormDict->SetNewFor<CPDF_Number>("Rotate", rotate * 90);
 }
+
+FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
+                                   unsigned int R,
+                                   unsigned int G,
+                                   unsigned int B,
+                                   unsigned int A) {
+  if (!page_object || R > 255 || G > 255 || B > 255 || A > 255)
+    return false;
+
+  float rgb[3] = {R / 255.f, G / 255.f, B / 255.f};
+  auto* pPageObj = static_cast<CPDF_PageObject*>(page_object);
+  pPageObj->m_GeneralState.SetFillAlpha(A / 255.f);
+  pPageObj->m_ColorState.SetFillColor(
+      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
+  return true;
+}
diff --git a/fpdfsdk/fpdfeditpath.cpp b/fpdfsdk/fpdfeditpath.cpp
index 54937ef..ad5ca94 100644
--- a/fpdfsdk/fpdfeditpath.cpp
+++ b/fpdfsdk/fpdfeditpath.cpp
@@ -7,6 +7,7 @@
 #include "core/fpdfapi/page/cpdf_path.h"
 #include "core/fpdfapi/page/cpdf_pathobject.h"
 #include "core/fxcrt/fx_system.h"
+#include "fpdfsdk/fsdk_define.h"
 #include "third_party/base/ptr_util.h"
 
 DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPageObj_CreateNewPath(float x, float y) {
@@ -56,15 +57,7 @@
                                           unsigned int G,
                                           unsigned int B,
                                           unsigned int A) {
-  if (!path || R > 255 || G > 255 || B > 255 || A > 255)
-    return false;
-
-  float rgb[3] = {R / 255.f, G / 255.f, B / 255.f};
-  auto* pPathObj = static_cast<CPDF_PathObject*>(path);
-  pPathObj->m_GeneralState.SetFillAlpha(A / 255.f);
-  pPathObj->m_ColorState.SetFillColor(
-      CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB), rgb, 3);
-  return true;
+  return FPDFPageObj_SetFillColor(path, R, G, B, A);
 }
 
 DLLEXPORT FPDF_BOOL FPDFPath_GetFillColor(FPDF_PAGEOBJECT path,
diff --git a/fpdfsdk/fpdfedittext.cpp b/fpdfsdk/fpdfedittext.cpp
index 2dde2e6..3deae7e 100644
--- a/fpdfsdk/fpdfedittext.cpp
+++ b/fpdfsdk/fpdfedittext.cpp
@@ -447,6 +447,14 @@
              : LoadSimpleFont(pDoc, std::move(pFont), data, size, font_type);
 }
 
+DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetFillColor(FPDF_PAGEOBJECT text_object,
+                                                  unsigned int R,
+                                                  unsigned int G,
+                                                  unsigned int B,
+                                                  unsigned int A) {
+  return FPDFPageObj_SetFillColor(text_object, R, G, B, A);
+}
+
 DLLEXPORT void STDCALL FPDFFont_Close(FPDF_FONT font) {
   CPDF_Font* pFont = static_cast<CPDF_Font*>(font);
   if (!pFont)
diff --git a/fpdfsdk/fsdk_define.h b/fpdfsdk/fsdk_define.h
index b7691bb..e14cc19 100644
--- a/fpdfsdk/fsdk_define.h
+++ b/fpdfsdk/fsdk_define.h
@@ -79,5 +79,10 @@
 void CheckUnSupportError(CPDF_Document* pDoc, uint32_t err_code);
 void CheckUnSupportAnnot(CPDF_Document* pDoc, const CPDF_Annot* pPDFAnnot);
 void ProcessParseError(CPDF_Parser::Error err);
+FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
+                                   unsigned int R,
+                                   unsigned int G,
+                                   unsigned int B,
+                                   unsigned int A);
 
 #endif  // FPDFSDK_FSDK_DEFINE_H_
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 47a0e68..904231f 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -469,6 +469,21 @@
                                               int font_type,
                                               FPDF_BOOL cid);
 
+// Set the fill RGBA of a text object. Range of values: 0 - 255.
+//
+// text_object  - handle to the text object.
+// R            - the red component for the path fill color.
+// G            - the green component for the path fill color.
+// B            - the blue component for the path fill color.
+// A            - the fill alpha for the path.
+//
+// Returns TRUE on success.
+DLLEXPORT FPDF_BOOL STDCALL FPDFText_SetFillColor(FPDF_PAGEOBJECT text_object,
+                                                  unsigned int R,
+                                                  unsigned int G,
+                                                  unsigned int B,
+                                                  unsigned int A);
+
 // Close a loaded PDF font.
 //
 // font   - Handle to the loaded font.