Add FPDFPageObj_TransformF() API

Add a variation of the FPDFPageObj_Transform() API that takes a
FS_MATRIX, instead of 6 doubles for the matrix. The new API also returns
whether it succeeded or not.

- Reimplement FPDFPageObj_Transform() using FPDFPageObj_TransformF().
- Switch a couple of existing FPDFPageObj_Transform() calls in tests to
  use the new API.
- Add test to show the new API correctly handles bad inputs and returns
  false.

Bug: 352379279
Change-Id: I70c507c24ad69c71771e3a457fedeca37bdc1da4
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/121630
Reviewed-by: dan sinclair <dsinclair@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index e5ace98..2f7d6b0 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -308,7 +308,8 @@
   ScopedFPDFWideString text = GetFPDFWideString(L"这是第一句。 这是第二行。");
   EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
 
-  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200);
+  const FS_MATRIX matrix(1, 0, 0, 1, 50, 200);
+  ASSERT_TRUE(FPDFPageObj_TransformF(text_object, &matrix));
   FPDFPage_InsertObject(page.get(), text_object);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
 
@@ -355,7 +356,8 @@
   EXPECT_TRUE(
       FPDFText_SetCharcodes(text_object, charcodes.data(), charcodes.size()));
 
-  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200);
+  const FS_MATRIX matrix(1, 0, 0, 1, 50, 200);
+  ASSERT_TRUE(FPDFPageObj_TransformF(text_object, &matrix));
   FPDFPage_InsertObject(page.get(), text_object);
   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
 
@@ -5324,6 +5326,17 @@
   VerifySavedDocument(kExpectedWidth, kExpectedHeight, HelloWorldChecksum());
 }
 
+TEST_F(FPDFEditEmbedderTest, PageObjTransformFWithBadParameters) {
+  ScopedFPDFDocument doc(FPDF_CreateNewDocument());
+  ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
+  ASSERT_TRUE(image);
+
+  const FS_MATRIX matrix(1, 2, 3, 4, 5, 6);
+  EXPECT_FALSE(FPDFPageObj_TransformF(nullptr, nullptr));
+  EXPECT_FALSE(FPDFPageObj_TransformF(image.get(), nullptr));
+  EXPECT_FALSE(FPDFPageObj_TransformF(nullptr, &matrix));
+}
+
 class FPDFEditMoveEmbedderTest : public EmbedderTest {
  protected:
   std::vector<std::string> HashesForDocument(int page_count) {
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 9ddd89f..5ed4c08 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -640,12 +640,24 @@
                       double d,
                       double e,
                       double f) {
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  if (!pPageObj)
-    return;
+  const FS_MATRIX matrix((float)a, (float)b, (float)c, (float)d, (float)e,
+                         (float)f);
+  FPDFPageObj_TransformF(page_object, &matrix);
+}
 
-  CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
-  pPageObj->Transform(matrix);
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_TransformF(FPDF_PAGEOBJECT page_object, const FS_MATRIX* matrix) {
+  if (!matrix) {
+    return false;
+  }
+
+  CPDF_PageObject* cpage_object = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!cpage_object) {
+    return false;
+  }
+
+  cpage_object->Transform(CFXMatrixFromFSMatrix(*matrix));
+  return true;
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 542dd88..5042fcf 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -232,6 +232,7 @@
     CHK(FPDFPageObj_SetStrokeColor);
     CHK(FPDFPageObj_SetStrokeWidth);
     CHK(FPDFPageObj_Transform);
+    CHK(FPDFPageObj_TransformF);
     CHK(FPDFPage_CountObjects);
     CHK(FPDFPage_Delete);
     CHK(FPDFPage_GenerateContent);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 35b5803..deae22f 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -284,6 +284,21 @@
                       double f);
 
 // Experimental API.
+// Transform |page_object| by the given matrix.
+//
+//   page_object - handle to a page object.
+//   matrix      - the transform matrix.
+//
+// Returns TRUE on success.
+//
+// This can be used to scale, rotate, shear and translate the |page_object|.
+// It is an improved version of FPDFPageObj_Transform() that does not do
+// unnecessary double to float conversions, and only uses 1 parameter for the
+// matrix. It also returns whether the operation succeeded or not.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_TransformF(FPDF_PAGEOBJECT page_object, const FS_MATRIX* matrix);
+
+// Experimental API.
 // Get the transform matrix of a page object.
 //
 //   page_object - handle to a page object.