Add a more complex test for a PDF saving bug

Add a second PDFEditImgTest case to further demonstrate a PDF saving
bug, where a no-op FPDFPageObj_SetMatrix() call causes the saved PDF to
have incorrect transformation matrices. This second test case has an
image inside a form. This test also demonstrates FPDFPageObj_GetMatrix()
behavior and shows what the matrix is relative to. Mention this in the
API's documentation.

Bug: pdfium:2132
Change-Id: Ibdfac7de37c43ffec45da6d093c2e8798b85950d
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/116911
Reviewed-by: Thomas Sepez <tsepez@google.com>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_editimg_embeddertest.cpp b/fpdfsdk/fpdf_editimg_embeddertest.cpp
index ebb6452..c06d495 100644
--- a/fpdfsdk/fpdf_editimg_embeddertest.cpp
+++ b/fpdfsdk/fpdf_editimg_embeddertest.cpp
@@ -223,3 +223,73 @@
   constexpr char kWrongChecksum[] = "d01c62c9ee094f5be545f006148808b5";
   VerifySavedDocument(kExpectedWidth, kExpectedHeight, kWrongChecksum);
 }
+
+TEST_F(PDFEditImgTest, GetAndSetMatrixForFormWithImage) {
+  constexpr int kExpectedWidth = 200;
+  constexpr int kExpectedHeight = 300;
+  constexpr char kExpectedChecksum[] = "fcb9007fd901d2052e2bd1c147b82800";
+
+  OpenDocument("form_object_with_image.pdf");
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
+                  kExpectedChecksum);
+  }
+
+  FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(form);
+  ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
+
+  FS_MATRIX matrix;
+  ASSERT_TRUE(FPDFPageObj_GetMatrix(form, &matrix));
+  EXPECT_FLOAT_EQ(60.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(30.0f, matrix.d);
+  EXPECT_FLOAT_EQ(0.0f, matrix.e);
+  EXPECT_FLOAT_EQ(270.0f, matrix.f);
+
+  ASSERT_TRUE(FPDFPageObj_SetMatrix(form, &matrix));
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
+                  kExpectedChecksum);
+  }
+
+  FPDF_PAGEOBJECT image = FPDFFormObj_GetObject(form, 0);
+  ASSERT_TRUE(image);
+  ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image));
+
+  ASSERT_TRUE(FPDFPageObj_GetMatrix(image, &matrix));
+  EXPECT_FLOAT_EQ(1.0f, matrix.a);
+  EXPECT_FLOAT_EQ(0.0f, matrix.b);
+  EXPECT_FLOAT_EQ(0.0f, matrix.c);
+  EXPECT_FLOAT_EQ(1.0f, matrix.d);
+  EXPECT_FLOAT_EQ(1.0f, matrix.e);
+  EXPECT_FLOAT_EQ(0.0f, matrix.f);
+
+  ASSERT_TRUE(FPDFPageObj_SetMatrix(image, &matrix));
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
+                  kExpectedChecksum);
+  }
+
+  ASSERT_TRUE(FPDFPage_GenerateContent(page));
+  ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+
+  {
+    ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+    CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
+                  kExpectedChecksum);
+  }
+
+  UnloadPage(page);
+
+  // TODO(crbug.com/pdfium/2132): Should be `kExpectedChecksum`.
+  constexpr char kWrongChecksum[] = "308e2263f01744b2749d68a9ca711fc6";
+  VerifySavedDocument(kExpectedWidth, kExpectedHeight, kWrongChecksum);
+}
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 082e39b..e2135f2 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -292,6 +292,11 @@
 //   |b d f|
 // and used to scale, rotate, shear and translate the page object.
 //
+// For page objects outside form objects, the matrix values are relative to the
+// page that contains it.
+// For page objects inside form objects, the matrix values are relative to the
+// form that contains it.
+//
 // Returns TRUE on success.
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFPageObj_GetMatrix(FPDF_PAGEOBJECT page_object, FS_MATRIX* matrix);
diff --git a/testing/resources/form_object_with_image.in b/testing/resources/form_object_with_image.in
new file mode 100644
index 0000000..ea499f8
--- /dev/null
+++ b/testing/resources/form_object_with_image.in
@@ -0,0 +1,68 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /XObject <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+1 0 0 -1 0 300 cm
+q
+60 0 0 -30 0 30 cm
+/F1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 200 30]
+  /Resources <<
+    /XObject <<
+      /IM1 6 0 R
+    >>
+  >>
+>>
+stream
+1 0 0 1 1 0 cm
+/IM1 Do
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/form_object_with_image.pdf b/testing/resources/form_object_with_image.pdf
new file mode 100644
index 0000000..e243a84
--- /dev/null
+++ b/testing/resources/form_object_with_image.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Resources <<
+    /XObject <<
+      /F1 5 0 R
+    >>
+  >>
+>>
+endobj
+4 0 obj <<
+  /Length 48
+>>
+stream
+1 0 0 -1 0 300 cm
+q
+60 0 0 -30 0 30 cm
+/F1 Do
+Q
+endstream
+endobj
+5 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 200 30]
+  /Resources <<
+    /XObject <<
+      /IM1 6 0 R
+    >>
+  >>
+>>
+stream
+1 0 0 1 1 0 cm
+/IM1 Do
+endstream
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Image
+  /Width 50
+  /Height 50
+  /BitsPerComponent 8
+  /ColorSpace /DeviceRGB
+  /Filter [/ASCIIHexDecode /FlateDecode]
+  /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000286 00000 n 
+0000000385 00000 n 
+0000000562 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 7
+>>
+startxref
+832
+%%EOF