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