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