Home | History | Annotate | Download | only in src
      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