Make some FPDFPage APIs locale independent.

PDFium must generate content stream data in a locale independent way.
e.g. Floats must be of the form 0.123456, and not 0,123456. Several
callers to ByteString::Format() do this incorrectly.

To fix this, pull out the WriteFloat() code and related code from
cpdf_pagecontentgenerator.cpp and put them in
cpdf_contentstream_write_utils.cpp. Then use it in
FPDFPage_TransFormWithClip() and FPDFPage_Flatten() where appropriate.

BUG=chromium:970047

Change-Id: I3f44cee8fe6db3914609fa21989a8c580eaa2248
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/56132
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/edit/BUILD.gn b/core/fpdfapi/edit/BUILD.gn
index 5595f8f..26ab715 100644
--- a/core/fpdfapi/edit/BUILD.gn
+++ b/core/fpdfapi/edit/BUILD.gn
@@ -8,6 +8,8 @@
 
 jumbo_source_set("edit") {
   sources = [
+    "cpdf_contentstream_write_utils.cpp",
+    "cpdf_contentstream_write_utils.h",
     "cpdf_creator.cpp",
     "cpdf_creator.h",
     "cpdf_pagecontentgenerator.cpp",
diff --git a/core/fpdfapi/edit/DEPS b/core/fpdfapi/edit/DEPS
index 8a4c38c..bcfd0a2 100644
--- a/core/fpdfapi/edit/DEPS
+++ b/core/fpdfapi/edit/DEPS
@@ -1,3 +1,5 @@
-include_rules = [
-  '+third_party/skia_shared',
-]
+specific_include_rules = {
+  "cpdf_contentstream_write_utils.cpp": [
+    '+third_party/skia_shared',
+  ]
+}
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
new file mode 100644
index 0000000..28165b1
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
@@ -0,0 +1,30 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// 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 "third_party/skia_shared/SkFloatToDecimal.h"
+
+std::ostream& WriteFloat(std::ostream& stream, float value) {
+  char buffer[pdfium::skia::kMaximumSkFloatToDecimalLength];
+  unsigned size = pdfium::skia::SkFloatToDecimal(value, buffer);
+  stream.write(buffer, size);
+  return stream;
+}
+
+std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix) {
+  WriteFloat(ar, matrix.a) << " ";
+  WriteFloat(ar, matrix.b) << " ";
+  WriteFloat(ar, matrix.c) << " ";
+  WriteFloat(ar, matrix.d) << " ";
+  WriteFloat(ar, matrix.e) << " ";
+  WriteFloat(ar, matrix.f);
+  return ar;
+}
+
+std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point) {
+  WriteFloat(ar, point.x) << " ";
+  WriteFloat(ar, point.y);
+  return ar;
+}
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.h b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
new file mode 100644
index 0000000..3e14c9f
--- /dev/null
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.h
@@ -0,0 +1,16 @@
+// Copyright 2019 PDFium Authors. All rights reserved.
+// 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_
+
+#include <ostream>
+
+#include "core/fxcrt/fx_coordinates.h"
+
+std::ostream& WriteFloat(std::ostream& stream, float value);
+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_
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 934101e..cdc8a57 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -8,11 +8,11 @@
 
 #include <map>
 #include <memory>
-#include <ostream>
 #include <set>
 #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"
@@ -36,33 +36,9 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "third_party/base/ptr_util.h"
 #include "third_party/base/stl_util.h"
-#include "third_party/skia_shared/SkFloatToDecimal.h"
 
 namespace {
 
-std::ostream& WriteFloat(std::ostream& stream, float value) {
-  char buffer[pdfium::skia::kMaximumSkFloatToDecimalLength];
-  unsigned size = pdfium::skia::SkFloatToDecimal(value, buffer);
-  stream.write(buffer, size);
-  return stream;
-}
-
-std::ostream& operator<<(std::ostream& ar, const CFX_Matrix& matrix) {
-  WriteFloat(ar, matrix.a) << " ";
-  WriteFloat(ar, matrix.b) << " ";
-  WriteFloat(ar, matrix.c) << " ";
-  WriteFloat(ar, matrix.d) << " ";
-  WriteFloat(ar, matrix.e) << " ";
-  WriteFloat(ar, matrix.f);
-  return ar;
-}
-
-std::ostream& operator<<(std::ostream& ar, const CFX_PointF& point) {
-  WriteFloat(ar, point.x) << " ";
-  WriteFloat(ar, point.y);
-  return ar;
-}
-
 bool GetColor(const CPDF_Color* pColor, float* rgb) {
   int intRGB[3];
   if (!pColor || !pColor->IsColorSpaceRGB() ||
diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp
index 01c9a82..470869b 100644
--- a/fpdfsdk/fpdf_flatten.cpp
+++ b/fpdfsdk/fpdf_flatten.cpp
@@ -14,6 +14,7 @@
 #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"
@@ -405,8 +406,13 @@
     }
     CFX_Matrix matrix = pAPDict->GetMatrixFor("Matrix");
     CFX_Matrix m = GetMatrix(rcAnnot, rcStream, matrix);
-    sStream += ByteString::Format("q %f 0 0 %f %f %f cm /%s Do Q\n", m.a, m.d,
-                                  m.e, m.f, sFormName.c_str());
+    m.b = 0;
+    m.c = 0;
+    std::ostringstream buf;
+    buf << m;
+    ByteString str(buf);
+    sStream += ByteString::Format("q %s cm /%s Do Q\n", str.c_str(),
+                                  sFormName.c_str());
     pNewXObject->SetDataAndRemoveFilter(sStream.AsRawSpan());
   }
   pPageDict->RemoveFor("Annots");
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index 8336665..fe49345 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -11,6 +11,7 @@
 #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"
@@ -170,19 +171,21 @@
   if (!pPage)
     return false;
 
-  std::ostringstream textBuf;
-  textBuf << "q ";
+  std::ostringstream text_buf;
+  text_buf << "q ";
 
   if (clipRect) {
     CFX_FloatRect rect = CFXFloatRectFromFSRECTF(*clipRect);
     rect.Normalize();
 
-    textBuf << ByteString::Format("%f %f %f %f re W* n ", rect.left,
-                                  rect.bottom, rect.Width(), rect.Height());
+    WriteFloat(text_buf, rect.left) << " ";
+    WriteFloat(text_buf, rect.bottom) << " ";
+    WriteFloat(text_buf, rect.Width()) << " ";
+    WriteFloat(text_buf, rect.Height()) << " re W* n ";
   }
   if (matrix) {
-    textBuf << ByteString::Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b,
-                                  matrix->c, matrix->d, matrix->e, matrix->f);
+    CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix);
+    text_buf << m << " cm ";
   }
 
   CPDF_Dictionary* pPageDict = pPage->GetDict();
@@ -196,7 +199,7 @@
 
   CPDF_Stream* pStream =
       pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
-  pStream->SetDataFromStringstream(&textBuf);
+  pStream->SetDataFromStringstream(&text_buf);
 
   CPDF_Stream* pEndStream =
       pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());