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);
}