1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "public/fpdf_edit.h"
8
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12
13 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
14 #include "core/fpdfapi/page/cpdf_form.h"
15 #include "core/fpdfapi/page/cpdf_formobject.h"
16 #include "core/fpdfapi/page/cpdf_imageobject.h"
17 #include "core/fpdfapi/page/cpdf_page.h"
18 #include "core/fpdfapi/page/cpdf_pageobject.h"
19 #include "core/fpdfapi/page/cpdf_pathobject.h"
20 #include "core/fpdfapi/page/cpdf_shadingobject.h"
21 #include "core/fpdfapi/parser/cpdf_array.h"
22 #include "core/fpdfapi/parser/cpdf_document.h"
23 #include "core/fpdfapi/parser/cpdf_number.h"
24 #include "core/fpdfapi/parser/cpdf_string.h"
25 #include "core/fpdfdoc/cpdf_annot.h"
26 #include "core/fpdfdoc/cpdf_annotlist.h"
27 #include "fpdfsdk/fsdk_define.h"
28 #include "public/fpdf_formfill.h"
29 #include "third_party/base/stl_util.h"
30
31 #ifdef PDF_ENABLE_XFA
32 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
33 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
34 #endif // PDF_ENABLE_XFA
35
36 #if _FX_OS_ == _FX_ANDROID_
37 #include <time.h>
38 #else
39 #include <ctime>
40 #endif
41
42 namespace {
43
44 static_assert(FPDF_PAGEOBJ_TEXT == CPDF_PageObject::TEXT,
45 "FPDF_PAGEOBJ_TEXT/CPDF_PageObject::TEXT mismatch");
46 static_assert(FPDF_PAGEOBJ_PATH == CPDF_PageObject::PATH,
47 "FPDF_PAGEOBJ_PATH/CPDF_PageObject::PATH mismatch");
48 static_assert(FPDF_PAGEOBJ_IMAGE == CPDF_PageObject::IMAGE,
49 "FPDF_PAGEOBJ_IMAGE/CPDF_PageObject::IMAGE mismatch");
50 static_assert(FPDF_PAGEOBJ_SHADING == CPDF_PageObject::SHADING,
51 "FPDF_PAGEOBJ_SHADING/CPDF_PageObject::SHADING mismatch");
52 static_assert(FPDF_PAGEOBJ_FORM == CPDF_PageObject::FORM,
53 "FPDF_PAGEOBJ_FORM/CPDF_PageObject::FORM mismatch");
54
IsPageObject(CPDF_Page * pPage)55 bool IsPageObject(CPDF_Page* pPage) {
56 if (!pPage || !pPage->m_pFormDict || !pPage->m_pFormDict->KeyExist("Type"))
57 return false;
58
59 CPDF_Object* pObject = pPage->m_pFormDict->GetObjectFor("Type")->GetDirect();
60 return pObject && !pObject->GetString().Compare("Page");
61 }
62
63 } // namespace
64
FPDF_CreateNewDocument()65 DLLEXPORT FPDF_DOCUMENT STDCALL FPDF_CreateNewDocument() {
66 CPDF_Document* pDoc = new CPDF_Document(nullptr);
67 pDoc->CreateNewDoc();
68 time_t currentTime;
69
70 CFX_ByteString DateStr;
71
72 if (FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS)) {
73 if (-1 != time(¤tTime)) {
74 tm* pTM = localtime(¤tTime);
75 if (pTM) {
76 DateStr.Format("D:%04d%02d%02d%02d%02d%02d", pTM->tm_year + 1900,
77 pTM->tm_mon + 1, pTM->tm_mday, pTM->tm_hour, pTM->tm_min,
78 pTM->tm_sec);
79 }
80 }
81 }
82
83 CPDF_Dictionary* pInfoDict = nullptr;
84 pInfoDict = pDoc->GetInfo();
85 if (pInfoDict) {
86 if (FSDK_IsSandBoxPolicyEnabled(FPDF_POLICY_MACHINETIME_ACCESS))
87 pInfoDict->SetNewFor<CPDF_String>("CreationDate", DateStr, false);
88 pInfoDict->SetNewFor<CPDF_String>("Creator", L"PDFium");
89 }
90
91 return FPDFDocumentFromCPDFDocument(pDoc);
92 }
93
FPDFPage_Delete(FPDF_DOCUMENT document,int page_index)94 DLLEXPORT void STDCALL FPDFPage_Delete(FPDF_DOCUMENT document, int page_index) {
95 if (UnderlyingDocumentType* pDoc = UnderlyingFromFPDFDocument(document))
96 pDoc->DeletePage(page_index);
97 }
98
FPDFPage_New(FPDF_DOCUMENT document,int page_index,double width,double height)99 DLLEXPORT FPDF_PAGE STDCALL FPDFPage_New(FPDF_DOCUMENT document,
100 int page_index,
101 double width,
102 double height) {
103 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
104 if (!pDoc)
105 return nullptr;
106
107 page_index = std::min(std::max(page_index, 0), pDoc->GetPageCount());
108 CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(page_index);
109 if (!pPageDict)
110 return nullptr;
111
112 CPDF_Array* pMediaBoxArray = pPageDict->SetNewFor<CPDF_Array>("MediaBox");
113 pMediaBoxArray->AddNew<CPDF_Number>(0);
114 pMediaBoxArray->AddNew<CPDF_Number>(0);
115 pMediaBoxArray->AddNew<CPDF_Number>(static_cast<FX_FLOAT>(width));
116 pMediaBoxArray->AddNew<CPDF_Number>(static_cast<FX_FLOAT>(height));
117 pPageDict->SetNewFor<CPDF_Number>("Rotate", 0);
118 pPageDict->SetNewFor<CPDF_Dictionary>("Resources");
119
120 #ifdef PDF_ENABLE_XFA
121 CPDFXFA_Page* pPage =
122 new CPDFXFA_Page(static_cast<CPDFXFA_Context*>(document), page_index);
123 pPage->LoadPDFPage(pPageDict);
124 #else // PDF_ENABLE_XFA
125 CPDF_Page* pPage = new CPDF_Page(pDoc, pPageDict, true);
126 pPage->ParseContent();
127 #endif // PDF_ENABLE_XFA
128
129 return pPage;
130 }
131
FPDFPage_GetRotation(FPDF_PAGE page)132 DLLEXPORT int STDCALL FPDFPage_GetRotation(FPDF_PAGE page) {
133 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
134 if (!IsPageObject(pPage))
135 return -1;
136
137 CPDF_Dictionary* pDict = pPage->m_pFormDict;
138 while (pDict) {
139 if (pDict->KeyExist("Rotate")) {
140 CPDF_Object* pRotateObj = pDict->GetObjectFor("Rotate")->GetDirect();
141 return pRotateObj ? pRotateObj->GetInteger() / 90 : 0;
142 }
143 if (!pDict->KeyExist("Parent"))
144 break;
145
146 pDict = ToDictionary(pDict->GetObjectFor("Parent")->GetDirect());
147 }
148
149 return 0;
150 }
151
FPDFPage_InsertObject(FPDF_PAGE page,FPDF_PAGEOBJECT page_obj)152 DLLEXPORT void STDCALL FPDFPage_InsertObject(FPDF_PAGE page,
153 FPDF_PAGEOBJECT page_obj) {
154 CPDF_PageObject* pPageObj = reinterpret_cast<CPDF_PageObject*>(page_obj);
155 if (!pPageObj)
156 return;
157
158 std::unique_ptr<CPDF_PageObject> pPageObjHolder(pPageObj);
159 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
160 if (!IsPageObject(pPage))
161 return;
162
163 pPage->GetPageObjectList()->push_back(std::move(pPageObjHolder));
164 switch (pPageObj->GetType()) {
165 case CPDF_PageObject::TEXT: {
166 break;
167 }
168 case CPDF_PageObject::PATH: {
169 CPDF_PathObject* pPathObj = pPageObj->AsPath();
170 pPathObj->CalcBoundingBox();
171 break;
172 }
173 case CPDF_PageObject::IMAGE: {
174 CPDF_ImageObject* pImageObj = pPageObj->AsImage();
175 pImageObj->CalcBoundingBox();
176 break;
177 }
178 case CPDF_PageObject::SHADING: {
179 CPDF_ShadingObject* pShadingObj = pPageObj->AsShading();
180 pShadingObj->CalcBoundingBox();
181 break;
182 }
183 case CPDF_PageObject::FORM: {
184 CPDF_FormObject* pFormObj = pPageObj->AsForm();
185 pFormObj->CalcBoundingBox();
186 break;
187 }
188 default: {
189 ASSERT(false);
190 break;
191 }
192 }
193 }
194
FPDFPage_CountObject(FPDF_PAGE page)195 DLLEXPORT int STDCALL FPDFPage_CountObject(FPDF_PAGE page) {
196 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
197 if (!IsPageObject(pPage))
198 return -1;
199 return pdfium::CollectionSize<int>(*pPage->GetPageObjectList());
200 }
201
FPDFPage_GetObject(FPDF_PAGE page,int index)202 DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFPage_GetObject(FPDF_PAGE page,
203 int index) {
204 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
205 if (!IsPageObject(pPage))
206 return nullptr;
207 return pPage->GetPageObjectList()->GetPageObjectByIndex(index);
208 }
209
FPDFPage_HasTransparency(FPDF_PAGE page)210 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_HasTransparency(FPDF_PAGE page) {
211 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
212 return pPage && pPage->BackgroundAlphaNeeded();
213 }
214
215 DLLEXPORT FPDF_BOOL STDCALL
FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT pageObject)216 FPDFPageObj_HasTransparency(FPDF_PAGEOBJECT pageObject) {
217 if (!pageObject)
218 return false;
219
220 CPDF_PageObject* pPageObj = reinterpret_cast<CPDF_PageObject*>(pageObject);
221 int blend_type = pPageObj->m_GeneralState.GetBlendType();
222 if (blend_type != FXDIB_BLEND_NORMAL)
223 return true;
224
225 CPDF_Dictionary* pSMaskDict =
226 ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
227 if (pSMaskDict)
228 return true;
229
230 if (pPageObj->m_GeneralState.GetFillAlpha() != 1.0f)
231 return true;
232
233 if (pPageObj->IsPath() && pPageObj->m_GeneralState.GetStrokeAlpha() != 1.0f) {
234 return true;
235 }
236
237 if (pPageObj->IsForm()) {
238 const CPDF_Form* pForm = pPageObj->AsForm()->form();
239 if (pForm) {
240 int trans = pForm->m_Transparency;
241 if ((trans & PDFTRANS_ISOLATED) || (trans & PDFTRANS_GROUP))
242 return true;
243 }
244 }
245
246 return false;
247 }
248
FPDFPage_GenerateContent(FPDF_PAGE page)249 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GenerateContent(FPDF_PAGE page) {
250 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
251 if (!IsPageObject(pPage))
252 return false;
253
254 CPDF_PageContentGenerator CG(pPage);
255 CG.GenerateContent();
256 return true;
257 }
258
FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object,double a,double b,double c,double d,double e,double f)259 DLLEXPORT void STDCALL FPDFPageObj_Transform(FPDF_PAGEOBJECT page_object,
260 double a,
261 double b,
262 double c,
263 double d,
264 double e,
265 double f) {
266 CPDF_PageObject* pPageObj = reinterpret_cast<CPDF_PageObject*>(page_object);
267 if (!pPageObj)
268 return;
269
270 CFX_Matrix matrix((FX_FLOAT)a, (FX_FLOAT)b, (FX_FLOAT)c, (FX_FLOAT)d,
271 (FX_FLOAT)e, (FX_FLOAT)f);
272 pPageObj->Transform(matrix);
273 }
274
FPDFPage_TransformAnnots(FPDF_PAGE page,double a,double b,double c,double d,double e,double f)275 DLLEXPORT void STDCALL FPDFPage_TransformAnnots(FPDF_PAGE page,
276 double a,
277 double b,
278 double c,
279 double d,
280 double e,
281 double f) {
282 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
283 if (!pPage)
284 return;
285
286 CPDF_AnnotList AnnotList(pPage);
287 for (size_t i = 0; i < AnnotList.Count(); ++i) {
288 CPDF_Annot* pAnnot = AnnotList.GetAt(i);
289 CFX_FloatRect rect = pAnnot->GetRect(); // transformAnnots Rectangle
290 CFX_Matrix matrix((FX_FLOAT)a, (FX_FLOAT)b, (FX_FLOAT)c, (FX_FLOAT)d,
291 (FX_FLOAT)e, (FX_FLOAT)f);
292 matrix.TransformRect(rect);
293
294 CPDF_Array* pRectArray = pAnnot->GetAnnotDict()->GetArrayFor("Rect");
295 if (!pRectArray)
296 pRectArray = pAnnot->GetAnnotDict()->SetNewFor<CPDF_Array>("Rect");
297
298 pRectArray->SetNewAt<CPDF_Number>(0, rect.left);
299 pRectArray->SetNewAt<CPDF_Number>(1, rect.bottom);
300 pRectArray->SetNewAt<CPDF_Number>(2, rect.right);
301 pRectArray->SetNewAt<CPDF_Number>(3, rect.top);
302
303 // TODO(unknown): Transform AP's rectangle
304 }
305 }
306
FPDFPage_SetRotation(FPDF_PAGE page,int rotate)307 DLLEXPORT void STDCALL FPDFPage_SetRotation(FPDF_PAGE page, int rotate) {
308 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
309 if (!IsPageObject(pPage))
310 return;
311
312 CPDF_Dictionary* pDict = pPage->m_pFormDict;
313 rotate %= 4;
314 pDict->SetNewFor<CPDF_Number>("Rotate", rotate * 90);
315 }
316