Convert all line endings to LF.
[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")->GetDirect();
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                         pp = (CPDF_Dictionary*)pp->GetElement("Parent")->GetDirect();
220                 else break;
221         }
222         
223         return NULL;
224 }
225
226 FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object *pObj, CPDF_Document *pDoc, 
227                                                                                  CFX_MapPtrToPtr* pMapPtrToPtr)
228 {
229         switch (pObj->GetType())
230         {
231         case PDFOBJ_REFERENCE:
232                 {
233                         CPDF_Reference* pReference = (CPDF_Reference*)pObj;
234                         int newobjnum = GetNewObjId(pDoc, pMapPtrToPtr, pReference);
235                         if (newobjnum == 0) return FALSE;
236                         pReference->SetRef(pDoc, newobjnum);//, 0);
237                         break;
238                 }
239         case PDFOBJ_DICTIONARY:
240                 {
241                         CPDF_Dictionary* pDict = (CPDF_Dictionary*)pObj;
242                         
243                         FX_POSITION pos = pDict->GetStartPos();
244                         while(pos)
245                         {
246                                 CFX_ByteString key("");
247                                 CPDF_Object* pNextObj = pDict->GetNextElement(pos, key);
248                                 if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") || !FXSYS_strcmp(key, "First"))
249                                         continue;
250                                 if(pNextObj)
251                                 {
252                                         if(!UpdateReference(pNextObj, pDoc, pMapPtrToPtr))
253                                                 pDict->RemoveAt(key);
254                                 }
255                                 else
256                                         return FALSE;
257                         }
258                         break;
259                 }
260         case    PDFOBJ_ARRAY:
261                 {
262                         CPDF_Array* pArray = (CPDF_Array*)pObj;
263                         FX_DWORD count = pArray->GetCount();
264                         for(FX_DWORD i = 0; i < count; i ++)
265                         {
266                                 CPDF_Object* pNextObj = pArray->GetElement(i);
267                                 if(pNextObj)
268                                 {
269                                         if(!UpdateReference(pNextObj, pDoc, pMapPtrToPtr))
270                                                 return FALSE;
271                                 }
272                                 else
273                                         return FALSE;
274                         }
275                         break;
276                 }
277         case    PDFOBJ_STREAM:
278                 {
279                         CPDF_Stream* pStream = (CPDF_Stream*)pObj;
280                         CPDF_Dictionary* pDict = pStream->GetDict();
281                         if(pDict)
282                         {
283                                 if(!UpdateReference(pDict, pDoc, pMapPtrToPtr))
284                                         return FALSE;
285                         }
286                         else
287                                 return FALSE;
288                         break;
289                 }
290         default:        break;
291         }
292
293         return TRUE;
294 }
295
296 int     CPDF_PageOrganizer::GetNewObjId(CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr,
297                                                                         CPDF_Reference *pRef)
298 {
299         size_t dwObjnum = 0;
300         if(!pRef)
301                 return 0;
302         dwObjnum = pRef->GetRefObjNum();
303         
304         size_t dwNewObjNum = 0;
305         
306         pMapPtrToPtr->Lookup((FX_LPVOID)dwObjnum, (FX_LPVOID&)dwNewObjNum);
307         if(dwNewObjNum)
308         {
309                 return (int)dwNewObjNum;
310         }
311         else
312         {
313                 CPDF_Object* pClone  = pRef->GetDirect()->Clone();
314                 if(!pClone)                     return 0;
315                 
316                 if(pClone->GetType() == PDFOBJ_DICTIONARY)
317                 {
318                         CPDF_Dictionary* pDictClone = (CPDF_Dictionary*)pClone;
319                         if(pDictClone->KeyExist("Type"))
320                         {
321                                 CFX_ByteString strType = pDictClone->GetString("Type");
322                                 if(!FXSYS_stricmp(strType, "Pages"))
323                                 {
324                                         pDictClone->Release();
325                                         return 4;
326                                 }
327                                 else if(!FXSYS_stricmp(strType, "Page"))
328                                 {
329                                         pDictClone->Release();
330                                         return  0;
331                                 }
332                         }
333                 }
334                 dwNewObjNum = pDoc->AddIndirectObject(pClone);//, onum, gnum);
335                 pMapPtrToPtr->SetAt((FX_LPVOID)dwObjnum, (FX_LPVOID)dwNewObjNum);
336                 
337                 if(!UpdateReference(pClone, pDoc, pMapPtrToPtr))
338                 {
339                         pClone->Release();
340                         return 0;
341                 }
342                 return (int)dwNewObjNum;
343         }
344         return 0;
345 }
346
347 FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring, CFX_WordArray* pageArray,int nCount)
348 {
349
350         if(rangstring.GetLength() != 0)
351         {
352                 rangstring.Remove(' ');
353                 int nLength = rangstring.GetLength();
354                 CFX_ByteString cbCompareString("0123456789-,");
355                 for(int i=0; i<nLength; i++)
356                 {
357                         if(cbCompareString.Find(rangstring[i]) == -1)
358                                 return FALSE;
359                 }
360                 CFX_ByteString cbMidRange;
361                 int nStringFrom = 0;
362                 int nStringTo=0;
363                 while(nStringTo < nLength)
364                 {
365                         nStringTo = rangstring.Find(',',nStringFrom);
366                         if(nStringTo == -1)
367                         {
368                                 nStringTo = nLength;
369                         }
370                         cbMidRange = rangstring.Mid(nStringFrom,nStringTo-nStringFrom);
371                         
372                         int nMid = cbMidRange.Find('-');
373                         if(nMid == -1)
374                         {
375                                 long lPageNum = atol(cbMidRange);
376                                 if(lPageNum <= 0 || lPageNum > nCount)
377                                         return FALSE;
378                                 pageArray->Add((FX_WORD)lPageNum);
379                         }
380                         else
381                         {
382                                 int nStartPageNum = atol(cbMidRange.Mid(0,nMid));
383                                 if (nStartPageNum ==0)
384                                 {
385                                         return FALSE;
386                                 }
387
388
389                                 nMid = nMid+1;
390                                 int nEnd = cbMidRange.GetLength()-nMid;
391
392                                 if(nEnd ==0)return FALSE;
393                                 
394                                 //                              int nEndPageNum = (nEnd == 0)?nCount:atol(cbMidRange.Mid(nMid,nEnd));
395                                 int nEndPageNum = atol(cbMidRange.Mid(nMid,nEnd));
396                                 
397                                 if(nStartPageNum < 0 ||nStartPageNum >nEndPageNum|| nEndPageNum > nCount)
398                                 {
399                                         return FALSE;
400                                 }
401                                 else
402                                 {
403                                         for(int nIndex=nStartPageNum; nIndex <= nEndPageNum; nIndex ++)
404                                                 pageArray->Add(nIndex);
405                                 }
406                         }
407                         nStringFrom = nStringTo +1;
408                 }
409         }
410         return TRUE;
411 }
412
413 DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,FPDF_DOCUMENT src_doc, 
414                                                                                          FPDF_BYTESTRING pagerange, int index)
415 {
416         if(dest_doc == NULL || src_doc == NULL )
417                 return FALSE;
418         CFX_WordArray pageArray;
419         CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
420         int nCount = pSrcDoc->GetPageCount();
421         if(pagerange)
422         {
423                 if(ParserPageRangeString(pagerange,&pageArray,nCount) == FALSE)
424                         return FALSE;
425         }
426         else
427         {
428                 for(int i=1; i<=nCount; i++)
429                 {
430                         pageArray.Add(i);
431                 }
432         }
433         
434         CPDF_Document* pDestDoc = (CPDF_Document*)dest_doc;
435         CPDF_PageOrganizer pageOrg;
436
437         pageOrg.PDFDocInit(pDestDoc,pSrcDoc);
438
439         if(pageOrg.ExportPage(pSrcDoc,&pageArray,pDestDoc,index))
440                 return TRUE;
441         return FALSE;
442 }
443
444 DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc)
445 {
446         if(src_doc == NULL || dest_doc == NULL)
447                 return false;
448         CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
449         CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
450         pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));;
451         if(!pSrcDict)
452                 return FALSE;
453         CPDF_Document* pDstDoc = (CPDF_Document*)dest_doc;
454         CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
455         if(!pDstDict)
456                 return FALSE;
457         pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE));
458         return TRUE;
459 }
460