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 "../../public/fpdf_ppo.h" 8#include "../include/fsdk_define.h" 9 10class CPDF_PageOrganizer 11{ 12public: 13 CPDF_PageOrganizer(); 14 ~CPDF_PageOrganizer(); 15 16public: 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 26CPDF_PageOrganizer::CPDF_PageOrganizer() 27{ 28 29} 30 31CPDF_PageOrganizer::~CPDF_PageOrganizer() 32{ 33 34} 35 36FX_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 90FX_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 191CPDF_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 224FX_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 294int 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 354FPDF_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 420DLLEXPORT 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 451DLLEXPORT 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 468