blob: 87c7bcdb6a2be39bf6cfbb65bcc27fc30a458540 [file] [log] [blame]
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001// 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.
Lei Zhanga6d9f0e2015-06-13 00:48:38 -07004
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07005// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
Lei Zhangb4e7f302015-11-06 15:52:32 -08007#include "public/fpdf_ppo.h"
8
Bo Xufdc00a72014-10-28 23:03:33 -07009#include "../include/fpdfxfa/fpdfxfa_doc.h"
Lei Zhang8241df72015-11-06 14:38:48 -080010#include "../include/fsdk_define.h"
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070011
Nico Weber9d8ec5a2015-08-04 13:00:21 -070012class CPDF_PageOrganizer {
13 public:
14 CPDF_PageOrganizer();
15 ~CPDF_PageOrganizer();
Lei Zhanga6d9f0e2015-06-13 00:48:38 -070016
Nico Weber9d8ec5a2015-08-04 13:00:21 -070017 public:
18 FX_BOOL PDFDocInit(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc);
19 FX_BOOL ExportPage(CPDF_Document* pSrcPDFDoc,
20 CFX_WordArray* nPageNum,
21 CPDF_Document* pDestPDFDoc,
22 int nIndex);
23 CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary* pDict,
24 CFX_ByteString nSrctag);
25 FX_BOOL UpdateReference(CPDF_Object* pObj,
26 CPDF_Document* pDoc,
27 CFX_MapPtrToPtr* pMapPtrToPtr);
28 int GetNewObjId(CPDF_Document* pDoc,
29 CFX_MapPtrToPtr* pMapPtrToPtr,
30 CPDF_Reference* pRef);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070031};
32
Nico Weber9d8ec5a2015-08-04 13:00:21 -070033CPDF_PageOrganizer::CPDF_PageOrganizer() {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070034
Nico Weber9d8ec5a2015-08-04 13:00:21 -070035CPDF_PageOrganizer::~CPDF_PageOrganizer() {}
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070036
Nico Weber9d8ec5a2015-08-04 13:00:21 -070037FX_BOOL CPDF_PageOrganizer::PDFDocInit(CPDF_Document* pDestPDFDoc,
38 CPDF_Document* pSrcPDFDoc) {
39 if (!pDestPDFDoc || !pSrcPDFDoc)
40 return false;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070041
Nico Weber9d8ec5a2015-08-04 13:00:21 -070042 CPDF_Dictionary* pNewRoot = pDestPDFDoc->GetRoot();
43 if (!pNewRoot)
Tom Sepez2f2ffec2015-07-23 14:42:09 -070044 return FALSE;
Nico Weber9d8ec5a2015-08-04 13:00:21 -070045
46 // Set the document information////////////////////////////////////////////
47
48 CPDF_Dictionary* DInfoDict = pDestPDFDoc->GetInfo();
49
50 if (!DInfoDict)
51 return FALSE;
52
53 CFX_ByteString producerstr;
54 producerstr.Format("PDFium");
55 DInfoDict->SetAt("Producer", new CPDF_String(producerstr));
56
57 // Set type////////////////////////////////////////////////////////////////
58 CFX_ByteString cbRootType = pNewRoot->GetString("Type", "");
59 if (cbRootType.Equal("")) {
60 pNewRoot->SetAt("Type", new CPDF_Name("Catalog"));
61 }
62
Dan Sinclairf1251c12015-10-20 16:24:45 -040063 CPDF_Dictionary* pNewPages = ToDictionary(
64 pNewRoot->GetElement("Pages") ? pNewRoot->GetElement("Pages")->GetDirect()
65 : nullptr);
66
Nico Weber9d8ec5a2015-08-04 13:00:21 -070067 if (!pNewPages) {
68 pNewPages = new CPDF_Dictionary;
69 FX_DWORD NewPagesON = pDestPDFDoc->AddIndirectObject(pNewPages);
70 pNewRoot->SetAt("Pages", new CPDF_Reference(pDestPDFDoc, NewPagesON));
71 }
72
73 CFX_ByteString cbPageType = pNewPages->GetString("Type", "");
74 if (cbPageType.Equal("")) {
75 pNewPages->SetAt("Type", new CPDF_Name("Pages"));
76 }
77
78 CPDF_Array* pKeysArray = pNewPages->GetArray("Kids");
79 if (pKeysArray == NULL) {
80 CPDF_Array* pNewKids = new CPDF_Array;
81 FX_DWORD Kidsobjnum = -1;
82 Kidsobjnum =
83 pDestPDFDoc->AddIndirectObject(pNewKids); //, Kidsobjnum, Kidsgennum);
84
85 pNewPages->SetAt(
86 "Kids", new CPDF_Reference(pDestPDFDoc, Kidsobjnum)); //, Kidsgennum));
87 pNewPages->SetAt("Count", new CPDF_Number(0));
88 }
89
90 return true;
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -070091}
92
Nico Weber9d8ec5a2015-08-04 13:00:21 -070093FX_BOOL CPDF_PageOrganizer::ExportPage(CPDF_Document* pSrcPDFDoc,
94 CFX_WordArray* nPageNum,
95 CPDF_Document* pDestPDFDoc,
96 int nIndex) {
97 int curpage = nIndex;
98
99 CFX_MapPtrToPtr* pMapPtrToPtr = new CFX_MapPtrToPtr;
100 pMapPtrToPtr->InitHashTable(1001);
101
102 for (int i = 0; i < nPageNum->GetSize(); i++) {
103 CPDF_Dictionary* pCurPageDict = pDestPDFDoc->CreateNewPage(curpage);
104 CPDF_Dictionary* pSrcPageDict = pSrcPDFDoc->GetPage(nPageNum->GetAt(i) - 1);
105 if (!pSrcPageDict || !pCurPageDict) {
106 delete pMapPtrToPtr;
107 return FALSE;
108 }
109
110 // Clone the page dictionary///////////
111 FX_POSITION SrcPos = pSrcPageDict->GetStartPos();
112 while (SrcPos) {
113 CFX_ByteString cbSrcKeyStr;
114 CPDF_Object* pObj = pSrcPageDict->GetNextElement(SrcPos, cbSrcKeyStr);
115 if (cbSrcKeyStr.Compare(("Type")) && cbSrcKeyStr.Compare(("Parent"))) {
116 if (pCurPageDict->KeyExist(cbSrcKeyStr))
117 pCurPageDict->RemoveAt(cbSrcKeyStr);
118 pCurPageDict->SetAt(cbSrcKeyStr, pObj->Clone());
119 }
120 }
121
122 // inheritable item///////////////////////
123 CPDF_Object* pInheritable = NULL;
124 // 1 MediaBox //required
125 if (!pCurPageDict->KeyExist("MediaBox")) {
126 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "MediaBox");
127 if (!pInheritable) {
128 // Search the "CropBox" from source page dictionary, if not exists,we
129 // take the letter size.
130 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox");
131 if (pInheritable)
132 pCurPageDict->SetAt("MediaBox", pInheritable->Clone());
133 else {
134 // Make the default size to be letter size (8.5'x11')
135 CPDF_Array* pArray = new CPDF_Array;
136 pArray->AddNumber(0);
137 pArray->AddNumber(0);
138 pArray->AddNumber(612);
139 pArray->AddNumber(792);
140 pCurPageDict->SetAt("MediaBox", pArray);
141 }
142 } else
143 pCurPageDict->SetAt("MediaBox", pInheritable->Clone());
144 }
145 // 2 Resources //required
146 if (!pCurPageDict->KeyExist("Resources")) {
147 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Resources");
148 if (!pInheritable) {
149 delete pMapPtrToPtr;
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700150 return FALSE;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700151 }
152 pCurPageDict->SetAt("Resources", pInheritable->Clone());
153 }
154 // 3 CropBox //Optional
155 if (!pCurPageDict->KeyExist("CropBox")) {
156 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox");
157 if (pInheritable)
158 pCurPageDict->SetAt("CropBox", pInheritable->Clone());
159 }
160 // 4 Rotate //Optional
161 if (!pCurPageDict->KeyExist("Rotate")) {
162 pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Rotate");
163 if (pInheritable)
164 pCurPageDict->SetAt("Rotate", pInheritable->Clone());
165 }
166
167 /////////////////////////////////////////////
168 // Update the reference
169 FX_DWORD dwOldPageObj = pSrcPageDict->GetObjNum();
170 FX_DWORD dwNewPageObj = pCurPageDict->GetObjNum();
171
172 pMapPtrToPtr->SetAt((void*)(uintptr_t)dwOldPageObj,
173 (void*)(uintptr_t)dwNewPageObj);
174
175 UpdateReference(pCurPageDict, pDestPDFDoc, pMapPtrToPtr);
176 curpage++;
177 }
178
179 delete pMapPtrToPtr;
180 return TRUE;
181}
182
183CPDF_Object* CPDF_PageOrganizer::PageDictGetInheritableTag(
184 CPDF_Dictionary* pDict,
185 CFX_ByteString nSrctag) {
186 if (!pDict || !pDict->KeyExist("Type") || nSrctag.IsEmpty())
187 return NULL;
188
189 CPDF_Object* pType = pDict->GetElement("Type")->GetDirect();
Dan Sinclair710c9092015-10-21 15:46:10 -0400190 if (!ToName(pType))
191 return nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700192 if (pType->GetString().Compare("Page"))
193 return NULL;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700194 if (!pDict->KeyExist("Parent"))
195 return NULL;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700196
Dan Sinclairf1251c12015-10-20 16:24:45 -0400197 CPDF_Dictionary* pp = ToDictionary(pDict->GetElement("Parent")->GetDirect());
198 if (!pp)
199 return nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700200
201 if (pDict->KeyExist((const char*)nSrctag))
202 return pDict->GetElement((const char*)nSrctag);
Dan Sinclairf1251c12015-10-20 16:24:45 -0400203
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700204 while (pp) {
205 if (pp->KeyExist((const char*)nSrctag))
206 return pp->GetElement((const char*)nSrctag);
Dan Sinclairf1251c12015-10-20 16:24:45 -0400207 if (!pp->KeyExist("Parent")) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700208 break;
Dan Sinclairf1251c12015-10-20 16:24:45 -0400209 }
210 pp = ToDictionary(pp->GetElement("Parent")->GetDirect());
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700211 }
Dan Sinclairf1251c12015-10-20 16:24:45 -0400212 return nullptr;
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700213}
214
215FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj,
216 CPDF_Document* pDoc,
217 CFX_MapPtrToPtr* pMapPtrToPtr) {
218 switch (pObj->GetType()) {
219 case PDFOBJ_REFERENCE: {
Dan Sinclairbf81c142015-10-26 16:54:39 -0400220 CPDF_Reference* pReference = pObj->AsReference();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700221 int newobjnum = GetNewObjId(pDoc, pMapPtrToPtr, pReference);
222 if (newobjnum == 0)
Tom Sepez2f2ffec2015-07-23 14:42:09 -0700223 return FALSE;
Dan Sinclairbf81c142015-10-26 16:54:39 -0400224 pReference->SetRef(pDoc, newobjnum);
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700225 break;
226 }
227 case PDFOBJ_DICTIONARY: {
Dan Sinclairf1251c12015-10-20 16:24:45 -0400228 CPDF_Dictionary* pDict = pObj->AsDictionary();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700229
230 FX_POSITION pos = pDict->GetStartPos();
231 while (pos) {
232 CFX_ByteString key("");
233 CPDF_Object* pNextObj = pDict->GetNextElement(pos, key);
234 if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") ||
235 !FXSYS_strcmp(key, "First"))
236 continue;
237 if (pNextObj) {
238 if (!UpdateReference(pNextObj, pDoc, pMapPtrToPtr))
239 pDict->RemoveAt(key);
240 } else
241 return FALSE;
242 }
243 break;
244 }
245 case PDFOBJ_ARRAY: {
Dan Sinclair2b11dc12015-10-22 15:02:06 -0400246 CPDF_Array* pArray = pObj->AsArray();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700247 FX_DWORD count = pArray->GetCount();
248 for (FX_DWORD i = 0; i < count; i++) {
249 CPDF_Object* pNextObj = pArray->GetElement(i);
250 if (pNextObj) {
251 if (!UpdateReference(pNextObj, pDoc, pMapPtrToPtr))
252 return FALSE;
253 } else
254 return FALSE;
255 }
256 break;
257 }
258 case PDFOBJ_STREAM: {
Dan Sinclairaa435ba2015-10-22 16:45:48 -0400259 CPDF_Stream* pStream = pObj->AsStream();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700260 CPDF_Dictionary* pDict = pStream->GetDict();
261 if (pDict) {
262 if (!UpdateReference(pDict, pDoc, pMapPtrToPtr))
263 return FALSE;
264 } else
265 return FALSE;
266 break;
267 }
268 default:
269 break;
270 }
271
272 return TRUE;
273}
274
275int CPDF_PageOrganizer::GetNewObjId(CPDF_Document* pDoc,
276 CFX_MapPtrToPtr* pMapPtrToPtr,
277 CPDF_Reference* pRef) {
278 if (!pRef)
279 return 0;
280
281 size_t dwObjnum = pRef->GetRefObjNum();
282 size_t dwNewObjNum = 0;
283
284 pMapPtrToPtr->Lookup((void*)dwObjnum, (void*&)dwNewObjNum);
285 if (dwNewObjNum) {
286 return (int)dwNewObjNum;
287 }
288
289 CPDF_Object* pDirect = pRef->GetDirect();
290 if (!pDirect) {
291 return 0;
292 }
293
294 CPDF_Object* pClone = pDirect->Clone();
295 if (!pClone) {
296 return 0;
297 }
298
Dan Sinclairf1251c12015-10-20 16:24:45 -0400299 if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) {
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700300 if (pDictClone->KeyExist("Type")) {
301 CFX_ByteString strType = pDictClone->GetString("Type");
302 if (!FXSYS_stricmp(strType, "Pages")) {
303 pDictClone->Release();
304 return 4;
305 } else if (!FXSYS_stricmp(strType, "Page")) {
306 pDictClone->Release();
307 return 0;
308 }
309 }
310 }
311
312 dwNewObjNum = pDoc->AddIndirectObject(pClone);
313 pMapPtrToPtr->SetAt((void*)dwObjnum, (void*)dwNewObjNum);
314 if (!UpdateReference(pClone, pDoc, pMapPtrToPtr)) {
315 pClone->Release();
316 return 0;
317 }
318
319 return (int)dwNewObjNum;
320}
321
322FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring,
323 CFX_WordArray* pageArray,
324 int nCount) {
325 if (rangstring.GetLength() != 0) {
326 rangstring.Remove(' ');
327 int nLength = rangstring.GetLength();
328 CFX_ByteString cbCompareString("0123456789-,");
329 for (int i = 0; i < nLength; i++) {
330 if (cbCompareString.Find(rangstring[i]) == -1)
331 return FALSE;
332 }
333 CFX_ByteString cbMidRange;
334 int nStringFrom = 0;
335 int nStringTo = 0;
336 while (nStringTo < nLength) {
337 nStringTo = rangstring.Find(',', nStringFrom);
338 if (nStringTo == -1) {
339 nStringTo = nLength;
340 }
341 cbMidRange = rangstring.Mid(nStringFrom, nStringTo - nStringFrom);
342
343 int nMid = cbMidRange.Find('-');
344 if (nMid == -1) {
345 long lPageNum = atol(cbMidRange);
346 if (lPageNum <= 0 || lPageNum > nCount)
347 return FALSE;
348
349 pageArray->Add((FX_WORD)lPageNum);
350 } else {
351 int nStartPageNum = atol(cbMidRange.Mid(0, nMid));
352 if (nStartPageNum == 0)
353 return FALSE;
354
355 nMid = nMid + 1;
356 int nEnd = cbMidRange.GetLength() - nMid;
357 if (nEnd == 0)
358 return FALSE;
359
360 int nEndPageNum = atol(cbMidRange.Mid(nMid, nEnd));
361 if (nStartPageNum < 0 || nStartPageNum > nEndPageNum ||
362 nEndPageNum > nCount)
363 return FALSE;
364
365 for (int nIndex = nStartPageNum; nIndex <= nEndPageNum; ++nIndex)
366 pageArray->Add(nIndex);
367 }
368 nStringFrom = nStringTo + 1;
369 }
370 }
371 return TRUE;
372}
373
374DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
375 FPDF_DOCUMENT src_doc,
376 FPDF_BYTESTRING pagerange,
377 int index) {
Tom Sepez471a1032015-10-15 16:17:18 -0700378 CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc);
379 if (!dest_doc)
380 return FALSE;
381
382 CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
383 if (!pSrcDoc)
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700384 return FALSE;
385 CFX_WordArray pageArray;
Tom Sepez471a1032015-10-15 16:17:18 -0700386 int nCount = pSrcDoc->GetPageCount();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700387 if (pagerange) {
388 if (ParserPageRangeString(pagerange, &pageArray, nCount) == FALSE)
389 return FALSE;
390 } else {
391 for (int i = 1; i <= nCount; i++) {
392 pageArray.Add(i);
393 }
394 }
395
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700396 CPDF_PageOrganizer pageOrg;
Tom Sepez471a1032015-10-15 16:17:18 -0700397 pageOrg.PDFDocInit(pDestDoc, pSrcDoc);
398 return pageOrg.ExportPage(pSrcDoc, &pageArray, pDestDoc, index);
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -0700399}
400
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700401DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc,
402 FPDF_DOCUMENT src_doc) {
Tom Sepez471a1032015-10-15 16:17:18 -0700403 CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc);
404 if (!pDstDoc)
405 return FALSE;
406
407 CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
408 if (!pSrcDoc)
409 return FALSE;
410
411 CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700412 pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));
413 if (!pSrcDict)
414 return FALSE;
Tom Sepez471a1032015-10-15 16:17:18 -0700415
416 CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700417 if (!pDstDict)
418 return FALSE;
Tom Sepez471a1032015-10-15 16:17:18 -0700419
Nico Weber9d8ec5a2015-08-04 13:00:21 -0700420 pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE));
421 return TRUE;
422}