Add experimental FPDFPageObj_SetIsActive() API
Introduce an experimental API to allow setting whether a page object is
active. For documents being edited, this allows page objects to remain
in memory yet be excluded during content generation and rendering.
Bug: 377660088
Change-Id: I473a790c79df906a32bae7270780c63d3054c3ef
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/125630
Reviewed-by: Lei Zhang <thestig@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 a11a222..1639b5e 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -136,6 +136,7 @@
CPDF_PageContentGenerator::CPDF_PageContentGenerator(
CPDF_PageObjectHolder* pObjHolder)
: m_pObjHolder(pObjHolder), m_pDocument(pObjHolder->GetDocument()) {
+ // Copy all page objects, even if they are inactive.
for (const auto& pObj : *pObjHolder) {
m_pageObjects.emplace_back(pObj.get());
}
@@ -161,6 +162,9 @@
// Figure out which streams are dirty.
std::set<int32_t> all_dirty_streams;
for (auto& pPageObj : m_pageObjects) {
+ // Must include dirty page objects even if they are marked as inactive.
+ // Otherwise an inactive object will not be detected that its stream needs
+ // to be removed as part of regeneration.
if (pPageObj->IsDirty())
all_dirty_streams.insert(pPageObj->GetContentStream());
}
@@ -195,6 +199,10 @@
// Process the page objects, write into each dirty stream.
for (auto& pPageObj : m_pageObjects) {
+ if (!pPageObj->IsActive()) {
+ continue;
+ }
+
int stream_index = pPageObj->GetContentStream();
auto it = streams.find(stream_index);
if (it == streams.end())
@@ -301,6 +309,9 @@
ResourcesMap seen_resources;
for (auto& page_object : m_pageObjects) {
+ if (!page_object->IsActive()) {
+ continue;
+ }
RecordPageObjectResourceUsage(page_object, seen_resources);
}
if (!m_DefaultGraphicsName.IsEmpty()) {
@@ -344,8 +355,10 @@
const CPDF_ContentMarks* content_marks = empty_content_marks.get();
for (auto& pPageObj : m_pageObjects) {
- if (m_pObjHolder->IsPage() && !pPageObj->IsDirty())
+ if (m_pObjHolder->IsPage() &&
+ (!pPageObj->IsDirty() || !pPageObj->IsActive())) {
continue;
+ }
bDirty = true;
content_marks = ProcessContentMarks(buf, pPageObj, content_marks);
@@ -358,8 +371,13 @@
void CPDF_PageContentGenerator::UpdateStreamlessPageObjects(
int new_content_stream_index) {
for (auto& pPageObj : m_pageObjects) {
- if (pPageObj->GetContentStream() == CPDF_PageObject::kNoContentStream)
+ if (!pPageObj->IsActive()) {
+ continue;
+ }
+
+ if (pPageObj->GetContentStream() == CPDF_PageObject::kNoContentStream) {
pPageObj->SetContentStream(new_content_stream_index);
+ }
}
}
diff --git a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
index 790bf13..676469e 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentmanager.cpp
@@ -176,6 +176,9 @@
// Since CPDF_PageContentManager is only instantiated in
// CPDF_PageContentGenerator::GenerateContent(), which cleans up the dirty
// streams first, this should always be true.
+ // This method does not bother to inspect IsActive() for page objects; it will
+ // remove any object that has been scheduled for removal, regardless of active
+ // status.
DCHECK(!page_obj_holder_->HasDirtyStreams());
if (streams_to_remove_.empty()) {
diff --git a/core/fpdfapi/page/cpdf_contentparser.cpp b/core/fpdfapi/page/cpdf_contentparser.cpp
index 20feada..60aec84 100644
--- a/core/fpdfapi/page/cpdf_contentparser.cpp
+++ b/core/fpdfapi/page/cpdf_contentparser.cpp
@@ -229,6 +229,9 @@
}
for (auto& pObj : *m_pPageObjectHolder) {
+ if (!pObj->IsActive()) {
+ continue;
+ }
CPDF_ClipPath& clip_path = pObj->mutable_clip_path();
if (!clip_path.HasRef()) {
continue;
diff --git a/core/fpdfapi/page/cpdf_form.cpp b/core/fpdfapi/page/cpdf_form.cpp
index cbe92fd..526c148 100644
--- a/core/fpdfapi/page/cpdf_form.cpp
+++ b/core/fpdfapi/page/cpdf_form.cpp
@@ -90,18 +90,22 @@
}
bool CPDF_Form::HasPageObjects() const {
- return GetPageObjectCount() != 0;
+ return GetActivePageObjectCount() != 0;
}
CFX_FloatRect CPDF_Form::CalcBoundingBox() const {
- if (GetPageObjectCount() == 0)
+ if (GetActivePageObjectCount() == 0) {
return CFX_FloatRect();
+ }
float left = 1000000.0f;
float right = -1000000.0f;
float bottom = 1000000.0f;
float top = -1000000.0f;
for (const auto& pObj : *this) {
+ if (!pObj->IsActive()) {
+ continue;
+ }
const auto& rect = pObj->GetRect();
left = std::min(left, rect.left);
right = std::max(right, rect.right);
@@ -117,12 +121,16 @@
std::optional<std::pair<RetainPtr<CFX_DIBitmap>, CFX_Matrix>>
CPDF_Form::GetBitmapAndMatrixFromSoleImageOfForm() const {
- if (GetPageObjectCount() != 1)
+ // TODO(crbug.com/377660088): Determine if there is a case where only a single
+ // active object but other inactive objects is problematic for this method.
+ if (GetActivePageObjectCount() != 1) {
return std::nullopt;
+ }
CPDF_ImageObject* pImageObject = (*begin())->AsImage();
- if (!pImageObject)
+ if (!pImageObject) {
return std::nullopt;
+ }
return {{pImageObject->GetIndependentBitmap(), pImageObject->matrix()}};
}
diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp
index 16d1342..744aea6 100644
--- a/core/fpdfapi/page/cpdf_pageobject.cpp
+++ b/core/fpdfapi/page/cpdf_pageobject.cpp
@@ -90,6 +90,13 @@
m_bDirty = true;
}
+void CPDF_PageObject::SetIsActive(bool value) {
+ if (m_bIsActive != value) {
+ m_bIsActive = value;
+ m_bDirty = true;
+ }
+}
+
void CPDF_PageObject::TransformClipPath(const CFX_Matrix& matrix) {
CPDF_ClipPath& clip_path = mutable_clip_path();
if (!clip_path.HasRef()) {
diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h
index 4821321..000dd29 100644
--- a/core/fpdfapi/page/cpdf_pageobject.h
+++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -62,6 +62,8 @@
void SetDirty(bool value) { m_bDirty = value; }
bool IsDirty() const { return m_bDirty; }
+ void SetIsActive(bool value);
+ bool IsActive() const { return m_bIsActive; }
void TransformClipPath(const CFX_Matrix& matrix);
void SetOriginalRect(const CFX_FloatRect& rect) { m_OriginalRect = rect; }
@@ -141,7 +143,12 @@
CFX_FloatRect m_Rect;
CFX_FloatRect m_OriginalRect;
CPDF_ContentMarks m_ContentMarks;
+ // Modifying `m_bIsActive` automatically set `m_bDirty` to be true, but
+ // otherwise `m_bDirty` and `m_bIsActive` are independent. A
+ // `CPDF_PageObject` can remain dirty until page object processing completes
+ // and marks it no longer dirty.
bool m_bDirty = false;
+ bool m_bIsActive = true;
int32_t m_ContentStream;
// The resource name for this object.
ByteString m_ResourceName;
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
index 173a9e0..fd5b231 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
@@ -156,6 +156,16 @@
m_Transparency.SetIsolated();
}
+size_t CPDF_PageObjectHolder::GetActivePageObjectCount() const {
+ size_t count = 0;
+ for (const auto& page_object : m_PageObjectList) {
+ if (page_object->IsActive()) {
+ ++count;
+ }
+ }
+ return count;
+}
+
CPDF_PageObject* CPDF_PageObjectHolder::GetPageObjectByIndex(
size_t index) const {
return fxcrt::IndexInBounds(m_PageObjectList, index)
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h
index 5a9b697..44cbbd2 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.h
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.h
@@ -87,6 +87,7 @@
return m_pPageResources;
}
size_t GetPageObjectCount() const { return m_PageObjectList.size(); }
+ size_t GetActivePageObjectCount() const;
CPDF_PageObject* GetPageObjectByIndex(size_t index) const;
void AppendPageObject(std::unique_ptr<CPDF_PageObject> pPageObj);
diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.cpp b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
index 3dfd7a1..71676da 100644
--- a/core/fpdfapi/render/cpdf_progressiverenderer.cpp
+++ b/core/fpdfapi/render/cpdf_progressiverenderer.cpp
@@ -76,7 +76,7 @@
bool is_mask = false;
while (iter != iterEnd) {
CPDF_PageObject* pCurObj = iter->get();
- if (pCurObj->GetRect().left <= m_ClipRect.right &&
+ if (pCurObj->IsActive() && pCurObj->GetRect().left <= m_ClipRect.right &&
pCurObj->GetRect().right >= m_ClipRect.left &&
pCurObj->GetRect().bottom <= m_ClipRect.top &&
pCurObj->GetRect().top >= m_ClipRect.bottom) {
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index de97aa8..de4b2c1 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -215,8 +215,9 @@
m_bStopped = true;
return;
}
- if (!pCurObj)
+ if (!pCurObj || !pCurObj->IsActive()) {
continue;
+ }
if (pCurObj->GetRect().left > clip_rect.right ||
pCurObj->GetRect().right < clip_rect.left ||
diff --git a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
index 0d6b92c..c80ca6a 100644
--- a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
+++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
@@ -10,6 +10,7 @@
#include "core/fxcrt/check.h"
#include "core/fxge/cfx_defaultrenderdevice.h"
#include "core/fxge/dib/fx_dib.h"
+#include "public/cpp/fpdf_scopers.h"
#include "public/fpdf_progressive.h"
#include "testing/embedder_test.h"
#include "testing/embedder_test_constants.h"
@@ -379,6 +380,51 @@
rectangles_checksum);
}
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderPathObjectUsability) {
+ // Test rendering of paths with one of the page objects active vs. inactive.
+ const char* all_rectangles_used_checksum = []() {
+ if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
+ return "b4e411a6b5ffa59a50efede2efece597";
+ }
+ return "0a90de37f52127619c3dfb642b5fa2fe";
+ }();
+ const char* one_rectangle_inactive_checksum = []() {
+ if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
+ return "cf5bb4e61609162c03f4c8a6d9791230";
+ }
+ return "0481e8936b35ac9484b51a0966ab4ab6";
+ }();
+
+ ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+ ScopedEmbedderTestPage page = LoadScopedPage(0);
+ ASSERT_TRUE(page);
+
+ // Check rendering result before modifications.
+ {
+ ScopedFPDFBitmap bitmap = RenderPage(page.get());
+ CompareBitmap(bitmap.get(), 200, 300, all_rectangles_used_checksum);
+ }
+
+ ASSERT_EQ(FPDFPage_CountObjects(page.get()), 8);
+ FPDF_PAGEOBJECT page_obj = FPDFPage_GetObject(page.get(), 4);
+ ASSERT_TRUE(page_obj);
+
+ // Check rendering result after a page object is made inactive.
+ // Contents do not need to be regenerated to observe an effect.
+ ASSERT_TRUE(FPDFPageObj_SetIsActive(page_obj, /*active=*/false));
+ {
+ ScopedFPDFBitmap bitmap = RenderPage(page.get());
+ CompareBitmap(bitmap.get(), 200, 300, one_rectangle_inactive_checksum);
+ }
+
+ // Check rendering result after the same page object is active again.
+ ASSERT_TRUE(FPDFPageObj_SetIsActive(page_obj, /*active=*/true));
+ {
+ ScopedFPDFBitmap bitmap = RenderPage(page.get());
+ CompareBitmap(bitmap.get(), 200, 300, all_rectangles_used_checksum);
+ }
+}
+
TEST_F(FPDFProgressiveRenderEmbedderTest, RenderHighlightWithColorScheme) {
// Test rendering of highlight with forced color scheme on.
//
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index 7edaceb..b3159c2 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -605,8 +605,6 @@
CPDF_TextPage::TextOrientation CPDF_TextPage::FindTextlineFlowOrientation()
const {
- DCHECK_NE(m_pPage->GetPageObjectCount(), 0u);
-
const int32_t nPageWidth = static_cast<int32_t>(m_pPage->GetPageWidth());
const int32_t nPageHeight = static_cast<int32_t>(m_pPage->GetPageHeight());
if (nPageWidth <= 0 || nPageHeight <= 0)
@@ -620,8 +618,9 @@
int32_t nStartV = nPageHeight;
int32_t nEndV = 0;
for (const auto& pPageObj : *m_pPage) {
- if (!pPageObj->IsText())
+ if (!pPageObj->IsActive() || !pPageObj->IsText()) {
continue;
+ }
int32_t minH = static_cast<int32_t>(
std::clamp<float>(pPageObj->GetRect().left, 0.0f, nPageWidth));
@@ -683,12 +682,17 @@
}
void CPDF_TextPage::ProcessObject() {
- if (m_pPage->GetPageObjectCount() == 0)
+ if (m_pPage->GetActivePageObjectCount() == 0) {
return;
+ }
m_TextlineDir = FindTextlineFlowOrientation();
for (auto it = m_pPage->begin(); it != m_pPage->end(); ++it) {
CPDF_PageObject* pObj = it->get();
+ if (!pObj->IsActive()) {
+ continue;
+ }
+
if (pObj->IsText()) {
ProcessTextObject(pObj->AsText(), CFX_Matrix(), m_pPage, it);
} else if (pObj->IsForm()) {
@@ -708,6 +712,10 @@
const CPDF_PageObjectHolder* pHolder = pFormObj->form();
for (auto it = pHolder->begin(); it != pHolder->end(); ++it) {
CPDF_PageObject* pPageObj = it->get();
+ if (!pPageObj->IsActive()) {
+ continue;
+ }
+
if (pPageObj->IsText()) {
ProcessTextObject(pPageObj->AsText(), curFormMatrix, pHolder, it);
} else if (pPageObj->IsForm()) {
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index d41b12a..9dab7f1 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -630,6 +630,17 @@
: FPDF_PAGEOBJ_UNKNOWN;
}
+FPDF_EXPORT FPDF_BOOL FPDFPageObj_SetIsActive(FPDF_PAGEOBJECT page_object,
+ FPDF_BOOL active) {
+ CPDF_PageObject* cpage_object = CPDFPageObjectFromFPDFPageObject(page_object);
+ if (!cpage_object) {
+ return false;
+ }
+
+ cpage_object->SetIsActive(active);
+ return true;
+}
+
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GenerateContent(FPDF_PAGE page) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
diff --git a/fpdfsdk/fpdf_editpage_embeddertest.cpp b/fpdfsdk/fpdf_editpage_embeddertest.cpp
index a62cb71..21ee8aa 100644
--- a/fpdfsdk/fpdf_editpage_embeddertest.cpp
+++ b/fpdfsdk/fpdf_editpage_embeddertest.cpp
@@ -495,3 +495,72 @@
CloseSavedPage(page);
CloseSavedDocument();
}
+
+TEST_F(FPDFEditPageEmbedderTest, PageObjectIsActive) {
+ const char* one_rectangle_inactive_checksum = []() {
+ if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
+ return "cf5bb4e61609162c03f4c8a6d9791230";
+ }
+ return "0481e8936b35ac9484b51a0966ab4ab6";
+ }();
+
+ ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+ ScopedEmbedderTestPage page = LoadScopedPage(0);
+ ASSERT_TRUE(page);
+ const int page_width = static_cast<int>(FPDF_GetPageWidth(page.get()));
+ const int page_height = static_cast<int>(FPDF_GetPageHeight(page.get()));
+
+ // Note the original count of page objects for the rectangles.
+ EXPECT_EQ(8, FPDFPage_CountObjects(page.get()));
+
+ {
+ // Render the page as is.
+ ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
+ CompareBitmap(bitmap.get(), page_width, page_height,
+ pdfium::RectanglesChecksum());
+ }
+
+ {
+ // Save a copy, open the copy, and render it.
+ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+ ASSERT_TRUE(OpenSavedDocument());
+ FPDF_PAGE saved_page = LoadSavedPage(0);
+ ASSERT_TRUE(saved_page);
+
+ // Note that all page objects for the rectangles are present in the copy.
+ EXPECT_EQ(8, FPDFPage_CountObjects(saved_page));
+
+ ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+ CompareBitmap(bitmap.get(), page_width, page_height,
+ pdfium::RectanglesChecksum());
+
+ CloseSavedPage(saved_page);
+ CloseSavedDocument();
+ }
+
+ // Mark one of the page objects as inactive. It is still present in the page.
+ FPDF_PAGEOBJECT page_obj = FPDFPage_GetObject(page.get(), 4);
+ ASSERT_TRUE(page_obj);
+ ASSERT_TRUE(FPDFPageObj_SetIsActive(page_obj, /*active=*/false));
+ EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
+ EXPECT_EQ(8, FPDFPage_CountObjects(page.get()));
+
+ {
+ // Save a copy, open the copy, and render it.
+ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+ ASSERT_TRUE(OpenSavedDocument());
+ FPDF_PAGE saved_page = LoadSavedPage(0);
+ ASSERT_TRUE(saved_page);
+
+ // Note that a rectangle is absent from the copy.
+ EXPECT_EQ(7, FPDFPage_CountObjects(saved_page));
+
+ // The absence of the inactive page object affects the rendered result.
+ ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
+ CompareBitmap(bitmap.get(), page_width, page_height,
+ one_rectangle_inactive_checksum);
+
+ CloseSavedPage(saved_page);
+ CloseSavedDocument();
+ }
+}
diff --git a/fpdfsdk/fpdf_flatten.cpp b/fpdfsdk/fpdf_flatten.cpp
index 3547bf9..248c23a 100644
--- a/fpdfsdk/fpdf_flatten.cpp
+++ b/fpdfsdk/fpdf_flatten.cpp
@@ -61,6 +61,10 @@
pPDFPage->ParseContent();
for (const auto& pPageObject : *pPDFPage) {
+ if (!pPageObject->IsActive()) {
+ continue;
+ }
+
const CFX_FloatRect& rc = pPageObject->GetRect();
if (IsValidRect(rc, pDict->GetRectFor(pdfium::page_object::kMediaBox)))
pRectArray->push_back(rc);
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 27ad99a..1765faf 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -229,6 +229,7 @@
CHK(FPDFPageObj_SetDashArray);
CHK(FPDFPageObj_SetDashPhase);
CHK(FPDFPageObj_SetFillColor);
+ CHK(FPDFPageObj_SetIsActive);
CHK(FPDFPageObj_SetLineCap);
CHK(FPDFPageObj_SetLineJoin);
CHK(FPDFPageObj_SetMatrix);
diff --git a/fxjs/cjs_document.cpp b/fxjs/cjs_document.cpp
index b5b5c1c..bb81a66 100644
--- a/fxjs/cjs_document.cpp
+++ b/fxjs/cjs_document.cpp
@@ -1235,7 +1235,7 @@
int nWords = 0;
WideString swRet;
for (auto& pPageObj : *page) {
- if (pPageObj->IsText()) {
+ if (pPageObj->IsActive() && pPageObj->IsText()) {
CPDF_TextObject* pTextObj = pPageObj->AsText();
int nObjWords = pTextObj->CountWords();
if (nWords + nObjWords >= nWordNo) {
@@ -1290,8 +1290,9 @@
int nWords = 0;
for (auto& pPageObj : *page) {
- if (pPageObj->IsText())
+ if (pPageObj->IsActive() && pPageObj->IsText()) {
nWords += pPageObj->AsText()->CountWords();
+ }
}
return CJS_Result::Success(pRuntime->NewNumber(nWords));
}
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 6ab72cb..bcef484 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -260,6 +260,22 @@
// error.
FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object);
+// Experimental API.
+// Sets if |page_object| is active within page.
+//
+// page_object - handle to a page object.
+// active - a boolean specifying if the object is active.
+//
+// Returns TRUE on success.
+//
+// Page objects all start in the active state by default, and remain in that
+// state unless this function is called.
+//
+// When |active| is false, this makes the |page_object| be treated as if it
+// wasn't in the document even though it is still held internally.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_SetIsActive(FPDF_PAGEOBJECT page_object, FPDF_BOOL active);
+
// Transform |page_object| by the given matrix.
//
// page_object - handle to a page object.