// 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")? pNewRoot->GetElement("Pages")->GetDirect() : NULL); 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(); if (pp->GetType() == PDFOBJ_NULL) break; } 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* pDirect = pRef->GetDirect(); if(!pDirect) { return 0; } CPDF_Object* pClone = pDirect->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; }