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