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