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