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