pdfium/fpdfsdk/fpdf_editpage.cpp
Lei Zhang 27499934d7 Properly account for CTM changes
In the existing code, CPDF_PageObjectHolder only stored the final value
of the current transformation matrix. This is insufficient information
for PDF editing and saving, where a page can have multiple content
streams and only some of them have been modified and get regenerated.
Even then, CPDF_PageContentGenerator mostly works, but the way it writes
out the page contents is based on the limitation of only having this
single CTM value.

To properly account for CTM changes from stream to stream, update
CPDF_StreamContentParser to store the CTM at the end of streams, when
handling "cm" and "Q" operators. CPDF_PageObjectHolder can then take
this data as `m_AllCTMs`, and stop storing `m_LastCTM`. With this
additional information, CPDF_PageContentGenerator can compensate for the
CTM when handling the modified streams. It can do so at the stream
level, and individual objects no longer need to take the CTM into
account, because each stream will start with the CTM set to the identity
matrix. At the end of each stream, the CTM will be restored to the value
expected by the next stream.

With the above changes, `CPDF_PageObject::m_bUsesCTM` is no longer
necessary, as objects no longer use the CTM. Remove it and associated
code. The FPDFEditPathEmbedderTest.AddPathToRectanglesWithLeakyCTM test
case also works as expected, so update the test expectation and remove
the TODO comment.

Bug: pdfium:2142
Change-Id: I95fab8dd5a06da19743f24f9e89007b4bb3c34c9
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/117356
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
2024-03-11 23:56:43 +00:00

1036 lines
33 KiB
C++

// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "public/fpdf_edit.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "constants/page_object.h"
#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
#include "core/fpdfapi/page/cpdf_colorspace.h"
#include "core/fpdfapi/page/cpdf_docpagedata.h"
#include "core/fpdfapi/page/cpdf_form.h"
#include "core/fpdfapi/page/cpdf_formobject.h"
#include "core/fpdfapi/page/cpdf_imageobject.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/page/cpdf_pageimagecache.h"
#include "core/fpdfapi/page/cpdf_pageobject.h"
#include "core/fpdfapi/page/cpdf_pathobject.h"
#include "core/fpdfapi/page/cpdf_shadingobject.h"
#include "core/fpdfapi/page/cpdf_textobject.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/render/cpdf_docrenderdata.h"
#include "core/fpdfdoc/cpdf_annot.h"
#include "core/fpdfdoc/cpdf_annotlist.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/fx_memcpy_wrappers.h"
#include "core/fxcrt/numerics/safe_conversions.h"
#include "core/fxcrt/span.h"
#include "core/fxcrt/stl_util.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "public/fpdf_formfill.h"
#ifdef PDF_ENABLE_XFA
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
#endif // PDF_ENABLE_XFA
namespace {
static_assert(FPDF_PAGEOBJ_TEXT ==
static_cast<int>(CPDF_PageObject::Type::kText),
"FPDF_PAGEOBJ_TEXT/CPDF_PageObject::TEXT mismatch");
static_assert(FPDF_PAGEOBJ_PATH ==
static_cast<int>(CPDF_PageObject::Type::kPath),
"FPDF_PAGEOBJ_PATH/CPDF_PageObject::PATH mismatch");
static_assert(FPDF_PAGEOBJ_IMAGE ==
static_cast<int>(CPDF_PageObject::Type::kImage),
"FPDF_PAGEOBJ_IMAGE/CPDF_PageObject::IMAGE mismatch");
static_assert(FPDF_PAGEOBJ_SHADING ==
static_cast<int>(CPDF_PageObject::Type::kShading),
"FPDF_PAGEOBJ_SHADING/CPDF_PageObject::SHADING mismatch");
static_assert(FPDF_PAGEOBJ_FORM ==
static_cast<int>(CPDF_PageObject::Type::kForm),
"FPDF_PAGEOBJ_FORM/CPDF_PageObject::FORM mismatch");
bool IsPageObject(CPDF_Page* pPage) {
if (!pPage)
return false;
RetainPtr<const CPDF_Dictionary> pFormDict = pPage->GetDict();
if (!pFormDict->KeyExist(pdfium::page_object::kType))
return false;
RetainPtr<const CPDF_Name> pName =
ToName(pFormDict->GetObjectFor(pdfium::page_object::kType)->GetDirect());
return pName && pName->GetString() == "Page";
}
void CalcBoundingBox(CPDF_PageObject* pPageObj) {
switch (pPageObj->GetType()) {
case CPDF_PageObject::Type::kText: {
break;
}
case CPDF_PageObject::Type::kPath: {
CPDF_PathObject* pPathObj = pPageObj->AsPath();
pPathObj->CalcBoundingBox();
break;
}
case CPDF_PageObject::Type::kImage: {
CPDF_ImageObject* pImageObj = pPageObj->AsImage();
pImageObj->CalcBoundingBox();
break;
}
case CPDF_PageObject::Type::kShading: {
CPDF_ShadingObject* pShadingObj = pPageObj->AsShading();
pShadingObj->CalcBoundingBox();
break;
}
case CPDF_PageObject::Type::kForm: {
CPDF_FormObject* pFormObj = pPageObj->AsForm();
pFormObj->CalcBoundingBox();
break;
}
}
}
RetainPtr<CPDF_Dictionary> GetMarkParamDict(FPDF_PAGEOBJECTMARK mark) {
CPDF_ContentMarkItem* pMarkItem =
CPDFContentMarkItemFromFPDFPageObjectMark(mark);
return pMarkItem ? pMarkItem->GetParam() : nullptr;
}
RetainPtr<CPDF_Dictionary> GetOrCreateMarkParamsDict(FPDF_DOCUMENT document,
FPDF_PAGEOBJECTMARK mark) {
CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
if (!pDoc)
return nullptr;
CPDF_ContentMarkItem* pMarkItem =
CPDFContentMarkItemFromFPDFPageObjectMark(mark);
if (!pMarkItem)
return nullptr;
RetainPtr<CPDF_Dictionary> pParams = pMarkItem->GetParam();
if (!pParams) {
pParams = pDoc->New<CPDF_Dictionary>();
pMarkItem->SetDirectDict(pParams);
}
return pParams;
}
bool PageObjectContainsMark(CPDF_PageObject* pPageObj,
FPDF_PAGEOBJECTMARK mark) {
const CPDF_ContentMarkItem* pMarkItem =
CPDFContentMarkItemFromFPDFPageObjectMark(mark);
return pMarkItem && pPageObj->GetContentMarks()->ContainsItem(pMarkItem);
}
CPDF_FormObject* CPDFFormObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
return pPageObj ? pPageObj->AsForm() : nullptr;
}
const CPDF_PageObjectHolder* CPDFPageObjHolderFromFPDFFormObject(
FPDF_PAGEOBJECT page_object) {
CPDF_FormObject* pFormObject = CPDFFormObjectFromFPDFPageObject(page_object);
return pFormObject ? pFormObject->form() : nullptr;
}
} // namespace
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() {
auto pDoc =
std::make_unique<CPDF_Document>(std::make_unique<CPDF_DocRenderData>(),
std::make_unique<CPDF_DocPageData>());
pDoc->CreateNewDoc();
time_t currentTime;
ByteString DateStr;
if (IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS)) {
if (FXSYS_time(&currentTime) != -1) {
tm* pTM = FXSYS_localtime(&currentTime);
if (pTM) {
DateStr = ByteString::Format(
"D:%04d%02d%02d%02d%02d%02d", pTM->tm_year + 1900, pTM->tm_mon + 1,
pTM->tm_mday, pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
}
}
}
RetainPtr<CPDF_Dictionary> pInfoDict = pDoc->GetInfo();
if (pInfoDict) {
if (IsPDFSandboxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
pInfoDict->SetNewFor<CPDF_String>("CreationDate", DateStr, false);
pInfoDict->SetNewFor<CPDF_String>("Creator", L"PDFium");
}
// Caller takes ownership of pDoc.
return FPDFDocumentFromCPDFDocument(pDoc.release());
}
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_Delete(FPDF_DOCUMENT document,
int page_index) {
auto* pDoc = CPDFDocumentFromFPDFDocument(document);
if (!pDoc)
return;
CPDF_Document::Extension* pExtension = pDoc->GetExtension();
if (pExtension) {
pExtension->DeletePage(page_index);
return;
}
pDoc->DeletePage(page_index);
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_MovePages(FPDF_DOCUMENT document,
const int* page_indices,
unsigned long page_indices_len,
int dest_page_index) {
auto* doc = CPDFDocumentFromFPDFDocument(document);
if (!doc) {
return false;
}
return doc->MovePages({page_indices, page_indices_len}, dest_page_index);
}
FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDFPage_New(FPDF_DOCUMENT document,
int page_index,
double width,
double height) {
CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
if (!pDoc)
return nullptr;
page_index = std::clamp(page_index, 0, pDoc->GetPageCount());
RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(page_index));
if (!pPageDict)
return nullptr;
pPageDict->SetRectFor(pdfium::page_object::kMediaBox,
CFX_FloatRect(0, 0, width, height));
pPageDict->SetNewFor<CPDF_Number>(pdfium::page_object::kRotate, 0);
pPageDict->SetNewFor<CPDF_Dictionary>(pdfium::page_object::kResources);
#ifdef PDF_ENABLE_XFA
if (pDoc->GetExtension()) {
auto pXFAPage = pdfium::MakeRetain<CPDFXFA_Page>(pDoc, page_index);
pXFAPage->LoadPDFPageFromDict(pPageDict);
return FPDFPageFromIPDFPage(pXFAPage.Leak()); // Caller takes ownership.
}
#endif // PDF_ENABLE_XFA
auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pPageDict);
pPage->AddPageImageCache();
pPage->ParseContent();
return FPDFPageFromIPDFPage(pPage.Leak()); // Caller takes ownership.
}
FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetRotation(FPDF_PAGE page) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
return IsPageObject(pPage) ? pPage->GetPageRotation() : -1;
}
FPDF_EXPORT void FPDF_CALLCONV
FPDFPage_InsertObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_object) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return;
std::unique_ptr<CPDF_PageObject> pPageObjHolder(pPageObj);
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
return;
pPageObj->SetDirty(true);
pPage->AppendPageObject(std::move(pPageObjHolder));
CalcBoundingBox(pPageObj);
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPage_RemoveObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_object) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
return false;
// Caller takes ownership.
return !!pPage->RemovePageObject(pPageObj).release();
}
FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
return -1;
return pdfium::checked_cast<int>(pPage->GetPageObjectCount());
}
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page,
int index) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
return nullptr;
return FPDFPageObjectFromCPDFPageObject(pPage->GetPageObjectByIndex(index));
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_HasTransparency(FPDF_PAGE page) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
return pPage && pPage->BackgroundAlphaNeeded();
}
FPDF_EXPORT void FPDF_CALLCONV
FPDFPageObj_Destroy(FPDF_PAGEOBJECT page_object) {
delete CPDFPageObjectFromFPDFPageObject(page_object);
}
FPDF_EXPORT int FPDF_CALLCONV
FPDFPageObj_CountMarks(FPDF_PAGEOBJECT page_object) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return -1;
return pdfium::checked_cast<int>(pPageObj->GetContentMarks()->CountItems());
}
FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
FPDFPageObj_GetMark(FPDF_PAGEOBJECT page_object, unsigned long index) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return nullptr;
CPDF_ContentMarks* pMarks = pPageObj->GetContentMarks();
if (index >= pMarks->CountItems())
return nullptr;
return FPDFPageObjectMarkFromCPDFContentMarkItem(pMarks->GetItem(index));
}
FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return nullptr;
CPDF_ContentMarks* pMarks = pPageObj->GetContentMarks();
pMarks->AddMark(name);
pPageObj->SetDirty(true);
const size_t index = pMarks->CountItems() - 1;
return FPDFPageObjectMarkFromCPDFContentMarkItem(pMarks->GetItem(index));
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_RemoveMark(FPDF_PAGEOBJECT page_object, FPDF_PAGEOBJECTMARK mark) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
CPDF_ContentMarkItem* pMarkItem =
CPDFContentMarkItemFromFPDFPageObjectMark(mark);
if (!pPageObj || !pMarkItem)
return false;
if (!pPageObj->GetContentMarks()->RemoveMark(pMarkItem))
return false;
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_GetName(FPDF_PAGEOBJECTMARK mark,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen) {
const CPDF_ContentMarkItem* pMarkItem =
CPDFContentMarkItemFromFPDFPageObjectMark(mark);
if (!pMarkItem || !out_buflen)
return false;
*out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
WideString::FromUTF8(pMarkItem->GetName().AsStringView()), buffer,
buflen);
return true;
}
FPDF_EXPORT int FPDF_CALLCONV
FPDFPageObjMark_CountParams(FPDF_PAGEOBJECTMARK mark) {
const CPDF_ContentMarkItem* pMarkItem =
CPDFContentMarkItemFromFPDFPageObjectMark(mark);
if (!pMarkItem)
return -1;
RetainPtr<const CPDF_Dictionary> pParams = pMarkItem->GetParam();
return pParams ? fxcrt::CollectionSize<int>(*pParams) : 0;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_GetParamKey(FPDF_PAGEOBJECTMARK mark,
unsigned long index,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen) {
if (!out_buflen)
return false;
RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
if (!pParams)
return false;
CPDF_DictionaryLocker locker(pParams);
for (auto& it : locker) {
if (index == 0) {
*out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
WideString::FromUTF8(it.first.AsStringView()), buffer, buflen);
return true;
}
--index;
}
return false;
}
FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
FPDFPageObjMark_GetParamValueType(FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key) {
RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
if (!pParams)
return FPDF_OBJECT_UNKNOWN;
RetainPtr<const CPDF_Object> pObject = pParams->GetObjectFor(key);
return pObject ? pObject->GetType() : FPDF_OBJECT_UNKNOWN;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_GetParamIntValue(FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key,
int* out_value) {
if (!out_value)
return false;
RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
if (!pParams)
return false;
RetainPtr<const CPDF_Object> pObj = pParams->GetObjectFor(key);
if (!pObj || !pObj->IsNumber())
return false;
*out_value = pObj->GetInteger();
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_GetParamStringValue(FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen) {
if (!out_buflen)
return false;
RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
if (!pParams)
return false;
RetainPtr<const CPDF_Object> pObj = pParams->GetObjectFor(key);
if (!pObj || !pObj->IsString())
return false;
*out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
WideString::FromUTF8(pObj->GetString().AsStringView()), buffer, buflen);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_GetParamBlobValue(FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key,
void* buffer,
unsigned long buflen,
unsigned long* out_buflen) {
if (!out_buflen)
return false;
RetainPtr<const CPDF_Dictionary> pParams = GetMarkParamDict(mark);
if (!pParams)
return false;
RetainPtr<const CPDF_Object> pObj = pParams->GetObjectFor(key);
if (!pObj || !pObj->IsString())
return false;
ByteString result = pObj->GetString();
const unsigned long len =
pdfium::checked_cast<unsigned long>(result.GetLength());
if (buffer && len <= buflen)
memcpy(buffer, result.c_str(), len);
*out_buflen = len;
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT page_object) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj) {
return false;
}
if (pPageObj->general_state().GetBlendType() != BlendMode::kNormal) {
return true;
}
if (pPageObj->general_state().GetSoftMask()) {
return true;
}
if (pPageObj->general_state().GetFillAlpha() != 1.0f) {
return true;
}
if (pPageObj->IsPath() &&
pPageObj->general_state().GetStrokeAlpha() != 1.0f) {
return true;
}
if (!pPageObj->IsForm()) {
return false;
}
const CPDF_Form* pForm = pPageObj->AsForm()->form();
if (!pForm) {
return false;
}
const CPDF_Transparency& trans = pForm->GetTransparency();
return trans.IsGroup() || trans.IsIsolated();
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_SetIntParam(FPDF_DOCUMENT document,
FPDF_PAGEOBJECT page_object,
FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key,
int value) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
return false;
RetainPtr<CPDF_Dictionary> pParams =
GetOrCreateMarkParamsDict(document, mark);
if (!pParams)
return false;
pParams->SetNewFor<CPDF_Number>(key, value);
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_SetStringParam(FPDF_DOCUMENT document,
FPDF_PAGEOBJECT page_object,
FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key,
FPDF_BYTESTRING value) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
return false;
RetainPtr<CPDF_Dictionary> pParams =
GetOrCreateMarkParamsDict(document, mark);
if (!pParams)
return false;
pParams->SetNewFor<CPDF_String>(key, value, false);
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_SetBlobParam(FPDF_DOCUMENT document,
FPDF_PAGEOBJECT page_object,
FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key,
void* value,
unsigned long value_len) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
return false;
RetainPtr<CPDF_Dictionary> pParams =
GetOrCreateMarkParamsDict(document, mark);
if (!pParams)
return false;
if (!value && value_len > 0)
return false;
pParams->SetNewFor<CPDF_String>(
key, ByteString(static_cast<const char*>(value), value_len), true);
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObjMark_RemoveParam(FPDF_PAGEOBJECT page_object,
FPDF_PAGEOBJECTMARK mark,
FPDF_BYTESTRING key) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
RetainPtr<CPDF_Dictionary> pParams = GetMarkParamDict(mark);
if (!pParams)
return false;
auto removed = pParams->RemoveFor(key);
if (!removed)
return false;
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT page_object) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
return pPageObj ? static_cast<int>(pPageObj->GetType())
: FPDF_PAGEOBJ_UNKNOWN;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GenerateContent(FPDF_PAGE page) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
return false;
CPDF_PageContentGenerator CG(pPage);
CG.GenerateContent();
return true;
}
FPDF_EXPORT void FPDF_CALLCONV
FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object,
double a,
double b,
double c,
double d,
double e,
double f) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return;
CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
pPageObj->Transform(matrix);
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetMatrix(FPDF_PAGEOBJECT page_object, FS_MATRIX* matrix) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !matrix)
return false;
switch (pPageObj->GetType()) {
case CPDF_PageObject::Type::kText:
*matrix = FSMatrixFromCFXMatrix(pPageObj->AsText()->GetTextMatrix());
return true;
case CPDF_PageObject::Type::kPath:
*matrix = FSMatrixFromCFXMatrix(pPageObj->AsPath()->matrix());
return true;
case CPDF_PageObject::Type::kImage:
*matrix = FSMatrixFromCFXMatrix(pPageObj->AsImage()->matrix());
return true;
case CPDF_PageObject::Type::kShading:
return false;
case CPDF_PageObject::Type::kForm:
*matrix = FSMatrixFromCFXMatrix(pPageObj->AsForm()->form_matrix());
return true;
}
}
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)
return false;
CFX_Matrix cmatrix = CFXMatrixFromFSMatrix(*matrix);
switch (pPageObj->GetType()) {
case CPDF_PageObject::Type::kText:
pPageObj->AsText()->SetTextMatrix(cmatrix);
break;
case CPDF_PageObject::Type::kPath:
pPageObj->AsPath()->SetPathMatrix(cmatrix);
break;
case CPDF_PageObject::Type::kImage:
pPageObj->AsImage()->SetImageMatrix(cmatrix);
break;
case CPDF_PageObject::Type::kShading:
return false;
case CPDF_PageObject::Type::kForm:
pPageObj->AsForm()->SetFormMatrix(cmatrix);
break;
}
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT void FPDF_CALLCONV
FPDFPageObj_SetBlendMode(FPDF_PAGEOBJECT page_object,
FPDF_BYTESTRING blend_mode) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return;
pPageObj->mutable_general_state().SetBlendMode(blend_mode);
pPageObj->SetDirty(true);
}
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_TransformAnnots(FPDF_PAGE page,
double a,
double b,
double c,
double d,
double e,
double f) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!pPage)
return;
CPDF_AnnotList AnnotList(pPage);
for (size_t i = 0; i < AnnotList.Count(); ++i) {
CPDF_Annot* pAnnot = AnnotList.GetAt(i);
CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e,
(float)f);
CFX_FloatRect rect = matrix.TransformRect(pAnnot->GetRect());
RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
RetainPtr<CPDF_Array> pRectArray = pAnnotDict->GetMutableArrayFor("Rect");
if (pRectArray)
pRectArray->Clear();
else
pRectArray = pAnnotDict->SetNewFor<CPDF_Array>("Rect");
pRectArray->AppendNew<CPDF_Number>(rect.left);
pRectArray->AppendNew<CPDF_Number>(rect.bottom);
pRectArray->AppendNew<CPDF_Number>(rect.right);
pRectArray->AppendNew<CPDF_Number>(rect.top);
// TODO(unknown): Transform AP's rectangle
}
}
FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetRotation(FPDF_PAGE page,
int rotate) {
CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
if (!IsPageObject(pPage))
return;
rotate %= 4;
pPage->GetMutableDict()->SetNewFor<CPDF_Number>(pdfium::page_object::kRotate,
rotate * 90);
pPage->UpdateDimensions();
}
FPDF_BOOL FPDFPageObj_SetFillColor(FPDF_PAGEOBJECT page_object,
unsigned int R,
unsigned int G,
unsigned int B,
unsigned int A) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || R > 255 || G > 255 || B > 255 || A > 255)
return false;
std::vector<float> rgb = {R / 255.f, G / 255.f, B / 255.f};
pPageObj->mutable_general_state().SetFillAlpha(A / 255.f);
pPageObj->mutable_color_state().SetFillColor(
CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB),
std::move(rgb));
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetFillColor(FPDF_PAGEOBJECT page_object,
unsigned int* R,
unsigned int* G,
unsigned int* B,
unsigned int* A) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !R || !G || !B || !A)
return false;
if (!pPageObj->color_state().HasRef()) {
return false;
}
FX_COLORREF fill_color = pPageObj->color_state().GetFillColorRef();
*R = FXSYS_GetRValue(fill_color);
*G = FXSYS_GetGValue(fill_color);
*B = FXSYS_GetBValue(fill_color);
*A = FXSYS_GetUnsignedAlpha(pPageObj->general_state().GetFillAlpha());
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetBounds(FPDF_PAGEOBJECT page_object,
float* left,
float* bottom,
float* right,
float* top) {
CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
const CFX_FloatRect& bbox = pPageObj->GetRect();
*left = bbox.left;
*bottom = bbox.bottom;
*right = bbox.right;
*top = bbox.top;
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetRotatedBounds(FPDF_PAGEOBJECT page_object,
FS_QUADPOINTSF* quad_points) {
CPDF_PageObject* cpage_object = CPDFPageObjectFromFPDFPageObject(page_object);
if (!cpage_object || !quad_points)
return false;
CFX_Matrix matrix;
switch (cpage_object->GetType()) {
case CPDF_PageObject::Type::kText:
matrix = cpage_object->AsText()->GetTextMatrix();
break;
case CPDF_PageObject::Type::kImage:
matrix = cpage_object->AsImage()->matrix();
break;
default:
// TODO(crbug.com/pdfium/1840): Support more object types.
return false;
}
const CFX_FloatRect& bbox = cpage_object->GetOriginalRect();
const CFX_PointF bottom_left = matrix.Transform({bbox.left, bbox.bottom});
const CFX_PointF bottom_right = matrix.Transform({bbox.right, bbox.bottom});
const CFX_PointF top_right = matrix.Transform({bbox.right, bbox.top});
const CFX_PointF top_left = matrix.Transform({bbox.left, bbox.top});
// See PDF 32000-1:2008, figure 64 for the QuadPoints ordering.
quad_points->x1 = bottom_left.x;
quad_points->y1 = bottom_left.y;
quad_points->x2 = bottom_right.x;
quad_points->y2 = bottom_right.y;
quad_points->x3 = top_right.x;
quad_points->y3 = top_right.y;
quad_points->x4 = top_left.x;
quad_points->y4 = top_left.y;
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetStrokeColor(FPDF_PAGEOBJECT page_object,
unsigned int R,
unsigned int G,
unsigned int B,
unsigned int A) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || R > 255 || G > 255 || B > 255 || A > 255)
return false;
std::vector<float> rgb = {R / 255.f, G / 255.f, B / 255.f};
pPageObj->mutable_general_state().SetStrokeAlpha(A / 255.f);
pPageObj->mutable_color_state().SetStrokeColor(
CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB),
std::move(rgb));
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetStrokeColor(FPDF_PAGEOBJECT page_object,
unsigned int* R,
unsigned int* G,
unsigned int* B,
unsigned int* A) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !R || !G || !B || !A) {
return false;
}
if (!pPageObj->color_state().HasRef()) {
return false;
}
FX_COLORREF stroke_color = pPageObj->color_state().GetStrokeColorRef();
*R = FXSYS_GetRValue(stroke_color);
*G = FXSYS_GetGValue(stroke_color);
*B = FXSYS_GetBValue(stroke_color);
*A = FXSYS_GetUnsignedAlpha(pPageObj->general_state().GetStrokeAlpha());
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetStrokeWidth(FPDF_PAGEOBJECT page_object, float width) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || width < 0.0f)
return false;
pPageObj->mutable_graph_state().SetLineWidth(width);
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetStrokeWidth(FPDF_PAGEOBJECT page_object, float* width) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !width)
return false;
*width = pPageObj->graph_state().GetLineWidth();
return true;
}
FPDF_EXPORT int FPDF_CALLCONV
FPDFPageObj_GetLineJoin(FPDF_PAGEOBJECT page_object) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
return pPageObj ? static_cast<int>(pPageObj->graph_state().GetLineJoin())
: -1;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetLineJoin(FPDF_PAGEOBJECT page_object, int line_join) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
if (line_join < FPDF_LINEJOIN_MITER || line_join > FPDF_LINEJOIN_BEVEL)
return false;
pPageObj->mutable_graph_state().SetLineJoin(
static_cast<CFX_GraphStateData::LineJoin>(line_join));
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT int FPDF_CALLCONV
FPDFPageObj_GetLineCap(FPDF_PAGEOBJECT page_object) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
return pPageObj ? static_cast<int>(pPageObj->graph_state().GetLineCap()) : -1;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetLineCap(FPDF_PAGEOBJECT page_object, int line_cap) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
if (line_cap < FPDF_LINECAP_BUTT ||
line_cap > FPDF_LINECAP_PROJECTING_SQUARE) {
return false;
}
pPageObj->mutable_graph_state().SetLineCap(
static_cast<CFX_GraphStateData::LineCap>(line_cap));
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetDashPhase(FPDF_PAGEOBJECT page_object, float* phase) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !phase)
return false;
*phase = pPageObj->graph_state().GetLineDashPhase();
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetDashPhase(FPDF_PAGEOBJECT page_object, float phase) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
pPageObj->mutable_graph_state().SetLineDashPhase(phase);
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT int FPDF_CALLCONV
FPDFPageObj_GetDashCount(FPDF_PAGEOBJECT page_object) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
return pPageObj ? pdfium::checked_cast<int>(
pPageObj->graph_state().GetLineDashSize())
: -1;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_GetDashArray(FPDF_PAGEOBJECT page_object,
float* dash_array,
size_t dash_count) {
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj || !dash_array)
return false;
auto dash_vector = pPageObj->graph_state().GetLineDashArray();
if (dash_vector.size() > dash_count)
return false;
FXSYS_memcpy(dash_array, dash_vector.data(),
dash_vector.size() * sizeof(float));
return true;
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPageObj_SetDashArray(FPDF_PAGEOBJECT page_object,
const float* dash_array,
size_t dash_count,
float phase) {
if (dash_count > 0 && !dash_array)
return false;
auto* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
if (!pPageObj)
return false;
std::vector<float> dashes;
if (dash_count > 0) {
dashes.reserve(dash_count);
dashes.assign(dash_array, dash_array + dash_count);
}
pPageObj->mutable_graph_state().SetLineDash(dashes, phase, 1.0f);
pPageObj->SetDirty(true);
return true;
}
FPDF_EXPORT int FPDF_CALLCONV
FPDFFormObj_CountObjects(FPDF_PAGEOBJECT form_object) {
const auto* pObjectList = CPDFPageObjHolderFromFPDFFormObject(form_object);
return pObjectList
? pdfium::checked_cast<int>(pObjectList->GetPageObjectCount())
: -1;
}
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index) {
const auto* pObjectList = CPDFPageObjHolderFromFPDFFormObject(form_object);
if (!pObjectList)
return nullptr;
return FPDFPageObjectFromCPDFPageObject(
pObjectList->GetPageObjectByIndex(index));
}