Make bounding box changes take effect immediately.

Similar to https://crbug.com/pdfium/1209 for FPDFPage_SetRotation(),
FPDFPage_SetCropBox() and FPDFPage_SetMediaBox() do not take effect
immediately. Fix this by adding more CPDF_Page::UpdateDimensions()
calls.

Add tests similar to the FPDFPage_SetRotation() tests to make sure this
works correctly.

Change-Id: I5cebff52d0a637c871d26036c2b5ad290608dab0
Reviewed-on: https://pdfium-review.googlesource.com/c/47670
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index 2b86abb..aedfff4 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -30,8 +30,11 @@
 void SetBoundingBox(CPDF_Page* page,
                     const ByteString& key,
                     const CFX_FloatRect& rect) {
-  if (page)
-    page->GetDict()->SetRectFor(key, rect);
+  if (!page)
+    return;
+
+  page->GetDict()->SetRectFor(key, rect);
+  page->UpdateDimensions();
 }
 
 bool GetBoundingBox(CPDF_Page* page,
diff --git a/fpdfsdk/fpdf_transformpage_embeddertest.cpp b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
index 2a80e4e..36f86f3 100644
--- a/fpdfsdk/fpdf_transformpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_transformpage_embeddertest.cpp
@@ -188,6 +188,153 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFTransformEmbedderTest, SetCropBox) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kCroppedMD5[] = "9937883715d5144c079fb8f7e3d4f395";
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      FS_RECTF cropbox;
+      EXPECT_FALSE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                       &cropbox.right, &cropbox.top));
+      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);
+    }
+
+    FPDFPage_SetCropBox(page, 10, 20, 100, 150);
+
+    {
+      // Render the page after setting the CropBox.
+      // Note that the change affects the rendering, as expected.
+      // It behaves just like the case below, rather than the case above.
+      FS_RECTF cropbox;
+      EXPECT_TRUE(FPDFPage_GetCropBox(page, &cropbox.left, &cropbox.bottom,
+                                      &cropbox.right, &cropbox.top));
+      EXPECT_EQ(10, cropbox.left);
+      EXPECT_EQ(20, cropbox.bottom);
+      EXPECT_EQ(100, cropbox.right);
+      EXPECT_EQ(150, cropbox.top);
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(90, page_width);
+      EXPECT_EQ(130, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the rotation.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    OpenSavedDocument(nullptr);
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    FS_RECTF cropbox;
+    EXPECT_TRUE(FPDFPage_GetCropBox(saved_page, &cropbox.left, &cropbox.bottom,
+                                    &cropbox.right, &cropbox.top));
+    EXPECT_EQ(10, cropbox.left);
+    EXPECT_EQ(20, cropbox.bottom);
+    EXPECT_EQ(100, cropbox.right);
+    EXPECT_EQ(150, cropbox.top);
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(90, page_width);
+    EXPECT_EQ(130, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kCroppedMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
+TEST_F(FPDFTransformEmbedderTest, SetMediaBox) {
+  const char kOriginalMD5[] = "0a90de37f52127619c3dfb642b5fa2fe";
+  const char kShrunkMD5[] = "eab5958f62f7ce65d7c32de98389fee1";
+
+  {
+    ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+    FPDF_PAGE page = LoadPage(0);
+    ASSERT_TRUE(page);
+
+    {
+      // Render the page as is.
+      FS_RECTF mediabox;
+      EXPECT_FALSE(FPDFPage_GetMediaBox(page, &mediabox.left, &mediabox.bottom,
+                                        &mediabox.right, &mediabox.top));
+      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);
+    }
+
+    FPDFPage_SetMediaBox(page, 20, 30, 100, 150);
+
+    {
+      // Render the page after setting the MediaBox.
+      // Note that the change affects the rendering, as expected.
+      // It behaves just like the case below, rather than the case above.
+      FS_RECTF mediabox;
+      EXPECT_TRUE(FPDFPage_GetMediaBox(page, &mediabox.left, &mediabox.bottom,
+                                       &mediabox.right, &mediabox.top));
+      EXPECT_EQ(20, mediabox.left);
+      EXPECT_EQ(30, mediabox.bottom);
+      EXPECT_EQ(100, mediabox.right);
+      EXPECT_EQ(150, mediabox.top);
+      const int page_width = static_cast<int>(FPDF_GetPageWidth(page));
+      const int page_height = static_cast<int>(FPDF_GetPageHeight(page));
+      EXPECT_EQ(80, page_width);
+      EXPECT_EQ(120, page_height);
+      ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
+      CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+    }
+
+    UnloadPage(page);
+  }
+
+  {
+    // Save a copy, open the copy, and render it.
+    // Note that it renders the rotation.
+    EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+    OpenSavedDocument(nullptr);
+    FPDF_PAGE saved_page = LoadSavedPage(0);
+    ASSERT_TRUE(saved_page);
+
+    FS_RECTF mediabox;
+    EXPECT_TRUE(FPDFPage_GetMediaBox(saved_page, &mediabox.left,
+                                     &mediabox.bottom, &mediabox.right,
+                                     &mediabox.top));
+    EXPECT_EQ(20, mediabox.left);
+    EXPECT_EQ(30, mediabox.bottom);
+    EXPECT_EQ(100, mediabox.right);
+    EXPECT_EQ(150, mediabox.top);
+    const int page_width = static_cast<int>(FPDF_GetPageWidth(saved_page));
+    const int page_height = static_cast<int>(FPDF_GetPageHeight(saved_page));
+    EXPECT_EQ(80, page_width);
+    EXPECT_EQ(120, page_height);
+    ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+    CompareBitmap(bitmap.get(), page_width, page_height, kShrunkMD5);
+
+    CloseSavedPage(saved_page);
+    CloseSavedDocument();
+  }
+}
+
 TEST_F(FPDFTransformEmbedderTest, ClipPath) {
   ASSERT_TRUE(OpenDocument("hello_world.pdf"));