M135: 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, 409054978
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>
(cherry picked from commit ed3b0b84d7d7db97636a3a92a822a8c78f55992f)
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/130511
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index 0590b6a..43615ae 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -213,6 +213,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(
@@ -379,19 +431,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;