Use WriteFloat() inside WriteColorToStream()

Avoid writing out color values that violate the PDF spec.

Change-Id: I1a7b52407bedfbea443a306906e32236473addbe
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/117396
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 807d7e2..62a82e7 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -65,9 +65,9 @@
   }
 
   // TODO(thestig): Remove float to int to float conversion.
-  buf << FXSYS_GetRValue(colors.value()) / 255.0f << " "
-      << FXSYS_GetGValue(colors.value()) / 255.0f << " "
-      << FXSYS_GetBValue(colors.value()) / 255.0f;
+  WriteFloat(buf, FXSYS_GetRValue(colors.value()) / 255.0f) << " ";
+  WriteFloat(buf, FXSYS_GetGValue(colors.value()) / 255.0f) << " ";
+  WriteFloat(buf, FXSYS_GetBValue(colors.value()) / 255.0f);
   return true;
 }
 
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index d580460..b5d3822 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -78,7 +78,7 @@
   EXPECT_EQ("q 0 0 5.1999998 3.78 re n Q\n", ByteString(buf));
 }
 
-TEST_F(CPDFPageContentGeneratorTest, BUG_937) {
+TEST_F(CPDFPageContentGeneratorTest, Bug937) {
   static const std::vector<float> rgb = {0.000000000000000000001f, 0.7f, 0.35f};
   RetainPtr<CPDF_ColorSpace> pCS =
       CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
@@ -102,9 +102,11 @@
     fxcrt::ostringstream buf;
     TestProcessPath(&generator, &buf, pPathObj.get());
     EXPECT_EQ(
-        "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 200000000000000000000 w"
-        " 1 0 0 1 .00000000000000000000099999997 200000000000000 cm .000000000"
-        "00000000000099999997 .00000000000000000000099999997 100 100 re f Q\n",
+        "q 0 .7019608 .34901962 rg 0 .7019608 .34901962 RG "
+        "200000000000000000000 w "
+        "1 0 0 1 .00000000000000000000099999997 200000000000000 cm "
+        ".00000000000000000000099999997 .00000000000000000000099999997 100 100 "
+        "re f Q\n",
         ByteString(buf));
   }
 
@@ -136,10 +138,12 @@
 
     TestProcessPath(&generator, &buf, pPathObj.get());
     EXPECT_EQ(
-        "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 2 w 1 0 0 1 432 4999999"
-        "90000000 cm .00000000000000000000099999997 4.6700001 m .0000000000000"
-        "0000000099999997 100000000000000 l .000000000000099999998 3.1500001 3"
-        ".5699999 2.98 53.400002 5000000000000000000 c h f Q\n",
+        "q 0 .7019608 .34901962 rg 0 .7019608 .34901962 RG 2 w "
+        "1 0 0 1 432 499999990000000 cm "
+        ".00000000000000000000099999997 4.6700001 m "
+        ".00000000000000000000099999997 100000000000000 l "
+        ".000000000000099999998 3.1500001 3.5699999 2.98 53.400002 "
+        "5000000000000000000 c h f Q\n",
         ByteString(buf));
   }
 }
@@ -211,13 +215,20 @@
   ByteString path_string(buf);
 
   // Color RGB values used are integers divided by 255.
-  EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /",
-            path_string.First(48));
-  EXPECT_EQ(" gs 1 2 m 3 4 l 5 6 l h B Q\n", path_string.Last(28));
-  ASSERT_GT(path_string.GetLength(), 76U);
-  RetainPtr<const CPDF_Dictionary> external_gs =
-      TestGetResource(&generator, "ExtGState",
-                      path_string.Substr(48, path_string.GetLength() - 76));
+  const ByteStringView expected_string_start =
+      "q .50196081 .7019608 .34901962 rg 1 .90196079 0 RG /";
+  const ByteStringView expected_string_end = " gs 1 2 m 3 4 l 5 6 l h B Q\n";
+  const size_t expected_string_min_length =
+      expected_string_start.GetLength() + expected_string_end.GetLength();
+  EXPECT_EQ(expected_string_start,
+            path_string.First(expected_string_start.GetLength()));
+  EXPECT_EQ(expected_string_end,
+            path_string.Last(expected_string_end.GetLength()));
+  ASSERT_GT(path_string.GetLength(), expected_string_min_length);
+  RetainPtr<const CPDF_Dictionary> external_gs = TestGetResource(
+      &generator, "ExtGState",
+      path_string.Substr(expected_string_start.GetLength(),
+                         path_string.GetLength() - expected_string_min_length));
   ASSERT_TRUE(external_gs);
   EXPECT_EQ(0.5f, external_gs->GetFloatFor("ca"));
   EXPECT_EQ(0.8f, external_gs->GetFloatFor("CA"));
@@ -226,15 +237,26 @@
   pPathObj->mutable_graph_state().SetLineWidth(10.5f);
   buf.str("");
   TestProcessPath(&generator, &buf, pPathObj.get());
+  const ByteStringView expected_string_start2 =
+      "q .50196081 .7019608 .34901962 rg 1 .90196079 0 RG 10.5 w /";
   ByteString path_string2(buf);
-  EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG 10.5 w /",
-            path_string2.First(55));
-  EXPECT_EQ(" gs 1 2 m 3 4 l 5 6 l h B Q\n", path_string2.Last(28));
+  EXPECT_EQ(expected_string_start2,
+            path_string2.First(expected_string_start2.GetLength()));
+  EXPECT_EQ(expected_string_end,
+            path_string2.Last(expected_string_end.GetLength()));
 
   // Compare with the previous (should use same dictionary for gs)
-  EXPECT_EQ(path_string.GetLength() + 7, path_string2.GetLength());
-  EXPECT_EQ(path_string.Substr(48, path_string.GetLength() - 76),
-            path_string2.Substr(55, path_string2.GetLength() - 83));
+  const size_t expected_strings_length_difference =
+      expected_string_start2.GetLength() - expected_string_start.GetLength();
+  EXPECT_EQ(path_string.GetLength() + expected_strings_length_difference,
+            path_string2.GetLength());
+  EXPECT_EQ(
+      path_string.Substr(expected_string_start.GetLength(),
+                         path_string.GetLength() - expected_string_min_length),
+      path_string2.Substr(expected_string_start2.GetLength(),
+                          path_string2.GetLength() -
+                              expected_string_min_length -
+                              expected_strings_length_difference));
 }
 
 TEST_F(CPDFPageContentGeneratorTest, ProcessStandardText) {
@@ -278,7 +300,7 @@
       text_string.Last(text_string.GetLength() - second_resource_at.value());
   // q and Q must be outside the BT .. ET operations
   ByteString compare_string1 =
-      "q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /";
+      "q .50196081 .7019608 .34901962 rg 1 .90196079 0 RG /";
   // Color RGB values used are integers divided by 255.
   ByteString compare_string2 = " gs BT 1 0 0 1 100 100 Tm /";
   ByteString compare_string3 = " 10 Tf 0 Tr <48656C6C6F20576F726C64> Tj ET Q\n";