Properly write floats in CFX_PSRenderer.

Use WriteFloat() and friends to write out float, CFX_Matrix, and
CFX_PointF values properly to a PostScript stream. Move
cpdf_contentstream_write_utils.* from core/fpdfapi/edit/ to core/fxge/
to make the layering work correctly.

This fixes a variant of https:://crbug.com/pdfium/937

Change-Id: I58afaeee4161493e0fb5a6103f423e2783a064fd
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/63471
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn
index b83b89a..7e2528a 100644
--- a/core/fpdfapi/edit/BUILD.gn
+++ b/core/fpdfapi/edit/BUILD.gn
@@ -7,8 +7,6 @@
 
 source_set("edit") {
   sources = [
-    "cpdf_contentstream_write_utils.cpp",
-    "cpdf_contentstream_write_utils.h",
     "cpdf_creator.cpp",
     "cpdf_creator.h",
     "cpdf_pagecontentgenerator.cpp",
@@ -21,7 +19,6 @@
   configs += [ "../../../:pdfium_core_config" ]
   deps = [
     "../../../constants",
-    "../../../third_party:skia_shared",
     "../../fxcrt",
     "../../fxge",
     "../font",
diff --git a/core/fpdfapi/edit/DEPS b/core/fpdfapi/edit/DEPS
deleted file mode 100644
index bcfd0a2..0000000
--- a/core/fpdfapi/edit/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-specific_include_rules = {
-  "cpdf_contentstream_write_utils.cpp": [
-    '+third_party/skia_shared',
-  ]
-}
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index c804c05..a67697a 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -12,7 +12,6 @@
 #include <tuple>
 #include <utility>
 
-#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
 #include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
 #include "core/fpdfapi/font/cpdf_truetypefont.h"
@@ -34,6 +33,7 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxge/cfx_contentstream_write_utils.h"
 #include "core/fxge/render_defines.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
diff --git a/core/fxge/BUILD.gn b/core/fxge/BUILD.gn
index ad47e0a..ead8abe 100644
--- a/core/fxge/BUILD.gn
+++ b/core/fxge/BUILD.gn
@@ -22,6 +22,8 @@
     "cfx_cliprgn.h",
     "cfx_color.cpp",
     "cfx_color.h",
+    "cfx_contentstream_write_utils.cpp",
+    "cfx_contentstream_write_utils.h",
     "cfx_defaultrenderdevice.h",
     "cfx_face.cpp",
     "cfx_face.h",
@@ -119,6 +121,7 @@
   ]
 
   deps = [
+    "../../third_party:skia_shared",
     "../fxcrt",
   ]
 
diff --git a/core/fxge/DEPS b/core/fxge/DEPS
index 6492756..f02b93c 100644
--- a/core/fxge/DEPS
+++ b/core/fxge/DEPS
@@ -1,3 +1,9 @@
 include_rules = [
   '+third_party/skia/include'
 ]
+
+specific_include_rules = {
+  "cfx_contentstream_write_utils.cpp": [
+    '+third_party/skia_shared',
+  ]
+}
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp b/core/fxge/cfx_contentstream_write_utils.cpp
similarity index 93%
rename from core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
rename to core/fxge/cfx_contentstream_write_utils.cpp
index 28165b1..f06c039 100644
--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
+++ b/core/fxge/cfx_contentstream_write_utils.cpp
@@ -2,7 +2,7 @@
 // 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_contentstream_write_utils.h"
+#include "core/fxge/cfx_contentstream_write_utils.h"
 
 #include "third_party/skia_shared/SkFloatToDecimal.h"
 
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h b/core/fxge/cfx_contentstream_write_utils.h
similarity index 69%
rename from core/fpdfapi/edit/cpdf_contentstream_write_utils.h
rename to core/fxge/cfx_contentstream_write_utils.h
index 3e14c9f..2e94295 100644
--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
+++ b/core/fxge/cfx_contentstream_write_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
-#define CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
+#ifndef CORE_FXGE_CFX_CONTENTSTREAM_WRITE_UTILS_H_
+#define CORE_FXGE_CFX_CONTENTSTREAM_WRITE_UTILS_H_
 
 #include <ostream>
 
@@ -13,4 +13,4 @@
 std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix);
 std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point);
 
-#endif  // CORE_FPDFAPI_EDIT_CPDF_CONTENTSTREAM_WRITE_UTILS_H_
+#endif  // CORE_FXGE_CFX_CONTENTSTREAM_WRITE_UTILS_H_
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index f9f7de2..237c9f8 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include "core/fxcrt/maybe_owned.h"
+#include "core/fxge/cfx_contentstream_write_utils.h"
 #include "core/fxge/cfx_fontcache.h"
 #include "core/fxge/cfx_gemodule.h"
 #include "core/fxge/cfx_glyphcache.h"
@@ -121,7 +122,7 @@
     if (pObject2Device)
       pos = pObject2Device->Transform(pos);
 
-    buf << pos.x << " " << pos.y;
+    buf << pos;
     switch (type) {
       case FXPT_TYPE::MoveTo:
         buf << " m ";
@@ -138,8 +139,7 @@
           pos1 = pObject2Device->Transform(pos1);
           pos2 = pObject2Device->Transform(pos2);
         }
-        buf << " " << pos1.x << " " << pos1.y << " " << pos2.x << " " << pos2.y
-            << " c";
+        buf << " " << pos1 << " " << pos2 << " c";
         if (closing)
           buf << " h";
         buf << "\n";
@@ -178,9 +178,7 @@
   SetGraphState(pGraphState);
 
   std::ostringstream buf;
-  buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
-      << pObject2Device->c << " " << pObject2Device->d << " "
-      << pObject2Device->e << " " << pObject2Device->f << "]cm ";
+  buf << "mx Cm [" << *pObject2Device << "]cm ";
   WriteToStream(&buf);
 
   OutputPath(pPathData, nullptr);
@@ -211,9 +209,7 @@
     SetGraphState(pGraphState);
     if (pObject2Device) {
       std::ostringstream buf;
-      buf << "mx Cm [" << pObject2Device->a << " " << pObject2Device->b << " "
-          << pObject2Device->c << " " << pObject2Device->d << " "
-          << pObject2Device->e << " " << pObject2Device->f << "]cm ";
+      buf << "mx Cm [" << *pObject2Device << "]cm ";
       WriteToStream(&buf);
     }
   }
@@ -254,9 +250,10 @@
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_DashArray != pGraphState->m_DashArray) {
     buf << "[";
-    for (const auto& dash : pGraphState->m_DashArray)
-      buf << dash << " ";
-    buf << "]" << pGraphState->m_DashPhase << " d\n";
+    for (float dash : pGraphState->m_DashArray)
+      WriteFloat(buf, dash);
+    buf << "]";
+    WriteFloat(buf, pGraphState->m_DashPhase) << " d\n";
   }
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_LineJoin != pGraphState->m_LineJoin) {
@@ -264,11 +261,11 @@
   }
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_LineWidth != pGraphState->m_LineWidth) {
-    buf << pGraphState->m_LineWidth << " w\n";
+    WriteFloat(buf, pGraphState->m_LineWidth) << " w\n";
   }
   if (!m_bGraphStateSet ||
       m_CurGraphState.m_MiterLimit != pGraphState->m_MiterLimit) {
-    buf << pGraphState->m_MiterLimit << " M\n";
+    WriteFloat(buf, pGraphState->m_MiterLimit) << " M\n";
   }
   m_CurGraphState = *pGraphState;
   m_bGraphStateSet = true;
@@ -316,8 +313,7 @@
   m_pStream->WriteString("q\n");
 
   std::ostringstream buf;
-  buf << "[" << matrix.a << " " << matrix.b << " " << matrix.c << " "
-      << matrix.d << " " << matrix.e << " " << matrix.f << "]cm ";
+  buf << "[" << matrix << "]cm ";
 
   int width = pSource->GetWidth();
   int height = pSource->GetHeight();
@@ -448,13 +444,14 @@
   if (bCMYK != m_bCmykOutput || !m_bColorSet || m_LastColor != color) {
     std::ostringstream buf;
     if (bCMYK) {
-      buf << FXSYS_GetCValue(color) / 255.0 << " "
-          << FXSYS_GetMValue(color) / 255.0 << " "
-          << FXSYS_GetYValue(color) / 255.0 << " "
-          << FXSYS_GetKValue(color) / 255.0 << " k\n";
+      WriteFloat(buf, FXSYS_GetCValue(color) / 255.0f) << " ";
+      WriteFloat(buf, FXSYS_GetMValue(color) / 255.0f) << " ";
+      WriteFloat(buf, FXSYS_GetYValue(color) / 255.0f) << " ";
+      WriteFloat(buf, FXSYS_GetKValue(color) / 255.0f) << " k\n";
     } else {
-      buf << FXARGB_R(color) / 255.0 << " " << FXARGB_G(color) / 255.0 << " "
-          << FXARGB_B(color) / 255.0 << " rg\n";
+      WriteFloat(buf, FXARGB_R(color) / 255.0f) << " ";
+      WriteFloat(buf, FXARGB_G(color) / 255.0f) << " ";
+      WriteFloat(buf, FXARGB_B(color) / 255.0f) << " rg\n";
     }
     if (bCMYK == m_bCmykOutput) {
       m_bColorSet = true;
@@ -548,18 +545,17 @@
     CFX_PointF point = TransformedPath.GetPoint(p);
     switch (TransformedPath.GetType(p)) {
       case FXPT_TYPE::MoveTo: {
-        buf << point.x << " " << point.y << " m\n";
+        buf << point << " m\n";
         break;
       }
       case FXPT_TYPE::LineTo: {
-        buf << point.x << " " << point.y << " l\n";
+        buf << point << " l\n";
         break;
       }
       case FXPT_TYPE::BezierTo: {
         CFX_PointF point1 = TransformedPath.GetPoint(p + 1);
         CFX_PointF point2 = TransformedPath.GetPoint(p + 2);
-        buf << point.x << " " << point.y << " " << point1.x << " " << point1.y
-            << " " << point2.x << " " << point2.y << " c\n";
+        buf << point << " " << point1 << " " << point2 << " c\n";
         p += 2;
         break;
       }
@@ -597,9 +593,7 @@
 
   SetColor(color);
   std::ostringstream buf;
-  buf << "q[" << mtObject2Device.a << " " << mtObject2Device.b << " "
-      << mtObject2Device.c << " " << mtObject2Device.d << " "
-      << mtObject2Device.e << " " << mtObject2Device.f << "]cm\n";
+  buf << "q[" << mtObject2Device << "]cm\n";
 
   CFX_FontCache* pCache = CFX_GEModule::Get()->GetFontCache();
   RetainPtr<CFX_GlyphCache> pGlyphCache = pCache->GetGlyphCache(pFont);
@@ -612,7 +606,7 @@
       buf << "/X" << ps_fontnum << " Ff " << font_size << " Fs Sf ";
       last_fontnum = ps_fontnum;
     }
-    buf << pCharPos[i].m_Origin.x << " " << pCharPos[i].m_Origin.y << " m";
+    buf << pCharPos[i].m_Origin << " m";
     ByteString hex = ByteString::Format("<%02X>", ps_glyphindex);
     buf << hex.AsStringView() << "Tj\n";
   }
diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp
index 424e1b4..374e103 100644
--- a/fpdfsdk/fpdf_flatten.cpp
+++ b/fpdfsdk/fpdf_flatten.cpp
@@ -14,7 +14,6 @@
 #include "constants/annotation_common.h"
 #include "constants/annotation_flags.h"
 #include "constants/page_object.h"
-#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -26,6 +25,7 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfdoc/cpdf_annot.h"
+#include "core/fxge/cfx_contentstream_write_utils.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 
 enum FPDF_TYPE { MAX, MIN };
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index 496b89e..3670533 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "constants/page_object.h"
-#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 #include "core/fpdfapi/page/cpdf_clippath.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/page/cpdf_pageobject.h"
@@ -22,6 +21,7 @@
 #include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxge/cfx_contentstream_write_utils.h"
 #include "core/fxge/cfx_pathdata.h"
 #include "core/fxge/render_defines.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index fbf3897..e5caa5a 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -1226,9 +1226,11 @@
       "q\n"
       "Q\n"
       "q\n"
-      "281 106.7 m 331 106.7 l 331 56.7 l 281 56.7 l 281 106.7 l h W* n\n"
+      "281 106.700012 m "
+      "331 106.700012 l 331 56.700012 l 281 56.700012 l 281 106.700012 l "
+      "h W* n\n"
       "q\n"
-      "[49.9 0 0 -50 281.1 106.6]cm 50 50 8[50 0 0 -50 0 "
+      "[49.900002 0 0 -50 281.10001 106.599976]cm 50 50 8[50 0 0 -50 0 "
       "50]currentfile/ASCII85Decode filter /DCTDecode filter false 3 "
       "colorimage\n"
       "s4IA0!\"_al8O`[\\!<<*#!!*'\"s4[N@!!ic5#6k>;#6tJ?#m^kH'FbHY$Odmc'+Yct)"
@@ -1293,9 +1295,9 @@
 q
 Q
 q
-281 106.7 m 331 106.7 l 331 56.7 l 281 56.7 l 281 106.7 l h W* n
+281 106.700012 m 331 106.700012 l 331 56.700012 l 281 56.700012 l 281 106.700012 l h W* n
 q
-[49.9 0 0 -50 281.1 106.6]cm 50 50 8[50 0 0 -50 0 50]currentfile/ASCII85Decode filter /FlateDecode filter false 3 colorimage
+[49.900002 0 0 -50 281.10001 106.599976]cm 50 50 8[50 0 0 -50 0 50]currentfile/ASCII85Decode filter /FlateDecode filter false 3 colorimage
 Gb"0;0`_7S!5bE%:[N')TE"rlzGQSs[!!*~>
 Q
 Q