Do no write out identity matrices in CPDF_PageContentGenerator

Concatenating the identity matrix is a no-op. Check for this case and
write out less data when generating PDFs. Update unit test expectations
to match the new behavior.

Change-Id: Ie157dffc4613369f194b5f5aa94578b2e2cfb810
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/117050
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index c427478..24d4496 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -418,7 +418,9 @@
   if (!ctm.IsIdentity()) {
     matrix.Concat(ctm.GetInverse());
   }
-  WriteMatrix(*buf, matrix) << " cm ";
+  if (!matrix.IsIdentity()) {
+    WriteMatrix(*buf, matrix) << " cm ";
+  }
 
   bool bWasInline = pStream->IsInline();
   if (bWasInline)
@@ -455,7 +457,9 @@
   if (!ctm.IsIdentity()) {
     matrix.Concat(ctm.GetInverse());
   }
-  WriteMatrix(*buf, matrix) << " cm ";
+  if (!matrix.IsIdentity()) {
+    WriteMatrix(*buf, matrix) << " cm ";
+  }
 
   *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
 }
@@ -517,7 +521,11 @@
 
   // TODO(crbug.com/pdfium/2132): Does this need to take the current
   // transformation matrix in `m_pObjHolder` into account?
-  WriteMatrix(*buf, pPathObj->matrix()) << " cm ";
+  const CFX_Matrix& matrix = pPathObj->matrix();
+  if (!matrix.IsIdentity()) {
+    WriteMatrix(*buf, matrix) << " cm ";
+  }
+
   ProcessPathPoints(buf, &pPathObj->path());
 
   if (pPathObj->has_no_filltype())
@@ -669,9 +677,14 @@
                                             CPDF_TextObject* pTextObj) {
   ProcessGraphics(buf, pTextObj);
   *buf << "BT ";
+
   // TODO(crbug.com/pdfium/2132): Does this need to take the current
   // transformation matrix in `m_pObjHolder` into account?
-  WriteMatrix(*buf, pTextObj->GetTextMatrix()) << " Tm ";
+  const CFX_Matrix& matrix = pTextObj->GetTextMatrix();
+  if (!matrix.IsIdentity()) {
+    WriteMatrix(*buf, matrix) << " Tm ";
+  }
+
   RetainPtr<CPDF_Font> pFont(pTextObj->GetFont());
   if (!pFont)
     pFont = CPDF_Font::GetStockFont(m_pDocument, "Helvetica");
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index 47f3130..d580460 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -63,7 +63,7 @@
   CPDF_PageContentGenerator generator(pTestPage.Get());
   fxcrt::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
-  EXPECT_EQ("q 1 0 0 1 0 0 cm 10 5 3 25 re B* Q\n", ByteString(buf));
+  EXPECT_EQ("q 10 5 3 25 re B* Q\n", ByteString(buf));
 
   pPathObj = std::make_unique<CPDF_PathObject>();
   pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove);
@@ -75,7 +75,7 @@
                                        CFX_Path::Point::Type::kLine);
   buf.str("");
   TestProcessPath(&generator, &buf, pPathObj.get());
-  EXPECT_EQ("q 1 0 0 1 0 0 cm 0 0 5.1999998 3.78 re n Q\n", ByteString(buf));
+  EXPECT_EQ("q 0 0 5.1999998 3.78 re n Q\n", ByteString(buf));
 }
 
 TEST_F(CPDFPageContentGeneratorTest, BUG_937) {
@@ -174,7 +174,7 @@
   fxcrt::ostringstream buf;
   TestProcessPath(&generator, &buf, pPathObj.get());
   EXPECT_EQ(
-      "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4499998 .28999999 l 4.2399998 "
+      "q 3.102 4.6700001 m 5.4499998 .28999999 l 4.2399998 "
       "3.1500001 4.6500001 2.98 3.4560001 .23999999 c 10.6000004 11.149999"
       "6 l 11 12.5 l 11.46 12.6700001 11.8400002 12.96 12 13.6400003 c h f"
       " Q\n",
@@ -213,12 +213,11 @@
   // Color RGB values used are integers divided by 255.
   EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /",
             path_string.First(48));
-  EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
-            path_string.Last(43));
-  ASSERT_GT(path_string.GetLength(), 91U);
+  EXPECT_EQ(" gs 1 2 m 3 4 l 5 6 l h B Q\n", path_string.Last(28));
+  ASSERT_GT(path_string.GetLength(), 76U);
   RetainPtr<const CPDF_Dictionary> external_gs =
       TestGetResource(&generator, "ExtGState",
-                      path_string.Substr(48, path_string.GetLength() - 91));
+                      path_string.Substr(48, path_string.GetLength() - 76));
   ASSERT_TRUE(external_gs);
   EXPECT_EQ(0.5f, external_gs->GetFloatFor("ca"));
   EXPECT_EQ(0.8f, external_gs->GetFloatFor("CA"));
@@ -230,8 +229,7 @@
   ByteString path_string2(buf);
   EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG 10.5 w /",
             path_string2.First(55));
-  EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
-            path_string2.Last(43));
+  EXPECT_EQ(" gs 1 2 m 3 4 l 5 6 l h B Q\n", path_string2.Last(28));
 
   // Compare with the previous (should use same dictionary for gs)
   EXPECT_EQ(path_string.GetLength() + 7, path_string2.GetLength());
@@ -358,7 +356,7 @@
   ByteString last_string =
       text_string.Last(text_string.GetLength() - first_resource_at.value());
   // q and Q must be outside the BT .. ET operations
-  ByteString compare_string1 = "q 0 0 5 4 re W* n BT 1 0 0 1 0 0 Tm /";
+  ByteString compare_string1 = "q 0 0 5 4 re W* n BT /";
   ByteString compare_string2 =
       " 15.5 Tf 4 Tr <4920616D20696E646972656374> Tj ET Q\n";
   EXPECT_LT(compare_string1.GetLength() + compare_string2.GetLength(),
@@ -406,7 +404,7 @@
   auto pDoc = std::make_unique<CPDF_TestDocument>();
   pDoc->CreateNewDoc();
   static constexpr uint8_t kContents[] =
-      "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 "
+      "q 3.102 4.6700001 m 5.4500012 .28999999 "
       "l 4.2399998 3.1499999 4.65 2.98 3.456 0.24 c 3.102 4.6700001 l h f Q\n";
   auto pStream = pdfium::MakeRetain<CPDF_Stream>(
       DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
@@ -422,7 +420,7 @@
   fxcrt::ostringstream process_buf;
   generator.ProcessPageObjects(&process_buf);
   EXPECT_EQ(
-      "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 l 4.2399998 3.14"
+      "q 3.102 4.6700001 m 5.4500012 .28999999 l 4.2399998 3.14"
       "99999 4.6500001 2.98 3.4560001 .24000001 c 3.102 4.6700001 l h f Q\n",
       ByteString(process_buf));
 }