Keep track of ExtGState resources For all CPDF_PageObjects types, keep track of the ExtGState resources associated with them. Using this data, CPDF_PageContentGenerator can detect and remove unused resources from resources dictionaries. Then CPDF_Creator can detect the unreferenced resources and remove them when saving. Bug: chromium:1428724,pdfium:1409 Change-Id: I786a515b4ddcfa9ea2dccb94d3d7ad6a189ec7ce Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/105614 Reviewed-by: Nigi <nigi@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 c149194..0859757 100644 --- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -79,12 +79,21 @@ break; } } + const ByteString& graphics_resource_name = + page_object->GetGraphicsResourceName(); + if (!graphics_resource_name.IsEmpty()) { + seen_resources["ExtGState"].insert(graphics_resource_name); + } } void RemoveUnusedResources(RetainPtr<CPDF_Dictionary> resources_dict, const ResourcesMap& resources_in_use) { - // TODO(thestig): Remove other unused resource types, like ExtGState. - static constexpr const char* kResourceKeys[] = {"Font", "XObject"}; + // TODO(thestig): Remove other unused resource types: + // - ColorSpace + // - Pattern + // - Shading + static constexpr const char* kResourceKeys[] = {"ExtGState", "Font", + "XObject"}; for (const char* resource_key : kResourceKeys) { RetainPtr<CPDF_Dictionary> resource_dict = resources_dict->GetMutableDictFor(resource_key); @@ -203,7 +212,7 @@ return; // Make sure default graphics are created. - GetOrCreateDefaultGraphics(); + m_DefaultGraphicsName = GetOrCreateDefaultGraphics(); CPDF_PageContentManager page_content_manager(m_pObjHolder, m_pDocument); for (auto& pair : new_stream_data) { @@ -229,6 +238,9 @@ for (auto& page_object : m_pageObjects) { RecordPageObjectResourceUsage(page_object, seen_resources); } + if (!m_DefaultGraphicsName.IsEmpty()) { + seen_resources["ExtGState"].insert(m_DefaultGraphicsName); + } RemoveUnusedResources(std::move(resources), seen_resources); } @@ -562,6 +574,7 @@ } m_pDocument->AddIndirectObject(gsDict); name = RealizeResource(std::move(gsDict), "ExtGState"); + pPageObj->SetGraphicsResourceName(name); m_pObjHolder->GraphicsMapInsert(graphD, name); } *buf << "/" << PDF_NameEncode(name) << " gs "; @@ -572,8 +585,8 @@ *buf << "0 0 0 RG 0 0 0 rg 1 w " << static_cast<int>(CFX_GraphStateData::LineCap::kButt) << " J " << static_cast<int>(CFX_GraphStateData::LineJoin::kMiter) << " j\n"; - ByteString name = GetOrCreateDefaultGraphics(); - *buf << "/" << PDF_NameEncode(name) << " gs "; + m_DefaultGraphicsName = GetOrCreateDefaultGraphics(); + *buf << "/" << PDF_NameEncode(m_DefaultGraphicsName) << " gs "; } ByteString CPDF_PageContentGenerator::GetOrCreateDefaultGraphics() const {
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h index 45894f3..6d0f9ef 100644 --- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
@@ -71,6 +71,7 @@ UnownedPtr<CPDF_PageObjectHolder> const m_pObjHolder; UnownedPtr<CPDF_Document> const m_pDocument; std::vector<UnownedPtr<CPDF_PageObject>> m_pageObjects; + ByteString m_DefaultGraphicsName; }; #endif // CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
diff --git a/core/fpdfapi/page/cpdf_allstates.cpp b/core/fpdfapi/page/cpdf_allstates.cpp index 07d4c4f..a4330db 100644 --- a/core/fpdfapi/page/cpdf_allstates.cpp +++ b/core/fpdfapi/page/cpdf_allstates.cpp
@@ -16,6 +16,7 @@ #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" +#include "core/fxcrt/bytestring.h" #include "core/fxge/cfx_graphstatedata.h" #include "third_party/base/cxx17_backports.h" @@ -25,6 +26,7 @@ void CPDF_AllStates::Copy(const CPDF_AllStates& src) { CopyStates(src); + m_GraphicsResourceName = src.m_GraphicsResourceName; m_TextMatrix = src.m_TextMatrix; m_ParentMatrix = src.m_ParentMatrix; m_CTM = src.m_CTM;
diff --git a/core/fpdfapi/page/cpdf_allstates.h b/core/fpdfapi/page/cpdf_allstates.h index 0ae2802..63eb527 100644 --- a/core/fpdfapi/page/cpdf_allstates.h +++ b/core/fpdfapi/page/cpdf_allstates.h
@@ -8,6 +8,7 @@ #define CORE_FPDFAPI_PAGE_CPDF_ALLSTATES_H_ #include "core/fpdfapi/page/cpdf_graphicstates.h" +#include "core/fxcrt/bytestring.h" #include "core/fxcrt/fx_coordinates.h" class CPDF_Array; @@ -24,6 +25,7 @@ CPDF_StreamContentParser* pParser); void SetLineDash(const CPDF_Array* pArray, float phase, float scale); + ByteString m_GraphicsResourceName; CFX_Matrix m_TextMatrix; CFX_Matrix m_CTM; CFX_Matrix m_ParentMatrix;
diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h index 3a0b87a..7d9d015 100644 --- a/core/fpdfapi/page/cpdf_pageobject.h +++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -94,6 +94,13 @@ m_ResourceName = resource_name; } + const ByteString& GetGraphicsResourceName() const { + return m_GraphicsResourceName; + } + void SetGraphicsResourceName(const ByteString& resource_name) { + m_GraphicsResourceName = resource_name; + } + protected: void CopyData(const CPDF_PageObject* pSrcObject); @@ -103,7 +110,8 @@ CPDF_ContentMarks m_ContentMarks; bool m_bDirty = false; int32_t m_ContentStream; - ByteString m_ResourceName; // The resource name for this object. + ByteString m_ResourceName; // The resource name for this object. + ByteString m_GraphicsResourceName; // Like `m_ResourceName` but for graphics. }; #endif // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp index 30c5b90..0522cf1 100644 --- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp +++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -35,6 +35,7 @@ #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" #include "core/fxcrt/autonuller.h" +#include "core/fxcrt/bytestring.h" #include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/scoped_set_insertion.h" #include "core/fxcrt/stl_util.h" @@ -425,6 +426,7 @@ if (bText) { pObj->m_TextState = m_pCurStates->m_TextState; } + pObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName); } // static @@ -776,6 +778,7 @@ auto pFormObj = std::make_unique<CPDF_FormObject>(GetCurrentStreamIndex(), std::move(form), matrix); pFormObj->SetResourceName(name); + pFormObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName); if (!m_pObjectHolder->BackgroundAlphaNeeded() && pFormObj->form()->BackgroundAlphaNeeded()) { m_pObjectHolder->SetBackgroundAlphaNeeded(true); @@ -900,6 +903,7 @@ if (!pGS) return; + m_pCurStates->m_GraphicsResourceName = name; m_pCurStates->ProcessExtGS(pGS.Get(), this); }
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp index 21c3512..8a3bd14 100644 --- a/fpdfsdk/fpdf_edit_embeddertest.cpp +++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -3068,10 +3068,11 @@ "5384da3406d62360ffb5cac4476fff1c"); } - // Never mind, my new favorite color is blue, increase alpha + // Never mind, my new favorite color is blue, increase alpha. + // The red graphics state goes away. EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 0, 0, 255, 180)); EXPECT_TRUE(FPDFPage_GenerateContent(page)); - EXPECT_EQ(3u, graphics_dict->size()); + EXPECT_EQ(2u, graphics_dict->size()); // Check that bitmap displays changed content { @@ -3082,7 +3083,7 @@ // And now generate, without changes EXPECT_TRUE(FPDFPage_GenerateContent(page)); - EXPECT_EQ(3u, graphics_dict->size()); + EXPECT_EQ(2u, graphics_dict->size()); { ScopedFPDFBitmap page_bitmap = RenderPage(page); CompareBitmap(page_bitmap.get(), 612, 792, @@ -3105,7 +3106,7 @@ // Generate yet again, check dicts are reasonably sized EXPECT_TRUE(FPDFPage_GenerateContent(page)); - EXPECT_EQ(3u, graphics_dict->size()); + EXPECT_EQ(2u, graphics_dict->size()); EXPECT_EQ(1u, font_dict->size()); FPDF_ClosePage(page); }