Add TransFormWithClipAndSaveWithLocale embedder test.

Further exercise FPDFPage_TransFormWithClip() and make sure it works
correctly independent of the locale.

Copy over Chromium's base::ScopedLocale class for use in the new test.

BUG=chromium:970047

Change-Id: Ifa3cb2ad8c71902fc7fe893f0793d7e59ee574ab
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/56234
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 42f2093..91a2ccb 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -284,6 +284,7 @@
     "core/fxcrt",
     "testing:embedder_test_support",
     "testing:test_support",
+    "third_party:pdfium_base_test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/fpdfsdk/fpdf_transformpage_embeddertest.cpp b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
index db8e516..ff2fbcf 100644
--- a/fpdfsdk/fpdf_transformpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
@@ -3,8 +3,14 @@
 // found in the LICENSE file.
 
 #include "public/fpdf_transformpage.h"
+
+#include "build/build_config.h"
 #include "testing/embedder_test.h"
 
+#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#include "third_party/base/test/scoped_locale.h"
+#endif
+
 class FPDFTransformEmbedderTest : public EmbedderTest {};
 
 TEST_F(FPDFTransformEmbedderTest, GetBoundingBoxes) {
@@ -450,3 +456,64 @@
     CloseSavedDocument();
   }
 }
+
+#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+TEST_F(FPDFTransformEmbedderTest, TransFormWithClipAndSaveWithLocale) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kShrunkMD5[] = "f4136cc9209207ab60eb8381a3df2e69";
+
+  pdfium::base::ScopedLocale scoped_locale("da_DK.UTF-8");
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    {
+      // Render the page after transforming.
+      // Note that the change should affect the rendering, but does not.
+      // It should behaves just like the case below, rather than the case above.
+      // TODO(bug_1328): The checksum below should be |kShrunkMD5|.
+      const FS_MATRIX half_matrix{0.5, 0, 0, 0.5, 0, 0};
+      EXPECT_TRUE(FPDFPage_TransFormWithClip(page, &half_matrix, nullptr));
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(200, page_width);
+      EXPECT_EQ(300, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kOriginalMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the transform.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    ASSERT_TRUE(OpenSavedDocument());
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(200, page_width);
+    EXPECT_EQ(300, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+#endif  // defined(OS_LINUX) || defined(OS_FUCHSIA)
diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn
index 275787c..dce2b1f 100644
--- a/third_party/BUILD.gn
+++ b/third_party/BUILD.gn
@@ -651,6 +651,25 @@
   }
 }
 
+jumbo_source_set("pdfium_base_test_support") {
+  testonly = true
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+    ":pdfium_third_party_config",
+  ]
+  sources = []
+  deps = []
+
+  if (is_posix || is_fuchsia) {
+    sources += [
+      "base/test/scoped_locale.cc",
+      "base/test/scoped_locale.h",
+    ]
+    deps += [ "//testing/gtest" ]
+  }
+}
+
 jumbo_source_set("skia_shared") {
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
diff --git a/third_party/base/test/scoped_locale.cc b/third_party/base/test/scoped_locale.cc
new file mode 100644
index 0000000..2643dd9
--- /dev/null
+++ b/third_party/base/test/scoped_locale.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium 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 "third_party/base/test/scoped_locale.h"
+
+#include <locale.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace pdfium {
+namespace base {
+
+ScopedLocale::ScopedLocale(const std::string& locale) {
+  prev_locale_ = setlocale(LC_ALL, nullptr);
+  EXPECT_TRUE(setlocale(LC_ALL, locale.c_str()) != nullptr)
+      << "Failed to set locale: " << locale;
+}
+
+ScopedLocale::~ScopedLocale() {
+  EXPECT_STREQ(prev_locale_.c_str(), setlocale(LC_ALL, prev_locale_.c_str()));
+}
+
+}  // namespace base
+}  // namespace pdfium
diff --git a/third_party/base/test/scoped_locale.h b/third_party/base/test/scoped_locale.h
new file mode 100644
index 0000000..ef6d6ea
--- /dev/null
+++ b/third_party/base/test/scoped_locale.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium 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 THIRD_PARTY_BASE_TEST_SCOPED_LOCALE_H_
+#define THIRD_PARTY_BASE_TEST_SCOPED_LOCALE_H_
+
+#include <string>
+
+namespace pdfium {
+namespace base {
+
+// Sets the given |locale| on construction, and restores the previous locale
+// on destruction.
+class ScopedLocale {
+ public:
+  explicit ScopedLocale(const std::string& locale);
+  ~ScopedLocale();
+
+ private:
+  std::string prev_locale_;
+
+  ScopedLocale(const ScopedLocale&) = delete;
+  ScopedLocale& operator=(const ScopedLocale&) = delete;
+};
+
+}  // namespace base
+}  // namespace pdfium
+
+#endif  // THIRD_PARTY_BASE_TEST_SCOPED_LOCALE_H_