CFX_MapByteStringToPtr considered harmful.
[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 ? (CPDF_Dictionary*)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 (!pType || pType->GetType() != PDFOBJ_NAME)
182     return nullptr;
183   if (pType->GetString().Compare("Page"))
184     return nullptr;
185
186   CPDF_Object* pParent = pDict->GetElement("Parent")->GetDirect();
187   if (!pParent || pParent->GetType() != PDFOBJ_DICTIONARY)
188     return nullptr;
189
190   if (pDict->KeyExist((const char*)nSrctag))
191     return pDict->GetElement((const char*)nSrctag);
192
193   CPDF_Dictionary* pp = (CPDF_Dictionary*)pParent;
194   while (pp) {
195     if (pp->KeyExist((const char*)nSrctag)) {
196       return pp->GetElement((const char*)nSrctag);
197     }
198     if (!pp->KeyExist("Parent")) {
199       break;
200     }
201     pp = (CPDF_Dictionary*)pp->GetElement("Parent")->GetDirect();
202     if (pp->GetType() == PDFOBJ_NULL) {
203       break;
204     }
205   }
206
207   return nullptr;
208 }
209
210 FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object* pObj,
211                                             CPDF_Document* pDoc,
212                                             ObjectNumberMap* pObjNumberMap) {
213   switch (pObj->GetType()) {
214     case PDFOBJ_REFERENCE: {
215       CPDF_Reference* pReference = (CPDF_Reference*)pObj;
216       FX_DWORD newobjnum = GetNewObjId(pDoc, pObjNumberMap, pReference);
217       if (newobjnum == 0)
218         return FALSE;
219       pReference->SetRef(pDoc, newobjnum);
220       break;
221     }
222     case PDFOBJ_DICTIONARY: {
223       CPDF_Dictionary* pDict = (CPDF_Dictionary*)pObj;
224
225       FX_POSITION pos = pDict->GetStartPos();
226       while (pos) {
227         CFX_ByteString key("");
228         CPDF_Object* pNextObj = pDict->GetNextElement(pos, key);
229         if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") ||
230             !FXSYS_strcmp(key, "First")) {
231           continue;
232         }
233         if (pNextObj) {
234           if (!UpdateReference(pNextObj, pDoc, pObjNumberMap))
235             pDict->RemoveAt(key);
236         } else {
237           return FALSE;
238         }
239       }
240       break;
241     }
242     case PDFOBJ_ARRAY: {
243       CPDF_Array* pArray = (CPDF_Array*)pObj;
244       FX_DWORD count = pArray->GetCount();
245       for (FX_DWORD i = 0; i < count; ++i) {
246         CPDF_Object* pNextObj = pArray->GetElement(i);
247         if (!pNextObj)
248           return FALSE;
249         if (!UpdateReference(pNextObj, pDoc, pObjNumberMap))
250           return FALSE;
251       }
252       break;
253     }
254     case PDFOBJ_STREAM: {
255       CPDF_Stream* pStream = (CPDF_Stream*)pObj;
256       CPDF_Dictionary* pDict = pStream->GetDict();
257       if (pDict) {
258         if (!UpdateReference(pDict, pDoc, pObjNumberMap))
259           return FALSE;
260       } else {
261         return FALSE;
262       }
263       break;
264     }
265     default:
266       break;
267   }
268
269   return TRUE;
270 }
271
272 FX_DWORD CPDF_PageOrganizer::GetNewObjId(CPDF_Document* pDoc,
273                                          ObjectNumberMap* pObjNumberMap,
274                                          CPDF_Reference* pRef) {
275   if (!pRef)
276     return 0;
277
278   FX_DWORD dwObjnum = pRef->GetRefObjNum();
279   FX_DWORD dwNewObjNum = 0;
280   const auto it = pObjNumberMap->find(dwObjnum);
281   if (it != pObjNumberMap->end())
282     dwNewObjNum = it->second;
283   if (dwNewObjNum)
284     return dwNewObjNum;
285
286   CPDF_Object* pDirect = pRef->GetDirect();
287   if (!pDirect)
288     return 0;
289
290   CPDF_Object* pClone = pDirect->Clone();
291   if (!pClone)
292     return 0;
293
294   if (pClone->GetType() == PDFOBJ_DICTIONARY) {
295     CPDF_Dictionary* pDictClone = (CPDF_Dictionary*)pClone;
296     if (pDictClone->KeyExist("Type")) {
297       CFX_ByteString strType = pDictClone->GetString("Type");
298       if (!FXSYS_stricmp(strType, "Pages")) {
299         pDictClone->Release();
300         return 4;
301       }
302       if (!FXSYS_stricmp(strType, "Page")) {
303         pDictClone->Release();
304         return 0;
305       }
306     }
307   }
308   dwNewObjNum = pDoc->AddIndirectObject(pClone);
309   (*pObjNumberMap)[dwObjnum] = dwNewObjNum;
310
311   if (!UpdateReference(pClone, pDoc, pObjNumberMap)) {
312     pClone->Release();
313     return 0;
314   }
315   return dwNewObjNum;
316 }
317
318 FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring,
319                                 CFX_WordArray* pageArray,
320                                 int nCount) {
321   if (rangstring.GetLength() != 0) {
322     rangstring.Remove(' ');
323     int nLength = rangstring.GetLength();
324     CFX_ByteString cbCompareString("0123456789-,");
325     for (int i = 0; i < nLength; ++i) {
326       if (cbCompareString.Find(rangstring[i]) == -1)
327         return FALSE;
328     }
329     CFX_ByteString cbMidRange;
330     int nStringFrom = 0;
331     int nStringTo = 0;
332     while (nStringTo < nLength) {
333       nStringTo = rangstring.Find(',', nStringFrom);
334       if (nStringTo == -1)
335         nStringTo = nLength;
336       cbMidRange = rangstring.Mid(nStringFrom, nStringTo - nStringFrom);
337       int nMid = cbMidRange.Find('-');
338       if (nMid == -1) {
339         long lPageNum = atol(cbMidRange);
340         if (lPageNum <= 0 || lPageNum > nCount)
341           return FALSE;
342         pageArray->Add((FX_WORD)lPageNum);
343       } else {
344         int nStartPageNum = atol(cbMidRange.Mid(0, nMid));
345         if (nStartPageNum == 0)
346           return FALSE;
347
348         ++nMid;
349         int nEnd = cbMidRange.GetLength() - nMid;
350         if (nEnd == 0)
351           return FALSE;
352
353         int nEndPageNum = atol(cbMidRange.Mid(nMid, nEnd));
354         if (nStartPageNum < 0 || nStartPageNum > nEndPageNum ||
355             nEndPageNum > nCount) {
356           return FALSE;
357         }
358         for (int i = nStartPageNum; i <= nEndPageNum; ++i) {
359           pageArray->Add(i);
360         }
361       }
362       nStringFrom = nStringTo + 1;
363     }
364   }
365   return TRUE;
366 }
367
368 DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,
369                                              FPDF_DOCUMENT src_doc,
370                                              FPDF_BYTESTRING pagerange,
371                                              int index) {
372   if (!dest_doc || !src_doc)
373     return FALSE;
374
375   CFX_WordArray pageArray;
376   CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
377   int nCount = pSrcDoc->GetPageCount();
378   if (pagerange) {
379     if (!ParserPageRangeString(pagerange, &pageArray, nCount))
380       return FALSE;
381   } else {
382     for (int i = 1; i <= nCount; ++i) {
383       pageArray.Add(i);
384     }
385   }
386
387   CPDF_Document* pDestDoc = (CPDF_Document*)dest_doc;
388   CPDF_PageOrganizer pageOrg;
389
390   pageOrg.PDFDocInit(pDestDoc, pSrcDoc);
391
392   return pageOrg.ExportPage(pSrcDoc, &pageArray, pDestDoc, index);
393 }
394
395 DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc,
396                                                        FPDF_DOCUMENT src_doc) {
397   if (!src_doc || !dest_doc)
398     return false;
399
400   CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
401   CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
402   pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));
403   ;
404   if (!pSrcDict)
405     return FALSE;
406
407   CPDF_Document* pDstDoc = (CPDF_Document*)dest_doc;
408   CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
409   if (!pDstDict)
410     return FALSE;
411   pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE));
412   return TRUE;
413 }