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