| 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 | 
|---|