Let FPDFPageObj_SetMatrix() reset dirty bit if restoring image matrix
With existing public image APIs, embedders may want to first call
FPDFPageObj_SetMatrix() to set an image to a desired size, render, and
call FPDFPageObj_SetMatrix() to restore the original matrix. This has
the side effect of marking the image object as being dirty, which can
exacerbate issues in CPDF_PageContentGenerator.
Avoid this by remembering a page object's initial matrix, and have a
second dirty bit to track if the matrix has changed, separate from
other modifications. Do this only for image objects for now.
Bug: 405433817, 406540676, 407950121
Change-Id: I0e66ef4e590cd7bcc79c21f31eae2e02724c422f
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/130390
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_imageobject.cpp b/core/fpdfapi/page/cpdf_imageobject.cpp
index 8078bd9..4781fb3 100644
--- a/core/fpdfapi/page/cpdf_imageobject.cpp
+++ b/core/fpdfapi/page/cpdf_imageobject.cpp
@@ -73,6 +73,11 @@
return pSource ? pSource->Realize() : nullptr;
}
+void CPDF_ImageObject::SetInitialImageMatrix(const CFX_Matrix& matrix) {
+ InitializeOriginalMatrix(matrix);
+ SetImageMatrix(matrix);
+}
+
void CPDF_ImageObject::SetImageMatrix(const CFX_Matrix& matrix) {
m_Matrix = matrix;
CalcBoundingBox();
diff --git a/core/fpdfapi/page/cpdf_imageobject.h b/core/fpdfapi/page/cpdf_imageobject.h
index 06e5a64..bb8e40e 100644
--- a/core/fpdfapi/page/cpdf_imageobject.h
+++ b/core/fpdfapi/page/cpdf_imageobject.h
@@ -32,6 +32,8 @@
RetainPtr<CPDF_Image> GetImage() const;
RetainPtr<CFX_DIBitmap> GetIndependentBitmap() const;
+ void SetInitialImageMatrix(const CFX_Matrix& matrix);
+
void SetImageMatrix(const CFX_Matrix& matrix);
const CFX_Matrix& matrix() const { return m_Matrix; }
diff --git a/core/fpdfapi/page/cpdf_pageobject.cpp b/core/fpdfapi/page/cpdf_pageobject.cpp
index 744aea6..dbe19c7 100644
--- a/core/fpdfapi/page/cpdf_pageobject.cpp
+++ b/core/fpdfapi/page/cpdf_pageobject.cpp
@@ -90,6 +90,10 @@
m_bDirty = true;
}
+void CPDF_PageObject::InitializeOriginalMatrix(const CFX_Matrix& matrix) {
+ m_OriginalMatrix = matrix;
+}
+
void CPDF_PageObject::SetIsActive(bool value) {
if (m_bIsActive != value) {
m_bIsActive = value;
diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h
index 000dd29..f000aff 100644
--- a/core/fpdfapi/page/cpdf_pageobject.h
+++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -61,7 +61,8 @@
virtual const CPDF_FormObject* AsForm() const;
void SetDirty(bool value) { m_bDirty = value; }
- bool IsDirty() const { return m_bDirty; }
+ bool IsDirty() const { return m_bDirty || m_bMatrixDirty; }
+ void SetMatrixDirty(bool value) { m_bMatrixDirty = value; }
void SetIsActive(bool value);
bool IsActive() const { return m_bIsActive; }
void TransformClipPath(const CFX_Matrix& matrix);
@@ -135,19 +136,28 @@
void SetDefaultStates();
+ const CFX_Matrix& original_matrix() const { return m_OriginalMatrix; }
+
protected:
void CopyData(const CPDF_PageObject* pSrcObject);
+ void InitializeOriginalMatrix(const CFX_Matrix& matrix);
private:
CPDF_GraphicStates m_GraphicStates;
CFX_FloatRect m_Rect;
CFX_FloatRect m_OriginalRect;
+ // Only used with `CPDF_ImageObject` for now.
+ // TODO(thestig): Use with `CPDF_FormObject` and `CPDF_PageObject` as well.
+ CFX_Matrix m_OriginalMatrix;
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;
+ // Separately track if the current matrix is different from
+ // `m_OriginalMatrix`.
+ bool m_bMatrixDirty = false;
bool m_bIsActive = true;
int32_t m_ContentStream;
// The resource name for this object.
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index 340f0d7..62eea03 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -839,9 +839,8 @@
SetGraphicStates(pImageObj.get(), pImageObj->GetImage()->IsMask(), false,
false);
- CFX_Matrix ImageMatrix =
- m_pCurStates->current_transformation_matrix() * m_mtContentToUser;
- pImageObj->SetImageMatrix(ImageMatrix);
+ pImageObj->SetInitialImageMatrix(
+ m_pCurStates->current_transformation_matrix() * m_mtContentToUser);
CPDF_ImageObject* pRet = pImageObj.get();
m_pObjectHolder->AppendPageObject(std::move(pImageObj));
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 223f899..68f3af5 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -36,6 +36,7 @@
#include "core/fxcrt/compiler_specific.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/fx_memcpy_wrappers.h"
+#include "core/fxcrt/notreached.h"
#include "core/fxcrt/numerics/safe_conversions.h"
#include "core/fxcrt/span.h"
#include "core/fxcrt/span_util.h"
@@ -722,28 +723,32 @@
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetMatrix(FPDF_PAGEOBJECT page_object, const FS_MATRIX* matrix) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
- if (!pPageObj || !matrix)
+ if (!pPageObj || !matrix) {
return false;
+ }
CFX_Matrix cmatrix = CFXMatrixFromFSMatrix(*matrix);
switch (pPageObj->GetType()) {
case CPDF_PageObject::Type::kText:
pPageObj->AsText()->SetTextMatrix(cmatrix);
- break;
+ pPageObj->SetMatrixDirty(true);
+ return true;
case CPDF_PageObject::Type::kPath:
pPageObj->AsPath()->SetPathMatrix(cmatrix);
- break;
+ pPageObj->SetMatrixDirty(true);
+ return true;
case CPDF_PageObject::Type::kImage:
pPageObj->AsImage()->SetImageMatrix(cmatrix);
- break;
+ pPageObj->SetMatrixDirty(pPageObj->original_matrix() != cmatrix);
+ return true;
case CPDF_PageObject::Type::kShading:
return false;
case CPDF_PageObject::Type::kForm:
pPageObj->AsForm()->SetFormMatrix(cmatrix);
- break;
+ pPageObj->SetMatrixDirty(true);
+ return true;
}
- pPageObj->SetDirty(true);
- return true;
+ NOTREACHED();
}
FPDF_EXPORT void FPDF_CALLCONV