// 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; | |
} | |