PDF a11y: new API for fill and stroke colors

The text fill and stroke colors are now exposed by
FPDFText_GetFillColor() and FPDFText_GetStrokeColor(). Each API takes
separated parameters for R, G, B and A values.

Bug: chromium:985604
Change-Id: Ibb9990eba303b818674f0e61b30f74e779a9f156
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/60110
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxge/fx_dib.h b/core/fxge/fx_dib.h
index f61d1f2..c292e99 100644
--- a/core/fxge/fx_dib.h
+++ b/core/fxge/fx_dib.h
@@ -96,6 +96,10 @@
   return (bgr >> 16) & 0xff;
 }
 
+constexpr unsigned int FXSYS_GetUnsignedAlpha(float alpha) {
+  return static_cast<unsigned int>(alpha * 255.f + 0.5f);
+}
+
 #define FXSYS_GetCValue(cmyk) ((uint8_t)((cmyk) >> 24) & 0xff)
 #define FXSYS_GetMValue(cmyk) ((uint8_t)((cmyk) >> 16) & 0xff)
 #define FXSYS_GetYValue(cmyk) ((uint8_t)((cmyk) >> 8) & 0xff)
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 802cd90..0917b64 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -136,10 +136,6 @@
   return pMarkItem && pPageObj->m_ContentMarks.ContainsItem(pMarkItem);
 }
 
-unsigned int GetUnsignedAlpha(float alpha) {
-  return static_cast<unsigned int>(alpha * 255.f + 0.5f);
-}
-
 CPDF_FormObject* CPDFFormObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
   auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
   return pPageObj ? pPageObj->AsForm() : nullptr;
@@ -711,7 +707,7 @@
   *R = FXSYS_GetRValue(fillColor);
   *G = FXSYS_GetGValue(fillColor);
   *B = FXSYS_GetBValue(fillColor);
-  *A = GetUnsignedAlpha(pPageObj->m_GeneralState.GetFillAlpha());
+  *A = FXSYS_GetUnsignedAlpha(pPageObj->m_GeneralState.GetFillAlpha());
   return true;
 }
 
@@ -765,7 +761,7 @@
   *R = FXSYS_GetRValue(strokeColor);
   *G = FXSYS_GetGValue(strokeColor);
   *B = FXSYS_GetBValue(strokeColor);
-  *A = GetUnsignedAlpha(pPageObj->m_GeneralState.GetStrokeAlpha());
+  *A = FXSYS_GetUnsignedAlpha(pPageObj->m_GeneralState.GetStrokeAlpha());
   return true;
 }
 
diff --git a/fpdfsdk/fpdf_text.cpp b/fpdfsdk/fpdf_text.cpp
index 4d29d61..e204465 100644
--- a/fpdfsdk/fpdf_text.cpp
+++ b/fpdfsdk/fpdf_text.cpp
@@ -144,6 +144,57 @@
       charinfo.m_pTextObj->GetTextRenderMode());
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,
+                      int index,
+                      unsigned int* R,
+                      unsigned int* G,
+                      unsigned int* B,
+                      unsigned int* A) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage || !R || !G || !B || !A)
+    return false;
+
+  FPDF_CHAR_INFO charinfo;
+  textpage->GetCharInfo(index, &charinfo);
+  if (!charinfo.m_pTextObj)
+    return false;
+
+  FX_COLORREF fill_color = charinfo.m_pTextObj->m_ColorState.GetFillColorRef();
+  *R = FXSYS_GetRValue(fill_color);
+  *G = FXSYS_GetGValue(fill_color);
+  *B = FXSYS_GetBValue(fill_color);
+  *A = FXSYS_GetUnsignedAlpha(
+      charinfo.m_pTextObj->m_GeneralState.GetFillAlpha());
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,
+                        int index,
+                        unsigned int* R,
+                        unsigned int* G,
+                        unsigned int* B,
+                        unsigned int* A) {
+  CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
+  if (!textpage || !R || !G || !B || !A)
+    return false;
+
+  FPDF_CHAR_INFO charinfo;
+  textpage->GetCharInfo(index, &charinfo);
+  if (!charinfo.m_pTextObj)
+    return false;
+
+  FX_COLORREF stroke_color =
+      charinfo.m_pTextObj->m_ColorState.GetStrokeColorRef();
+  *R = FXSYS_GetRValue(stroke_color);
+  *G = FXSYS_GetGValue(stroke_color);
+  *B = FXSYS_GetBValue(stroke_color);
+  *A = FXSYS_GetUnsignedAlpha(
+      charinfo.m_pTextObj->m_GeneralState.GetStrokeAlpha());
+  return true;
+}
+
 FPDF_EXPORT double FPDF_CALLCONV FPDFText_GetCharAngle(FPDF_TEXTPAGE text_page,
                                                        int index) {
   CPDF_TextPage* textpage = GetTextPageForValidIndex(text_page, index);
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
index 40d0c97..e4d11ed 100644
--- a/fpdfsdk/fpdf_text_embeddertest.cpp
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -1297,3 +1297,69 @@
   FPDFText_ClosePage(text_page);
   UnloadPage(page);
 }
+
+TEST_F(FPDFTextEmbedderTest, GetFillColor) {
+  ASSERT_TRUE(OpenDocument("text_color.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  ASSERT_EQ(1, FPDFText_CountChars(text_page));
+
+  ASSERT_FALSE(
+      FPDFText_GetFillColor(nullptr, 0, nullptr, nullptr, nullptr, nullptr));
+  ASSERT_FALSE(
+      FPDFText_GetFillColor(text_page, -1, nullptr, nullptr, nullptr, nullptr));
+  ASSERT_FALSE(FPDFText_GetFillColor(text_page, 314, nullptr, nullptr, nullptr,
+                                     nullptr));
+  ASSERT_FALSE(
+      FPDFText_GetFillColor(text_page, 0, nullptr, nullptr, nullptr, nullptr));
+
+  unsigned int r;
+  unsigned int g;
+  unsigned int b;
+  unsigned int a;
+  ASSERT_TRUE(FPDFText_GetFillColor(text_page, 0, &r, &g, &b, &a));
+  ASSERT_EQ(0xffu, r);
+  ASSERT_EQ(0u, g);
+  ASSERT_EQ(0u, b);
+  ASSERT_EQ(0xffu, a);
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFTextEmbedderTest, GetStrokeColor) {
+  ASSERT_TRUE(OpenDocument("text_color.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
+  ASSERT_TRUE(text_page);
+
+  ASSERT_EQ(1, FPDFText_CountChars(text_page));
+
+  ASSERT_FALSE(
+      FPDFText_GetStrokeColor(nullptr, 0, nullptr, nullptr, nullptr, nullptr));
+  ASSERT_FALSE(FPDFText_GetStrokeColor(text_page, -1, nullptr, nullptr, nullptr,
+                                       nullptr));
+  ASSERT_FALSE(FPDFText_GetStrokeColor(text_page, 314, nullptr, nullptr,
+                                       nullptr, nullptr));
+  ASSERT_FALSE(FPDFText_GetStrokeColor(text_page, 0, nullptr, nullptr, nullptr,
+                                       nullptr));
+
+  unsigned int r;
+  unsigned int g;
+  unsigned int b;
+  unsigned int a;
+  ASSERT_TRUE(FPDFText_GetStrokeColor(text_page, 0, &r, &g, &b, &a));
+  ASSERT_EQ(0u, r);
+  ASSERT_EQ(0xffu, g);
+  ASSERT_EQ(0u, b);
+  ASSERT_EQ(0xffu, a);
+
+  FPDFText_ClosePage(text_page);
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 1e00348..0a7baf7 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -336,12 +336,14 @@
     CHK(FPDFText_GetCharBox);
     CHK(FPDFText_GetCharIndexAtPos);
     CHK(FPDFText_GetCharOrigin);
+    CHK(FPDFText_GetFillColor);
     CHK(FPDFText_GetFontInfo);
     CHK(FPDFText_GetFontSize);
     CHK(FPDFText_GetFontWeight);
     CHK(FPDFText_GetRect);
     CHK(FPDFText_GetSchCount);
     CHK(FPDFText_GetSchResultIndex);
+    CHK(FPDFText_GetStrokeColor);
     CHK(FPDFText_GetText);
     CHK(FPDFText_GetTextRenderMode);
     CHK(FPDFText_GetUnicode);
diff --git a/public/fpdf_text.h b/public/fpdf_text.h
index c7e9d71..04664be 100644
--- a/public/fpdf_text.h
+++ b/public/fpdf_text.h
@@ -142,6 +142,60 @@
 FPDFText_GetTextRenderMode(FPDF_TEXTPAGE text_page, int index);
 
 // Experimental API.
+// Function: FPDFText_GetFillColor
+//          Get the fill color of a particular character.
+// Parameters:
+//          text_page      -   Handle to a text page information structure.
+//                             Returned by FPDFText_LoadPage function.
+//          index          -   Zero-based index of the character.
+//          R              -   Pointer to an unsigned int number receiving the
+//                             red value of the fill color.
+//          G              -   Pointer to an unsigned int number receiving the
+//                             green value of the fill color.
+//          B              -   Pointer to an unsigned int number receiving the
+//                             blue value of the fill color.
+//          A              -   Pointer to an unsigned int number receiving the
+//                             alpha value of the fill color.
+// Return value:
+//          Whether the call succeeded. If false, |R|, |G|, |B| and |A| are
+//          unchanged.
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetFillColor(FPDF_TEXTPAGE text_page,
+                      int index,
+                      unsigned int* R,
+                      unsigned int* G,
+                      unsigned int* B,
+                      unsigned int* A);
+
+// Experimental API.
+// Function: FPDFText_GetStrokeColor
+//          Get the stroke color of a particular character.
+// Parameters:
+//          text_page      -   Handle to a text page information structure.
+//                             Returned by FPDFText_LoadPage function.
+//          index          -   Zero-based index of the character.
+//          R              -   Pointer to an unsigned int number receiving the
+//                             red value of the stroke color.
+//          G              -   Pointer to an unsigned int number receiving the
+//                             green value of the stroke color.
+//          B              -   Pointer to an unsigned int number receiving the
+//                             blue value of the stroke color.
+//          A              -   Pointer to an unsigned int number receiving the
+//                             alpha value of the stroke color.
+// Return value:
+//          Whether the call succeeded. If false, |R|, |G|, |B| and |A| are
+//          unchanged.
+//
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFText_GetStrokeColor(FPDF_TEXTPAGE text_page,
+                        int index,
+                        unsigned int* R,
+                        unsigned int* G,
+                        unsigned int* B,
+                        unsigned int* A);
+
+// Experimental API.
 // Function: FPDFText_GetCharAngle
 //          Get character rotation angle.
 // Parameters:
diff --git a/testing/resources/text_color.in b/testing/resources/text_color.in
new file mode 100644
index 0000000..7e76a39
--- /dev/null
+++ b/testing/resources/text_color.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+10 160 Td
+/F1 36 Tf
+0 1 0 RG
+1 0 0 rg
+2 Tr
+(0) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_color.pdf b/testing/resources/text_color.pdf
new file mode 100644
index 0000000..8dbcdd1
--- /dev/null
+++ b/testing/resources/text_color.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [ 0 0 200 200 ]
+  /Count 1
+  /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+  /Length 56
+>>
+stream
+BT
+10 160 Td
+/F1 36 Tf
+0 1 0 RG
+1 0 0 rg
+2 Tr
+(0) Tj
+ET
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000161 00000 n 
+0000000287 00000 n 
+0000000365 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+472
+%%EOF