Add FX_OVERRIDE and use it for virtual functions of FX_FINAL classes.
[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* pClone  = pRef->GetDirect()->Clone();
317                 if(!pClone)                     
318                         return 0;
319                 
320                 if(pClone->GetType() == PDFOBJ_DICTIONARY)
321                 {
322                         CPDF_Dictionary* pDictClone = (CPDF_Dictionary*)pClone;
323                         if(pDictClone->KeyExist("Type"))
324                         {
325                                 CFX_ByteString strType = pDictClone->GetString("Type");
326                                 if(!FXSYS_stricmp(strType, "Pages"))
327                                 {
328                                         pDictClone->Release();
329                                         return 4;
330                                 }
331                                 else if(!FXSYS_stricmp(strType, "Page"))
332                                 {
333                                         pDictClone->Release();
334                                         return  0;
335                                 }
336                         }
337                 }
338                 dwNewObjNum = pDoc->AddIndirectObject(pClone);//, onum, gnum);
339                 pMapPtrToPtr->SetAt((FX_LPVOID)dwObjnum, (FX_LPVOID)dwNewObjNum);
340                 
341                 if(!UpdateReference(pClone, pDoc, pMapPtrToPtr))
342                 {
343                         pClone->Release();
344                         return 0;
345                 }
346                 return (int)dwNewObjNum;
347         }
348         return 0;
349 }
350
351 FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring, CFX_WordArray* pageArray,int nCount)
352 {
353
354         if(rangstring.GetLength() != 0)
355         {
356                 rangstring.Remove(' ');
357                 int nLength = rangstring.GetLength();
358                 CFX_ByteString cbCompareString("0123456789-,");
359                 for(int i=0; i<nLength; i++)
360                 {
361                         if(cbCompareString.Find(rangstring[i]) == -1)
362                                 return FALSE;
363                 }
364                 CFX_ByteString cbMidRange;
365                 int nStringFrom = 0;
366                 int nStringTo=0;
367                 while(nStringTo < nLength)
368                 {
369                         nStringTo = rangstring.Find(',',nStringFrom);
370                         if(nStringTo == -1)
371                         {
372                                 nStringTo = nLength;
373                         }
374                         cbMidRange = rangstring.Mid(nStringFrom,nStringTo-nStringFrom);
375                         
376                         int nMid = cbMidRange.Find('-');
377                         if(nMid == -1)
378                         {
379                                 long lPageNum = atol(cbMidRange);
380                                 if(lPageNum <= 0 || lPageNum > nCount)
381                                         return FALSE;
382                                 pageArray->Add((FX_WORD)lPageNum);
383                         }
384                         else
385                         {
386                                 int nStartPageNum = atol(cbMidRange.Mid(0,nMid));
387                                 if (nStartPageNum ==0)
388                                 {
389                                         return FALSE;
390                                 }
391
392
393                                 nMid = nMid+1;
394                                 int nEnd = cbMidRange.GetLength()-nMid;
395
396                                 if(nEnd ==0)return FALSE;
397                                 
398                                 //                              int nEndPageNum = (nEnd == 0)?nCount:atol(cbMidRange.Mid(nMid,nEnd));
399                                 int nEndPageNum = atol(cbMidRange.Mid(nMid,nEnd));
400                                 
401                                 if(nStartPageNum < 0 ||nStartPageNum >nEndPageNum|| nEndPageNum > nCount)
402                                 {
403                                         return FALSE;
404                                 }
405                                 else
406                                 {
407                                         for(int nIndex=nStartPageNum; nIndex <= nEndPageNum; nIndex ++)
408                                                 pageArray->Add(nIndex);
409                                 }
410                         }
411                         nStringFrom = nStringTo +1;
412                 }
413         }
414         return TRUE;
415 }
416
417 DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,FPDF_DOCUMENT src_doc, 
418                                                                                          FPDF_BYTESTRING pagerange, int index)
419 {
420         if(dest_doc == NULL || src_doc == NULL )
421                 return FALSE;
422         CFX_WordArray pageArray;
423         CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
424         int nCount = pSrcDoc->GetPageCount();
425         if(pagerange)
426         {
427                 if(ParserPageRangeString(pagerange,&pageArray,nCount) == FALSE)
428                         return FALSE;
429         }
430         else
431         {
432                 for(int i=1; i<=nCount; i++)
433                 {
434                         pageArray.Add(i);
435                 }
436         }
437         
438         CPDF_Document* pDestDoc = (CPDF_Document*)dest_doc;
439         CPDF_PageOrganizer pageOrg;
440
441         pageOrg.PDFDocInit(pDestDoc,pSrcDoc);
442
443         if(pageOrg.ExportPage(pSrcDoc,&pageArray,pDestDoc,index))
444                 return TRUE;
445         return FALSE;
446 }
447
448 DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc)
449 {
450         if(src_doc == NULL || dest_doc == NULL)
451                 return false;
452         CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
453         CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
454         pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));;
455         if(!pSrcDict)
456                 return FALSE;
457         CPDF_Document* pDstDoc = (CPDF_Document*)dest_doc;
458         CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
459         if(!pDstDict)
460                 return FALSE;
461         pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE));
462         return TRUE;
463 }
464