| // Copyright 2014 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "../include/fpdfppo.h" | |
| #include "../include/fsdk_define.h" | |
| class CPDF_PageOrganizer | |
| { | |
| public: | |
| CPDF_PageOrganizer(); | |
| ~CPDF_PageOrganizer(); | |
| public: | |
| FX_BOOL PDFDocInit(CPDF_Document *pDestPDFDoc, CPDF_Document *pSrcPDFDoc); | |
| FX_BOOL ExportPage(CPDF_Document *pSrcPDFDoc, CFX_WordArray* nPageNum, CPDF_Document *pDestPDFDoc, int nIndex); | |
| CPDF_Object* PageDictGetInheritableTag(CPDF_Dictionary *pDict, CFX_ByteString nSrctag); | |
| FX_BOOL UpdateReference(CPDF_Object *pObj, CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr); | |
| int GetNewObjId(CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr, CPDF_Reference *pRef); | |
| }; | |
| CPDF_PageOrganizer::CPDF_PageOrganizer() | |
| { | |
| } | |
| CPDF_PageOrganizer::~CPDF_PageOrganizer() | |
| { | |
| } | |
| FX_BOOL CPDF_PageOrganizer::PDFDocInit(CPDF_Document *pDestPDFDoc, CPDF_Document *pSrcPDFDoc) | |
| { | |
| if(!pDestPDFDoc || !pSrcPDFDoc) | |
| return false; | |
| CPDF_Dictionary* pNewRoot = pDestPDFDoc->GetRoot(); | |
| if(!pNewRoot) return FALSE; | |
| //Set the document information//////////////////////////////////////////// | |
| CPDF_Dictionary* DInfoDict = pDestPDFDoc->GetInfo(); | |
| if(!DInfoDict) | |
| return FALSE; | |
| CFX_ByteString producerstr; | |
| #ifdef FOXIT_CHROME_BUILD | |
| producerstr.Format("Google"); | |
| #else | |
| producerstr.Format("Foxit PDF SDK %s - Foxit Corporation", "2.0"); | |
| #endif | |
| DInfoDict->SetAt("Producer", new CPDF_String(producerstr)); | |
| //Set type//////////////////////////////////////////////////////////////// | |
| CFX_ByteString cbRootType = pNewRoot->GetString("Type",""); | |
| if( cbRootType.Equal("") ) | |
| { | |
| pNewRoot->SetAt("Type", new CPDF_Name("Catalog")); | |
| } | |
| CPDF_Dictionary* pNewPages = (CPDF_Dictionary*)pNewRoot->GetElement("Pages")->GetDirect(); | |
| if(!pNewPages) | |
| { | |
| pNewPages = new CPDF_Dictionary; | |
| FX_DWORD NewPagesON = pDestPDFDoc->AddIndirectObject(pNewPages); | |
| pNewRoot->SetAt("Pages", new CPDF_Reference(pDestPDFDoc, NewPagesON)); | |
| } | |
| CFX_ByteString cbPageType = pNewPages->GetString("Type",""); | |
| if(cbPageType.Equal("")) | |
| { | |
| pNewPages->SetAt("Type", new CPDF_Name("Pages")); | |
| } | |
| CPDF_Array* pKeysArray = pNewPages->GetArray("Kids"); | |
| if(pKeysArray == NULL) | |
| { | |
| CPDF_Array* pNewKids = new CPDF_Array; | |
| FX_DWORD Kidsobjnum = -1; | |
| Kidsobjnum = pDestPDFDoc->AddIndirectObject(pNewKids);//, Kidsobjnum, Kidsgennum); | |
| pNewPages->SetAt("Kids", new CPDF_Reference(pDestPDFDoc, Kidsobjnum));//, Kidsgennum)); | |
| pNewPages->SetAt("Count", new CPDF_Number(0)); | |
| } | |
| return true; | |
| } | |
| FX_BOOL CPDF_PageOrganizer::ExportPage(CPDF_Document *pSrcPDFDoc, CFX_WordArray* nPageNum, | |
| CPDF_Document *pDestPDFDoc,int nIndex) | |
| { | |
| int curpage =nIndex; | |
| CFX_MapPtrToPtr* pMapPtrToPtr = new CFX_MapPtrToPtr; | |
| pMapPtrToPtr->InitHashTable(1001); | |
| for(int i=0; i<nPageNum->GetSize(); i++) | |
| { | |
| CPDF_Dictionary* pCurPageDict = pDestPDFDoc->CreateNewPage(curpage); | |
| CPDF_Dictionary* pSrcPageDict = pSrcPDFDoc->GetPage(nPageNum->GetAt(i)-1); | |
| if(!pSrcPageDict || !pCurPageDict) | |
| { | |
| delete pMapPtrToPtr; | |
| return FALSE; | |
| } | |
| // Clone the page dictionary/////////// | |
| FX_POSITION SrcPos = pSrcPageDict->GetStartPos(); | |
| while (SrcPos) | |
| { | |
| CFX_ByteString cbSrcKeyStr; | |
| CPDF_Object* pObj = pSrcPageDict->GetNextElement(SrcPos, cbSrcKeyStr); | |
| if(cbSrcKeyStr.Compare(("Type")) && cbSrcKeyStr.Compare(("Parent"))) | |
| { | |
| if(pCurPageDict->KeyExist(cbSrcKeyStr)) | |
| pCurPageDict->RemoveAt(cbSrcKeyStr); | |
| pCurPageDict->SetAt(cbSrcKeyStr, pObj->Clone()); | |
| } | |
| } | |
| //inheritable item/////////////////////// | |
| CPDF_Object* pInheritable = NULL; | |
| //1 MediaBox //required | |
| if(!pCurPageDict->KeyExist("MediaBox")) | |
| { | |
| pInheritable = PageDictGetInheritableTag(pSrcPageDict, "MediaBox"); | |
| if(!pInheritable) | |
| { | |
| //Search the "CropBox" from source page dictionary, if not exists,we take the letter size. | |
| pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); | |
| if(pInheritable) | |
| pCurPageDict->SetAt("MediaBox", pInheritable->Clone()); | |
| else | |
| { | |
| //Make the default size to be letter size (8.5'x11') | |
| CPDF_Array* pArray = new CPDF_Array; | |
| pArray->AddNumber(0); | |
| pArray->AddNumber(0); | |
| pArray->AddNumber(612); | |
| pArray->AddNumber(792); | |
| pCurPageDict->SetAt("MediaBox", pArray); | |
| } | |
| } | |
| else | |
| pCurPageDict->SetAt("MediaBox", pInheritable->Clone()); | |
| } | |
| //2 Resources //required | |
| if(!pCurPageDict->KeyExist("Resources")) | |
| { | |
| pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Resources"); | |
| if(!pInheritable) | |
| { | |
| delete pMapPtrToPtr; | |
| return FALSE; | |
| } | |
| pCurPageDict->SetAt("Resources", pInheritable->Clone()); | |
| } | |
| //3 CropBox //Optional | |
| if(!pCurPageDict->KeyExist("CropBox")) | |
| { | |
| pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox"); | |
| if(pInheritable) | |
| pCurPageDict->SetAt("CropBox", pInheritable->Clone()); | |
| } | |
| //4 Rotate //Optional | |
| if(!pCurPageDict->KeyExist("Rotate")) | |
| { | |
| pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Rotate"); | |
| if(pInheritable) | |
| pCurPageDict->SetAt("Rotate", pInheritable->Clone()); | |
| } | |
| ///////////////////////////////////////////// | |
| //Update the reference | |
| FX_DWORD dwOldPageObj = pSrcPageDict->GetObjNum(); | |
| FX_DWORD dwNewPageObj = pCurPageDict->GetObjNum(); | |
| pMapPtrToPtr->SetAt((FX_LPVOID)(FX_UINTPTR)dwOldPageObj, (FX_LPVOID)(FX_UINTPTR)dwNewPageObj); | |
| this->UpdateReference(pCurPageDict, pDestPDFDoc, pMapPtrToPtr); | |
| curpage++; | |
| } | |
| delete pMapPtrToPtr; | |
| return TRUE; | |
| } | |
| CPDF_Object* CPDF_PageOrganizer::PageDictGetInheritableTag(CPDF_Dictionary *pDict, CFX_ByteString nSrctag) | |
| { | |
| if(!pDict || !pDict->KeyExist("Type") || nSrctag.IsEmpty()) | |
| return NULL; | |
| CPDF_Object* pType = pDict->GetElement("Type")->GetDirect(); | |
| if(!pType || pType->GetType() != PDFOBJ_NAME) return NULL; | |
| if(pType->GetString().Compare("Page")) return NULL; | |
| if(!pDict->KeyExist("Parent")) return NULL; | |
| CPDF_Object* pParent = pDict->GetElement("Parent")->GetDirect(); | |
| if(!pParent || pParent->GetType() != PDFOBJ_DICTIONARY) return NULL; | |
| CPDF_Dictionary* pp = (CPDF_Dictionary*)pParent; | |
| if(pDict->KeyExist((const char*)nSrctag)) | |
| return pDict->GetElement((const char*)nSrctag); | |
| while (pp) | |
| { | |
| if(pp->KeyExist((const char*)nSrctag)) | |
| return pp->GetElement((const char*)nSrctag); | |
| else if(pp->KeyExist("Parent")) | |
| pp = (CPDF_Dictionary*)pp->GetElement("Parent")->GetDirect(); | |
| else break; | |
| } | |
| return NULL; | |
| } | |
| FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object *pObj, CPDF_Document *pDoc, | |
| CFX_MapPtrToPtr* pMapPtrToPtr) | |
| { | |
| switch (pObj->GetType()) | |
| { | |
| case PDFOBJ_REFERENCE: | |
| { | |
| CPDF_Reference* pReference = (CPDF_Reference*)pObj; | |
| int newobjnum = GetNewObjId(pDoc, pMapPtrToPtr, pReference); | |
| if (newobjnum == 0) return FALSE; | |
| pReference->SetRef(pDoc, newobjnum);//, 0); | |
| break; | |
| } | |
| case PDFOBJ_DICTIONARY: | |
| { | |
| CPDF_Dictionary* pDict = (CPDF_Dictionary*)pObj; | |
| FX_POSITION pos = pDict->GetStartPos(); | |
| while(pos) | |
| { | |
| CFX_ByteString key(""); | |
| CPDF_Object* pNextObj = pDict->GetNextElement(pos, key); | |
| if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") || !FXSYS_strcmp(key, "First")) | |
| continue; | |
| if(pNextObj) | |
| { | |
| if(!UpdateReference(pNextObj, pDoc, pMapPtrToPtr)) | |
| pDict->RemoveAt(key); | |
| } | |
| else | |
| return FALSE; | |
| } | |
| break; | |
| } | |
| case PDFOBJ_ARRAY: | |
| { | |
| CPDF_Array* pArray = (CPDF_Array*)pObj; | |
| FX_DWORD count = pArray->GetCount(); | |
| for(FX_DWORD i = 0; i < count; i ++) | |
| { | |
| CPDF_Object* pNextObj = pArray->GetElement(i); | |
| if(pNextObj) | |
| { | |
| if(!UpdateReference(pNextObj, pDoc, pMapPtrToPtr)) | |
| return FALSE; | |
| } | |
| else | |
| return FALSE; | |
| } | |
| break; | |
| } | |
| case PDFOBJ_STREAM: | |
| { | |
| CPDF_Stream* pStream = (CPDF_Stream*)pObj; | |
| CPDF_Dictionary* pDict = pStream->GetDict(); | |
| if(pDict) | |
| { | |
| if(!UpdateReference(pDict, pDoc, pMapPtrToPtr)) | |
| return FALSE; | |
| } | |
| else | |
| return FALSE; | |
| break; | |
| } | |
| default: break; | |
| } | |
| return TRUE; | |
| } | |
| int CPDF_PageOrganizer::GetNewObjId(CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr, | |
| CPDF_Reference *pRef) | |
| { | |
| size_t dwObjnum = 0; | |
| if(!pRef) | |
| return 0; | |
| dwObjnum = pRef->GetRefObjNum(); | |
| size_t dwNewObjNum = 0; | |
| pMapPtrToPtr->Lookup((FX_LPVOID)dwObjnum, (FX_LPVOID&)dwNewObjNum); | |
| if(dwNewObjNum) | |
| { | |
| return (int)dwNewObjNum; | |
| } | |
| else | |
| { | |
| CPDF_Object* pClone = pRef->GetDirect()->Clone(); | |
| if(!pClone) return 0; | |
| if(pClone->GetType() == PDFOBJ_DICTIONARY) | |
| { | |
| CPDF_Dictionary* pDictClone = (CPDF_Dictionary*)pClone; | |
| if(pDictClone->KeyExist("Type")) | |
| { | |
| CFX_ByteString strType = pDictClone->GetString("Type"); | |
| if(!FXSYS_stricmp(strType, "Pages")) | |
| { | |
| pDictClone->Release(); | |
| return 4; | |
| } | |
| else if(!FXSYS_stricmp(strType, "Page")) | |
| { | |
| pDictClone->Release(); | |
| return 0; | |
| } | |
| } | |
| } | |
| dwNewObjNum = pDoc->AddIndirectObject(pClone);//, onum, gnum); | |
| pMapPtrToPtr->SetAt((FX_LPVOID)dwObjnum, (FX_LPVOID)dwNewObjNum); | |
| if(!UpdateReference(pClone, pDoc, pMapPtrToPtr)) | |
| { | |
| pClone->Release(); | |
| return 0; | |
| } | |
| return (int)dwNewObjNum; | |
| } | |
| return 0; | |
| } | |
| FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring, CFX_WordArray* pageArray,int nCount) | |
| { | |
| if(rangstring.GetLength() != 0) | |
| { | |
| rangstring.Remove(' '); | |
| int nLength = rangstring.GetLength(); | |
| CFX_ByteString cbCompareString("0123456789-,"); | |
| for(int i=0; i<nLength; i++) | |
| { | |
| if(cbCompareString.Find(rangstring[i]) == -1) | |
| return FALSE; | |
| } | |
| CFX_ByteString cbMidRange; | |
| int nStringFrom = 0; | |
| int nStringTo=0; | |
| while(nStringTo < nLength) | |
| { | |
| nStringTo = rangstring.Find(',',nStringFrom); | |
| if(nStringTo == -1) | |
| { | |
| nStringTo = nLength; | |
| } | |
| cbMidRange = rangstring.Mid(nStringFrom,nStringTo-nStringFrom); | |
| int nMid = cbMidRange.Find('-'); | |
| if(nMid == -1) | |
| { | |
| long lPageNum = atol(cbMidRange); | |
| if(lPageNum <= 0 || lPageNum > nCount) | |
| return FALSE; | |
| pageArray->Add((FX_WORD)lPageNum); | |
| } | |
| else | |
| { | |
| int nStartPageNum = atol(cbMidRange.Mid(0,nMid)); | |
| if (nStartPageNum ==0) | |
| { | |
| return FALSE; | |
| } | |
| nMid = nMid+1; | |
| int nEnd = cbMidRange.GetLength()-nMid; | |
| if(nEnd ==0)return FALSE; | |
| // int nEndPageNum = (nEnd == 0)?nCount:atol(cbMidRange.Mid(nMid,nEnd)); | |
| int nEndPageNum = atol(cbMidRange.Mid(nMid,nEnd)); | |
| if(nStartPageNum < 0 ||nStartPageNum >nEndPageNum|| nEndPageNum > nCount) | |
| { | |
| return FALSE; | |
| } | |
| else | |
| { | |
| for(int nIndex=nStartPageNum; nIndex <= nEndPageNum; nIndex ++) | |
| pageArray->Add(nIndex); | |
| } | |
| } | |
| nStringFrom = nStringTo +1; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,FPDF_DOCUMENT src_doc, | |
| FPDF_BYTESTRING pagerange, int index) | |
| { | |
| if(dest_doc == NULL || src_doc == NULL ) | |
| return FALSE; | |
| CFX_WordArray pageArray; | |
| CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc; | |
| int nCount = pSrcDoc->GetPageCount(); | |
| if(pagerange) | |
| { | |
| if(ParserPageRangeString(pagerange,&pageArray,nCount) == FALSE) | |
| return FALSE; | |
| } | |
| else | |
| { | |
| for(int i=1; i<=nCount; i++) | |
| { | |
| pageArray.Add(i); | |
| } | |
| } | |
| CPDF_Document* pDestDoc = (CPDF_Document*)dest_doc; | |
| CPDF_PageOrganizer pageOrg; | |
| pageOrg.PDFDocInit(pDestDoc,pSrcDoc); | |
| if(pageOrg.ExportPage(pSrcDoc,&pageArray,pDestDoc,index)) | |
| return TRUE; | |
| return FALSE; | |
| } | |
| DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc) | |
| { | |
| if(src_doc == NULL || dest_doc == NULL) | |
| return false; | |
| CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc; | |
| CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot(); | |
| pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));; | |
| if(!pSrcDict) | |
| return FALSE; | |
| CPDF_Document* pDstDoc = (CPDF_Document*)dest_doc; | |
| CPDF_Dictionary* pDstDict = pDstDoc->GetRoot(); | |
| if(!pDstDict) | |
| return FALSE; | |
| pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE)); | |
| return TRUE; | |
| } | |