Add blend to GraphicsData

CL [1] added the ability to set the blend mode for a page object. This
CL adds the corresponding component to GraphicsData since the blend
mode is part of ExtGSState. In addition, a test using the SetBlendMode
method is added.
[1] https://pdfium-review.googlesource.com/c/5953/

Bug: pdfium:720
Change-Id: I49120284345185c200a45cc3b37ec59f0658e2dc
Reviewed-on: https://pdfium-review.googlesource.com/6510
Commit-Queue: Nicolás Peña <npm@chromium.org>
Reviewed-by: dsinclair <dsinclair@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index e06c28d..8b39a7a 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -269,10 +269,10 @@
   GraphicsData graphD;
   graphD.fillAlpha = pPageObj->m_GeneralState.GetFillAlpha();
   graphD.strokeAlpha = pPageObj->m_GeneralState.GetStrokeAlpha();
-  int blend_type = pPageObj->m_GeneralState.GetBlendType();
+  graphD.blendType = pPageObj->m_GeneralState.GetBlendType();
   if (graphD.fillAlpha == 1.0f && graphD.strokeAlpha == 1.0f &&
-      (blend_type == FXDIB_BLEND_UNSUPPORTED ||
-       blend_type == FXDIB_BLEND_NORMAL)) {
+      (graphD.blendType == FXDIB_BLEND_UNSUPPORTED ||
+       graphD.blendType == FXDIB_BLEND_NORMAL)) {
     return;
   }
 
@@ -288,8 +288,8 @@
     if (graphD.strokeAlpha != 1.0f)
       gsDict->SetNewFor<CPDF_Number>("CA", graphD.strokeAlpha);
 
-    if (blend_type != FXDIB_BLEND_UNSUPPORTED &&
-        blend_type != FXDIB_BLEND_NORMAL) {
+    if (graphD.blendType != FXDIB_BLEND_UNSUPPORTED &&
+        graphD.blendType != FXDIB_BLEND_NORMAL) {
       gsDict->SetNewFor<CPDF_Name>("BM",
                                    pPageObj->m_GeneralState.GetBlendMode());
     }
diff --git a/core/fpdfapi/page/cpdf_page.cpp b/core/fpdfapi/page/cpdf_page.cpp
index 092a1ca..075aa90 100644
--- a/core/fpdfapi/page/cpdf_page.cpp
+++ b/core/fpdfapi/page/cpdf_page.cpp
@@ -183,7 +183,9 @@
 bool GraphicsData::operator<(const GraphicsData& other) const {
   if (fillAlpha != other.fillAlpha)
     return fillAlpha < other.fillAlpha;
-  return strokeAlpha < other.strokeAlpha;
+  if (strokeAlpha != other.strokeAlpha)
+    return strokeAlpha < other.strokeAlpha;
+  return blendType < other.blendType;
 }
 
 bool FontData::operator<(const FontData& other) const {
diff --git a/core/fpdfapi/page/cpdf_page.h b/core/fpdfapi/page/cpdf_page.h
index 77cc793..0a080a5 100644
--- a/core/fpdfapi/page/cpdf_page.h
+++ b/core/fpdfapi/page/cpdf_page.h
@@ -26,6 +26,7 @@
 struct GraphicsData {
   float fillAlpha;
   float strokeAlpha;
+  int blendType;
   bool operator<(const GraphicsData& other) const;
 };
 
diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp
index 40081e6..1146a8c 100644
--- a/fpdfsdk/fpdfedit_embeddertest.cpp
+++ b/fpdfsdk/fpdfedit_embeddertest.cpp
@@ -556,6 +556,61 @@
   FPDF_ClosePage(page);
 }
 
+TEST_F(FPDFEditEmbeddertest, GraphicsData) {
+  // New page
+  std::unique_ptr<void, FPDFPageDeleter> page(
+      FPDFPage_New(CreateNewDocument(), 0, 612, 792));
+
+  // Create a rect with nontrivial graphics
+  FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
+  FPDFPageObj_SetBlendMode(rect1, "Color");
+  FPDFPage_InsertObject(page.get(), rect1);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+
+  // Check that the ExtGState was created
+  CPDF_Page* the_page = CPDFPageFromFPDFPage(page.get());
+  CPDF_Dictionary* graphics_dict =
+      the_page->m_pResources->GetDictFor("ExtGState");
+  ASSERT_TRUE(graphics_dict);
+  EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
+
+  // Add a text object causing no change to the graphics dictionary
+  FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
+  // Only alpha, the last component, matters for the graphics dictionary. And
+  // the default value is 255.
+  EXPECT_TRUE(FPDFText_SetFillColor(text1, 100, 100, 100, 255));
+  FPDFPage_InsertObject(page.get(), text1);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
+
+  // Add a text object increasing the size of the graphics dictionary
+  FPDF_PAGEOBJECT text2 =
+      FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
+  FPDFPage_InsertObject(page.get(), text2);
+  FPDFPageObj_SetBlendMode(text2, "Darken");
+  EXPECT_TRUE(FPDFText_SetFillColor(text2, 0, 0, 255, 150));
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
+
+  // Add a path that should reuse graphics
+  // TODO(npm): This causes a crash on Windows.
+  /*FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
+  FPDFPageObj_SetBlendMode(path, "Darken");
+  EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 200, 100, 150));
+  FPDFPage_InsertObject(page.get(), path);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));*/
+
+  // Add a rect increasing the size of the graphics dictionary
+  FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
+  FPDFPageObj_SetBlendMode(rect2, "Darken");
+  EXPECT_TRUE(FPDFPath_SetFillColor(rect2, 0, 0, 255, 150));
+  EXPECT_TRUE(FPDFPath_SetStrokeColor(rect2, 0, 0, 0, 200));
+  FPDFPage_InsertObject(page.get(), rect2);
+  EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+  EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
+}
+
 TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
   // Start with a blank page
   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index aceca35..022832f 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -136,23 +136,23 @@
 // |FPDFPage_GenerateContent| or any changes to |page| will be lost.
 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GenerateContent(FPDF_PAGE page);
 
-// Checks if |pageObject| contains transparency.
+// Checks if |page_object| contains transparency.
 //
-//   pageObject - handle to a page object.
+//   page_object - handle to a page object.
 //
 // Returns TRUE if |pageObject| contains transparency.
 DLLEXPORT FPDF_BOOL STDCALL
-FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT pageObject);
+FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT page_object);
 
-// Get type of |pageObject|.
+// Get type of |page_object|.
 //
-//   pageObject - handle to a page object.
+//   page_object - handle to a page object.
 //
 // Returns one of the FPDF_PAGEOBJ_* values on success, FPDF_PAGEOBJ_UNKNOWN on
 // error.
-DLLEXPORT int STDCALL FPDFPageObj_GetType(FPDF_PAGEOBJECT pageObject);
+DLLEXPORT int STDCALL FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object);
 
-// Transform |pageObject| by the given matrix.
+// Transform |page_object| by the given matrix.
 //
 //   page_object - handle to a page object.
 //   a           - matrix value.
@@ -304,30 +304,30 @@
                                                             float w,
                                                             float h);
 
-// Get the bounding box of |pageObject|.
+// Get the bounding box of |page_object|.
 //
-// pageObject - handle to a page object.
-// left       - pointer where the left coordinate will be stored
-// bottom     - pointer where the bottom coordinate will be stored
-// right      - pointer where the right coordinate will be stored
-// top        - pointer where the top coordinate will be stored
+// page_object  - handle to a page object.
+// left         - pointer where the left coordinate will be stored
+// bottom       - pointer where the bottom coordinate will be stored
+// right        - pointer where the right coordinate will be stored
+// top          - pointer where the top coordinate will be stored
 //
 // Returns TRUE on success.
-DLLEXPORT FPDF_BOOL STDCALL FPDFPageObj_GetBounds(FPDF_PAGEOBJECT pageObject,
+DLLEXPORT FPDF_BOOL STDCALL FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object,
                                                   float* left,
                                                   float* bottom,
                                                   float* right,
                                                   float* top);
 
-// Set the blend mode of |pageObject|.
+// Set the blend mode of |page_object|.
 //
-// pageObject - handle to a page object.
-// blend_mode - string containing the blend mode.
+// page_object  - handle to a page object.
+// blend_mode   - string containing the blend mode.
 //
 // Blend mode can be one of following: Color, ColorBurn, ColorDodge, Darken,
 // Difference, Exclusion, HardLight, Hue, Lighten, Luminosity, Multiply, Normal,
 // Overlay, Saturation, Screen, SoftLight
-DLLEXPORT void STDCALL FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page,
+DLLEXPORT void STDCALL FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object,
                                                 FPDF_BYTESTRING blend_mode);
 
 // Set the stroke RGBA of a path. Range of values: 0 - 255.
@@ -353,24 +353,25 @@
 // Returns TRUE on success
 DLLEXPORT FPDF_BOOL FPDFPath_SetStrokeWidth(FPDF_PAGEOBJECT path, float width);
 
-// Set the line join of |pageObject|.
+// Set the line join of |page_object|.
 //
-// pageObject - handle to a page object.
-// line_join - line join
+// page_object  - handle to a page object.
+// line_join    - line join
 //
 // Line join can be one of following: FPDF_LINEJOIN_MITER, FPDF_LINEJOIN_ROUND,
 // FPDF_LINEJOIN_BEVEL
-DLLEXPORT void STDCALL FPDFPath_SetLineJoin(FPDF_PAGEOBJECT page,
+DLLEXPORT void STDCALL FPDFPath_SetLineJoin(FPDF_PAGEOBJECT page_object,
                                             int line_join);
 
-// Set the line cap of |pageObject|.
+// Set the line cap of |page_object|.
 //
-// pageObject - handle to a page object.
-// line_cap - line cap
+// page_object - handle to a page object.
+// line_cap    - line cap
 //
 // Line cap can be one of following: FPDF_LINECAP_BUTT, FPDF_LINECAP_ROUND,
 // FPDF_LINECAP_PROJECTING_SQUARE
-DLLEXPORT void STDCALL FPDFPath_SetLineCap(FPDF_PAGEOBJECT page, int line_cap);
+DLLEXPORT void STDCALL FPDFPath_SetLineCap(FPDF_PAGEOBJECT page_object,
+                                           int line_cap);
 
 // Set the fill RGBA of a path. Range of values: 0 - 255.
 //