More master side changes for convergence with XFA.
[pdfium.git] / fpdfsdk / src / fpdfppo.cpp
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_ppo.h"
8 #include "../../third_party/base/nonstd_unique_ptr.h"
9 #include "../include/fsdk_define.h"
10
11 class CPDF_PageOrganizer {
12  public:
13   using ObjectNumberMap = std::map<FX_DWORD, FX_DWORD>;
14   CPDF_PageOrganizer();
15   ~CPDF_PageOrganizer();
16
17   FX_BOOL PDFDocInit(CPDF_Document* pDestPDFDoc, CPDF_Document* pSrcPDFDoc);
18   FX_BOOL ExportPage(CPDF_Document* pSrcPDFDoc,
19                      CFX_WordArray* nPageNum,
20                      CPDF_Document* pDestPDFDoc,
21                      int nIndex);
22   CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary* pDict,
23                                          CFX_ByteString nSrctag);
24   FX_BOOL UpdateReference(CPDF_Object* pObj,
25                           CPDF_Document* pDoc,
26                           ObjectNumberMap* pObjNumberMap);
27   FX_DWORD GetNewObjId(CPDF_Document* pDoc,
28                        ObjectNumberMap* pObjNumberMap,
29                        CPDF_Reference* pRef);
30 };
31
32 CPDF_PageOrganizer::CPDF_PageOrganizer() {}
33
34 CPDF_PageOrganizer::~CPDF_PageOrganizer() {}
35
36 FX_BOOL CPDF_PageOrganizer::PDFDocInit(CPDF_Document* pDestPDFDoc,
37                                        CPDF_Document* pSrcPDFDoc) {
38   if (!pDestPDFDoc || !pSrcPDFDoc)
39     return FALSE;
40
41   CPDF_Dictionary* pNewRoot = pDestPDFDoc->GetRoot();
42   if (!pNewRoot)
43     return FALSE;
44
45   // Set the document information////////////////////////////////////////////
46
47   CPDF_Dictionary* DInfoDict = pDestPDFDoc->GetInfo();
48   if (!DInfoDict)
49     return FALSE;
50
51   CFX_ByteString producerstr;
52   producerstr.Format("PDFium");
53   DInfoDict->SetAt("Producer", new CPDF_String(producerstr));
54
55   // Set type////////////////////////////////////////////////////////////////
56   CFX_ByteString cbRootType = pNewRoot->GetString("Type", "");
57   if (cbRootType.Equal("")) {
58     pNewRoot->SetAt("Type", new CPDF_Name("Catalog"));
59   }
60
61   CPDF_Object* pElement = pNewRoot->GetElement("Pages");
62   CPDF_Dictionary* pNewPages =
63       pElement ? ToDictionary(pElement->GetDirect()) : nullptr;
64   if (!pNewPages) {
65     pNewPages = new CPDF_Dictionary;
66     FX_DWORD NewPagesON = pDestPDFDoc->AddIndirectObject(pNewPages);
67     pNewRoot->SetAt("Pages", new CPDF_Reference(pDestPDFDoc, NewPagesON));
68   }
69
70   CFX_ByteString cbPageType = pNewPages->GetString("Type", "");
71   if (cbPageType.Equal("")) {
72     pNewPages->SetAt("Type", new CPDF_Name("Pages"));
73   }
74
75   CPDF_Array* pKeysArray = pNewPages->GetArray("Kids");
76   if (!pKeysArray) {
77     CPDF_Array* pNewKids = new CPDF_Array;
78     FX_DWORD Kidsobjnum = -1;
79     Kidsobjnum = pDestPDFDoc->AddIndirectObject(pNewKids);
80
81     pNewPages->SetAt("Kids", new CPDF_Reference(pDestPDFDoc, Kidsobjnum));
82     pNewPages->SetAt("Count", new CPDF_Number(0));
83   }
84
85   return TRUE;
86 }
87
88 FX_BOOL CPDF_PageOrganizer::ExportPage(CPDF_Document* pSrcPDFDoc,
89                                        CFX_WordArray* nPageNum,
90                                        CPDF_Document* pDestPDFDoc,
91                                        int nIndex) {
92   int curpage = nIndex;
93
94   nonstd::unique_ptr<ObjectNumberMap> pObjNumberMap(new ObjectNumberMap);
95
96   for (int i = 0; i < nPageNum->GetSize(); ++i) {
97     CPDF_Dictionary* pCurPageDict = pDestPDFDoc->CreateNewPage(curpage);
98     CPDF_Dictionary* pSrcPageDict = pSrcPDFDoc->GetPage(nPageNum->GetAt(i) - 1);
99     if (!pSrcPageDict || !pCurPageDict)
100       return FALSE;
101
102     // Clone the page dictionary///////////
103     FX_POSITION SrcPos = pSrcPageDict->GetStartPos();
104     while (SrcPos) {
105       CFX_ByteString cbSrcKeyStr;
106       CPDF_Object* pObj = pSrcPageDict->GetNextElement(SrcPos, cbSrcKeyStr);
107       if (cbSrcKeyStr.Compare(("Type")) && cbSrcKeyStr.Compare(("Parent"))) {
108         if (pCurPageDict->KeyExist(cbSrcKeyStr))
109           pCurPageDict->RemoveAt(cbSrcKeyStr);
110         pCurPageDict->SetAt(cbSrcKeyStr, pObj->Clone());
111       }
112     }
113
114     // inheritable item///////////////////////
115     CPDF_Object* pInheritable = nullptr;
116     // 1 MediaBox  //required
117     if (!pCurPageDict->KeyExist("MediaBox")) {
118       pInheritable = PageDictGetInheritableTag(pSrcPageDict, "MediaBox");
119       if (!pInheritable) {
120         // Search the "CropBox" from source page dictionary,
121         // if not exists,we take the letter size.
122         pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox");
123         if (pInheritable) {
124           pCurPageDict->SetAt("MediaBox", pInheritable->Clone());
125         } else {
126           // Make the default size to be letter size (8.5'x11')
127           CPDF_Array* pArray = new CPDF_Array;
128           pArray->AddNumber(0);
129           pArray->AddNumber(0);
130           pArray->AddNumber(612);
131           pArray->AddNumber(792);
132           pCurPageDict->SetAt("MediaBox", pArray);
133         }
134       } else {
135         pCurPageDict->SetAt("MediaBox", pInheritable->Clone());
136       }
137     }
138     // 2 Resources //required
139     if (!pCurPageDict->KeyExist("Resources")) {
140       pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Resources");
141       if (!pInheritable)
142         return FALSE;
143       pCurPageDict->SetAt("Resources", pInheritable->Clone());
144     }
145     // 3 CropBox  //Optional
146     if (!pCurPageDict->KeyExist("CropBox")) {
147       pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox");
148       if (pInheritable)
149         pCurPageDict->SetAt("CropBox", pInheritable->Clone());
150     }
151     // 4 Rotate  //Optional
152     if (!pCurPageDict->KeyExist("Rotate")) {
153       pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Rotate");
154       if (pInheritable)
155         pCurPageDict->SetAt("Rotate", pInheritable->Clone());
156     }
157
158     /////////////////////////////////////////////
159     // Update the reference
160     FX_DWORD dwOldPageObj = pSrcPageDict->GetObjNum();
161     FX_DWORD dwNewPageObj = pCurPageDict->GetObjNum();
162
163     (*pObjNumberMap)[dwOldPageObj] = dwNewPageObj;
164
165     UpdateReference(pCurPageDict, pDestPDFDoc, pObjNumberMap.get());
166     ++curpage;
167   }
168
169   return TRUE;
170 }
171
172 CPDF_Object* CPDF_PageOrganizer::PageDictGetInheritableTag(
173     CPDF_Dictionary* pDict,
174     CFX_ByteString nSrctag) {
175   if (!pDict || nSrctag.IsEmpty())
176     return nullptr;
177   if (!pDict->KeyExist("Parent") || !pDict->KeyExist("Type"))
178     return nullptr;
179
180   CPDF_Object* pType = pDict->GetElement("Type")->GetDirect();
181   if (!ToName(pType))
182     return nullptr;
183   if (pType->GetString().Compare("Page"))
184     return nullptr;
185
186   CPDF_Dictionary* pp = ToDictionary(pDict->GetElement("Parent")->GetDirect());
187   if (!pp)
188     return nullptr;
189
190   if (pDict->KeyExist((const char*)nSrctag))
191     return pDict->GetElement((const char*)nSrctag);
192
193   while (pp) {
194     if (pp->KeyExist((const char*)nSrctag)) {
195       return pp->GetElement((const char*)nSrctag);
196     }
197     if (!pp->KeyExist("Parent")) {
198       break;
199     }
200     pp = ToDictionary(pp->GetElement("Parent")->GetDirect());
201   }
202   return nullptr;
203 }
204
205 FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj,
206                                             CPDF_Document* pDoc,
207                                             ObjectNumberMap* pObjNumberMap) {
208   switch (pObj->GetType()) {
209     case PDFOBJ_REFERENCE: {
210       CPDF_Reference* pReference = (CPDF_Reference*)pObj;
211       FX_DWORD newobjnum = GetNewObjId(pDoc, pObjNumberMap, pReference);
212       if (newobjnum == 0)
213         return FALSE;
214       pReference->SetRef(pDoc, newobjnum);
215       break;
216     }
217     case PDFOBJ_DICTIONARY: {
218       CPDF_Dictionary* pDict = pObj->AsDictionary();
219
220       FX_POSITION pos = pDict->GetStartPos();
221       while (pos) {
222         CFX_ByteString key("");
223         CPDF_Object* pNextObj = pDict->GetNextElement(pos, key);
224         if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") ||
225             !FXSYS_strcmp(key, "First")) {
226           continue;
227         }
228         if (pNextObj) {
229           if (!UpdateReference(pNextObj, pDoc, pObjNumberMap))
230             pDict->RemoveAt(key);
231         } else {
232           return FALSE;
233         }
234       }
235       break;
236     }
237     case PDFOBJ_ARRAY: {
238       CPDF_Array* pArray = (CPDF_Array*)pObj;
239       FX_DWORD count = pArray->GetCount();
240       for (FX_DWORD i = 0; i < count; ++i) {
241         CPDF_Object* pNextObj = pArray->GetElement(i);
242         if (!pNextObj)
243           return FALSE;
244         if (!UpdateReference(pNextObj, pDoc, pObjNumberMap))
245           return FALSE;
246       }
247       break;
248     }
249     case PDFOBJ_STREAM: {
250       CPDF_Stream* pStream = (CPDF_Stream*)pObj;
251       CPDF_Dictionary* pDict = pStream->GetDict();
252       if (pDict) {
253         if (!UpdateReference(pDict, pDoc, pObjNumberMap))
254           return FALSE;
255       } else {
256         return FALSE;
257       }
258       break;
259     }
260     default:
261       break;
262   }
263
264   return TRUE;
265 }
266
267 FX_DWORD CPDF_PageOrganizer::GetNewObjId(CPDF_Document* pDoc,
268                                          ObjectNumberMap* pObjNumberMap,
269                                          CPDF_Reference* pRef) {
270   if (!pRef)
271     return 0;
272
273   FX_DWORD dwObjnum = pRef->GetRefObjNum();
274   FX_DWORD dwNewObjNum = 0;
275   const auto it = pObjNumberMap->find(dwObjnum);
276   if (it != pObjNumberMap->end())
277     dwNewObjNum = it->second;
278   if (dwNewObjNum)
279     return dwNewObjNum;
280
281   CPDF_Object* pDirect = pRef->GetDirect();
282   if (!pDirect)
283     return 0;
284
285   CPDF_Object* pClone = pDirect->Clone();
286   if (!pClone)
287     return 0;
288
289   if (CPDF_Dictionary* pDictClone = pClone->AsDictionary()) {
290     if (pDictClone->KeyExist("Type")) {
291       CFX_ByteString strType = pDictClone->GetString("Type");
292       if (!FXSYS_stricmp(strType, "Pages")) {
293         pDictClone->Release();
294         return 4;
295       }
296       if (!FXSYS_stricmp(strType, "Page")) {
297         pDictClone->Release();
298         return 0;
299       }
300     }
301   }
302   dwNewObjNum = pDoc->AddIndirectObject(pClone);
303   (*pObjNumberMap)[dwObjnum] = dwNewObjNum;
304
305   if (!UpdateReference(pClone, pDoc, pObjNumberMap)) {
306     pClone->Release();
307     return 0;
308   }
309   return dwNewObjNum;
310 }
311
312 FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring,
313                                 CFX_WordArray* pageArray,
314                                 int nCount) {
315   if (rangstring.GetLength() != 0) {
316     rangstring.Remove(' ');
317     int nLength = rangstring.GetLength();
318     CFX_ByteString cbCompareString("0123456789-,");
319     for (int i = 0; i < nLength; ++i) {
320       if (cbCompareString.Find(rangstring[i]) == -1)
321         return FALSE;
322     }
323     CFX_ByteString cbMidRange;
324     int nStringFrom = 0;
325     int nStringTo = 0;
326     while (nStringTo < nLength) {
327       nStringTo = rangstring.Find(',', nStringFrom);
328       if (nStringTo == -1)
329         nStringTo = nLength;
330       cbMidRange = rangstring.Mid(nStringFrom, nStringTo - nStringFrom);
331       int nMid = cbMidRange.Find('-');
332       if (nMid == -1) {
333         long lPageNum = atol(cbMidRange);
334         if (lPageNum <= 0 || lPageNum > nCount)
335           return FALSE;
336         pageArray->Add((FX_WORD)lPageNum);
337       } else {
338         int nStartPageNum = atol(cbMidRange.Mid(0, nMid));
339         if (nStartPageNum == 0)
340           return FALSE;
341
342         ++nMid;
343         int nEnd = cbMidRange.GetLength() - nMid;
344         if (nEnd == 0)
345           return FALSE;
346
347         int nEndPageNum = atol(cbMidRange.Mid(nMid, nEnd));
348         if (nStartPageNum < 0 || nStartPageNum > nEndPageNum ||
349             nEndPageNum > nCount) {
350           return FALSE;
351         }
352         for (int i = nStartPageNum; i <= nEndPageNum; ++i) {
353           pageArray->Add(i);
354         }
355       }
356       nStringFrom = nStringTo + 1;
357     }
358   }
359   return TRUE;
360 }
361
362 DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
363                                              FPDF_DOCUMENT src_doc,
364                                              FPDF_BYTESTRING pagerange,
365                                              int index) {
366   CPDF_Document* pDestDoc = CPDFDocumentFromFPDFDocument(dest_doc);
367   if (!dest_doc)
368     return FALSE;
369
370   CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
371   if (!pSrcDoc)
372     return FALSE;
373
374   CFX_WordArray pageArray;
375   int nCount = pSrcDoc->GetPageCount();
376   if (pagerange) {
377     if (!ParserPageRangeString(pagerange, &pageArray, nCount))
378       return FALSE;
379   } else {
380     for (int i = 1; i <= nCount; ++i) {
381       pageArray.Add(i);
382     }
383   }
384
385   CPDF_PageOrganizer pageOrg;
386   pageOrg.PDFDocInit(pDestDoc, pSrcDoc);
387   return pageOrg.ExportPage(pSrcDoc, &pageArray, pDestDoc, index);
388 }
389
390 DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc,
391                                                        FPDF_DOCUMENT src_doc) {
392   CPDF_Document* pDstDoc = CPDFDocumentFromFPDFDocument(dest_doc);
393   if (!pDstDoc)
394     return FALSE;
395
396   CPDF_Document* pSrcDoc = CPDFDocumentFromFPDFDocument(src_doc);
397   if (!pSrcDoc)
398     return FALSE;
399
400   CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
401   pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));
402   if (!pSrcDict)
403     return FALSE;
404
405   CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
406   if (!pDstDict)
407     return FALSE;
408
409   pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE));
410   return TRUE;
411 }