Improve shared resources dict detection in CPDF_PageContentGenerator

The existing code in CPDF_PageContentGenerator to detect shared page
resources dictionary can miss some cases, like when the resource
dictionary is inlined into the /Pages (not /Page) dictionary. Improve
the code that detects shared resources to look for this case and other
possibly scenarios by checking if the current page's page dictionary and
resources dictionary are in use twice in the same document.

With the additional code, refactor the related code into
IsPageResourceShared().

Bug: 407730667
Change-Id: Ie35100c0fdf7693171f91ec00df094adc36bdce3
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/130412
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 1137403..95d402b 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -218,6 +218,58 @@
   }
 }
 
+bool IsPageResourceShared(CPDF_Document* doc,
+                          RetainPtr<const CPDF_Dictionary> page_dict,
+                          RetainPtr<const CPDF_Dictionary> resources_dict) {
+  CHECK(doc);
+  CHECK(page_dict);
+  CHECK(resources_dict);
+
+  const uint32_t resources_object_number = resources_dict->GetObjNum();
+  if (resources_object_number) {
+    // If `resources_dict` is not an inline object, then check to see if is a
+    // shared object.
+    if (pdfium::Contains(GetObjectsWithMultipleReferences(doc),
+                         resources_object_number)) {
+      return true;
+    }
+  }
+
+  // The check above may not catch all cases. e.g. inline objects. Check all
+  // pages in the document to see if another page is using the same resources.
+  int page_dict_seen = 0;
+  int resources_dict_seen = 0;
+  for (int i = 0; i < doc->GetPageCount(); ++i) {
+    RetainPtr<CPDF_Dictionary> current_page_dict =
+        doc->GetMutablePageDictionary(i);
+    if (!current_page_dict) {
+      continue;
+    }
+
+    // Check to see if the current page's page dictionary is seen twice: Once
+    // for this page, and once for another. If the same page dictionary is in
+    // use twice, then the resource dictionary within must also be shared.
+    if (current_page_dict == page_dict) {
+      ++page_dict_seen;
+      if (page_dict_seen == 2) {
+        return true;
+      }
+    }
+
+    // Similar check as above, for the current page's resource dictionary.
+    auto page =
+        pdfium::MakeRetain<CPDF_Page>(doc, std::move(current_page_dict));
+    if (resources_dict == page->GetMutableResources()) {
+      ++resources_dict_seen;
+      if (resources_dict_seen == 2) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 }  // namespace
 
 CPDF_PageContentGenerator::CPDF_PageContentGenerator(
@@ -384,19 +436,14 @@
     return;
   }
 
-  const uint32_t resources_object_number = resources->GetObjNum();
-  if (resources_object_number) {
-    // If `resources` is not an inline object, then do not modify it directly if
-    // it has multiple references.
-    if (pdfium::Contains(GetObjectsWithMultipleReferences(m_pDocument),
-                         resources_object_number)) {
-      resources = pdfium::WrapRetain(resources->Clone()->AsMutableDictionary());
-      const uint32_t clone_object_number =
-          m_pDocument->AddIndirectObject(resources);
-      m_pObjHolder->SetResources(resources);
-      m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
-          pdfium::page_object::kResources, m_pDocument, clone_object_number);
-    }
+  // Do not modify shared resource dictionaries. Give this page its own copy.
+  if (IsPageResourceShared(m_pDocument, m_pObjHolder->GetDict(), resources)) {
+    resources = pdfium::WrapRetain(resources->Clone()->AsMutableDictionary());
+    const uint32_t clone_object_number =
+        m_pDocument->AddIndirectObject(resources);
+    m_pObjHolder->SetResources(resources);
+    m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
+        pdfium::page_object::kResources, m_pDocument, clone_object_number);
   }
 
   ResourcesMap seen_resources;