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 #include "../include/fsdk_define.h"
      9 
     10 class CPDF_PageOrganizer
     11 {
     12 public:
     13 	CPDF_PageOrganizer();
     14 	~CPDF_PageOrganizer();
     15 
     16 public:
     17 	FX_BOOL				PDFDocInit(CPDF_Document *pDestPDFDoc, CPDF_Document *pSrcPDFDoc);
     18 	FX_BOOL				ExportPage(CPDF_Document *pSrcPDFDoc, CFX_WordArray* nPageNum, CPDF_Document *pDestPDFDoc, int nIndex);
     19 	CPDF_Object*		PageDictGetInheritableTag(CPDF_Dictionary *pDict, CFX_ByteString nSrctag);
     20 	FX_BOOL				UpdateReference(CPDF_Object *pObj, CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr);
     21 	int					GetNewObjId(CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr, CPDF_Reference *pRef);
     22 
     23 };
     24 
     25 
     26 CPDF_PageOrganizer::CPDF_PageOrganizer()
     27 {
     28 
     29 }
     30 
     31 CPDF_PageOrganizer::~CPDF_PageOrganizer()
     32 {
     33 
     34 }
     35 
     36 FX_BOOL CPDF_PageOrganizer::PDFDocInit(CPDF_Document *pDestPDFDoc, CPDF_Document *pSrcPDFDoc)
     37 {
     38 	if(!pDestPDFDoc || !pSrcPDFDoc)
     39 		return false;
     40 
     41 	CPDF_Dictionary* pNewRoot = pDestPDFDoc->GetRoot();
     42 	if(!pNewRoot)	return FALSE;
     43 
     44 	//Set the document information////////////////////////////////////////////
     45 
     46 	CPDF_Dictionary* DInfoDict = pDestPDFDoc->GetInfo();
     47 
     48 	if(!DInfoDict)
     49 		return FALSE;
     50 
     51 	CFX_ByteString producerstr;
     52 	producerstr.Format("PDFium");
     53 	DInfoDict->SetAt("Producer", new CPDF_String(producerstr));
     54 
     55 	//Set type////////////////////////////////////////////////////////////////
     56 	CFX_ByteString cbRootType = pNewRoot->GetString("Type","");
     57 	if( cbRootType.Equal("") )
     58 	{
     59 		pNewRoot->SetAt("Type", new CPDF_Name("Catalog"));
     60 	}
     61 
     62 	CPDF_Dictionary* pNewPages = (CPDF_Dictionary*)(pNewRoot->GetElement("Pages")? pNewRoot->GetElement("Pages")->GetDirect() : NULL);
     63 	if(!pNewPages)
     64 	{
     65 		pNewPages = new CPDF_Dictionary;
     66 		FX_DWORD NewPagesON = pDestPDFDoc->AddIndirectObject(pNewPages);
     67 		pNewRoot->SetAt("Pages", new CPDF_Reference(pDestPDFDoc, NewPagesON));
     68 	}
     69 
     70 	CFX_ByteString cbPageType = pNewPages->GetString("Type","");
     71 	if(cbPageType.Equal(""))
     72 	{
     73 		pNewPages->SetAt("Type", new CPDF_Name("Pages"));
     74 	}
     75 
     76 	CPDF_Array* pKeysArray = pNewPages->GetArray("Kids");
     77 	if(pKeysArray == NULL)
     78 	{
     79 		CPDF_Array* pNewKids = new CPDF_Array;
     80 		FX_DWORD Kidsobjnum = -1;
     81 		Kidsobjnum = pDestPDFDoc->AddIndirectObject(pNewKids);//, Kidsobjnum, Kidsgennum);
     82 
     83 		pNewPages->SetAt("Kids", new CPDF_Reference(pDestPDFDoc, Kidsobjnum));//, Kidsgennum));
     84 		pNewPages->SetAt("Count", new CPDF_Number(0));
     85 	}
     86 
     87 	return true;
     88 }
     89 
     90 FX_BOOL CPDF_PageOrganizer::ExportPage(CPDF_Document *pSrcPDFDoc, CFX_WordArray* nPageNum,
     91 												CPDF_Document *pDestPDFDoc,int nIndex)
     92 {
     93 	int curpage =nIndex;
     94 
     95 	CFX_MapPtrToPtr* pMapPtrToPtr = new CFX_MapPtrToPtr;
     96 	pMapPtrToPtr->InitHashTable(1001);
     97 
     98 	for(int i=0; i<nPageNum->GetSize(); i++)
     99 	{
    100 
    101 		CPDF_Dictionary* pCurPageDict = pDestPDFDoc->CreateNewPage(curpage);
    102 		CPDF_Dictionary* pSrcPageDict = pSrcPDFDoc->GetPage(nPageNum->GetAt(i)-1);
    103 		if(!pSrcPageDict || !pCurPageDict)
    104 		{
    105 			delete pMapPtrToPtr;
    106 			return FALSE;
    107 		}
    108 
    109 		// Clone the page dictionary///////////
    110 		FX_POSITION	SrcPos = pSrcPageDict->GetStartPos();
    111 		while (SrcPos)
    112 		{
    113 			CFX_ByteString cbSrcKeyStr;
    114 			CPDF_Object* pObj = pSrcPageDict->GetNextElement(SrcPos, cbSrcKeyStr);
    115 			if(cbSrcKeyStr.Compare(("Type")) && cbSrcKeyStr.Compare(("Parent")))
    116 			{
    117 				if(pCurPageDict->KeyExist(cbSrcKeyStr))
    118 					pCurPageDict->RemoveAt(cbSrcKeyStr);
    119 				pCurPageDict->SetAt(cbSrcKeyStr, pObj->Clone());
    120 			}
    121 		}
    122 
    123 		//inheritable item///////////////////////
    124 		CPDF_Object* pInheritable = NULL;
    125 		//1	MediaBox  //required
    126 		if(!pCurPageDict->KeyExist("MediaBox"))
    127 		{
    128 
    129 			pInheritable = PageDictGetInheritableTag(pSrcPageDict, "MediaBox");
    130 			if(!pInheritable)
    131 			{
    132 				//Search the "CropBox" from source page dictionary, if not exists,we take the letter size.
    133 				pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox");
    134 				if(pInheritable)
    135 					pCurPageDict->SetAt("MediaBox", pInheritable->Clone());
    136 				else
    137 				{
    138 					//Make the default size to be letter size (8.5'x11')
    139 					CPDF_Array* pArray = new CPDF_Array;
    140 					pArray->AddNumber(0);
    141 					pArray->AddNumber(0);
    142 					pArray->AddNumber(612);
    143 					pArray->AddNumber(792);
    144 					pCurPageDict->SetAt("MediaBox", pArray);
    145 				}
    146 			}
    147 			else
    148 				pCurPageDict->SetAt("MediaBox", pInheritable->Clone());
    149 		}
    150 		//2 Resources //required
    151 		if(!pCurPageDict->KeyExist("Resources"))
    152 		{
    153 			pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Resources");
    154 			if(!pInheritable)
    155 			{
    156 				delete pMapPtrToPtr;
    157 				return FALSE;
    158 			}
    159 			pCurPageDict->SetAt("Resources", pInheritable->Clone());
    160 		}
    161 		//3 CropBox  //Optional
    162 		if(!pCurPageDict->KeyExist("CropBox"))
    163 		{
    164 			pInheritable = PageDictGetInheritableTag(pSrcPageDict, "CropBox");
    165 			if(pInheritable)
    166 				pCurPageDict->SetAt("CropBox", pInheritable->Clone());
    167 		}
    168 		//4 Rotate  //Optional
    169 		if(!pCurPageDict->KeyExist("Rotate"))
    170 		{
    171 			pInheritable = PageDictGetInheritableTag(pSrcPageDict, "Rotate");
    172 			if(pInheritable)
    173 				pCurPageDict->SetAt("Rotate", pInheritable->Clone());
    174 		}
    175 
    176 		/////////////////////////////////////////////
    177 		//Update the reference
    178 		FX_DWORD dwOldPageObj = pSrcPageDict->GetObjNum();
    179 		FX_DWORD dwNewPageObj = pCurPageDict->GetObjNum();
    180 
    181 		pMapPtrToPtr->SetAt((FX_LPVOID)(FX_UINTPTR)dwOldPageObj, (FX_LPVOID)(FX_UINTPTR)dwNewPageObj);
    182 
    183 		this->UpdateReference(pCurPageDict, pDestPDFDoc, pMapPtrToPtr);
    184 		curpage++;
    185 	}
    186 
    187 	delete pMapPtrToPtr;
    188 	return TRUE;
    189 }
    190 
    191 CPDF_Object* CPDF_PageOrganizer::PageDictGetInheritableTag(CPDF_Dictionary *pDict, CFX_ByteString nSrctag)
    192 {
    193 	if(!pDict || !pDict->KeyExist("Type") || nSrctag.IsEmpty())
    194 		return NULL;
    195 
    196 	CPDF_Object* pType = pDict->GetElement("Type")->GetDirect();
    197 	if(!pType || pType->GetType() != PDFOBJ_NAME)	return NULL;
    198 
    199 	if(pType->GetString().Compare("Page"))	return NULL;
    200 
    201 	if(!pDict->KeyExist("Parent"))	return NULL;
    202 	CPDF_Object* pParent = pDict->GetElement("Parent")->GetDirect();
    203 	if(!pParent || pParent->GetType() != PDFOBJ_DICTIONARY)	return NULL;
    204 
    205 	CPDF_Dictionary* pp = (CPDF_Dictionary*)pParent;
    206 
    207 	if(pDict->KeyExist((const char*)nSrctag))
    208 		return pDict->GetElement((const char*)nSrctag);
    209 	while (pp)
    210 	{
    211 		if(pp->KeyExist((const char*)nSrctag))
    212 			return pp->GetElement((const char*)nSrctag);
    213 		else if (pp->KeyExist("Parent"))
    214 		{
    215 			pp = (CPDF_Dictionary*)pp->GetElement("Parent")->GetDirect();
    216 			if (pp->GetType() == PDFOBJ_NULL) break;
    217 		}
    218 		else break;
    219 	}
    220 
    221 	return NULL;
    222 }
    223 
    224 FX_BOOL CPDF_PageOrganizer::UpdateReference(CPDF_Object *pObj, CPDF_Document *pDoc,
    225 										 CFX_MapPtrToPtr* pMapPtrToPtr)
    226 {
    227 	switch (pObj->GetType())
    228 	{
    229 	case PDFOBJ_REFERENCE:
    230 		{
    231 			CPDF_Reference* pReference = (CPDF_Reference*)pObj;
    232 			int newobjnum = GetNewObjId(pDoc, pMapPtrToPtr, pReference);
    233 			if (newobjnum == 0) return FALSE;
    234 			pReference->SetRef(pDoc, newobjnum);//, 0);
    235 			break;
    236 		}
    237 	case PDFOBJ_DICTIONARY:
    238 		{
    239 			CPDF_Dictionary* pDict = (CPDF_Dictionary*)pObj;
    240 
    241 			FX_POSITION pos = pDict->GetStartPos();
    242 			while(pos)
    243 			{
    244 				CFX_ByteString key("");
    245 				CPDF_Object* pNextObj = pDict->GetNextElement(pos, key);
    246 				if (!FXSYS_strcmp(key, "Parent") || !FXSYS_strcmp(key, "Prev") || !FXSYS_strcmp(key, "First"))
    247 					continue;
    248 				if(pNextObj)
    249 				{
    250 					if(!UpdateReference(pNextObj, pDoc, pMapPtrToPtr))
    251 						pDict->RemoveAt(key);
    252 				}
    253 				else
    254 					return FALSE;
    255 			}
    256 			break;
    257 		}
    258 	case	PDFOBJ_ARRAY:
    259 		{
    260 			CPDF_Array* pArray = (CPDF_Array*)pObj;
    261 			FX_DWORD count = pArray->GetCount();
    262 			for(FX_DWORD i = 0; i < count; i ++)
    263 			{
    264 				CPDF_Object* pNextObj = pArray->GetElement(i);
    265 				if(pNextObj)
    266 				{
    267 					if(!UpdateReference(pNextObj, pDoc, pMapPtrToPtr))
    268 						return FALSE;
    269 				}
    270 				else
    271 					return FALSE;
    272 			}
    273 			break;
    274 		}
    275 	case	PDFOBJ_STREAM:
    276 		{
    277 			CPDF_Stream* pStream = (CPDF_Stream*)pObj;
    278 			CPDF_Dictionary* pDict = pStream->GetDict();
    279 			if(pDict)
    280 			{
    281 				if(!UpdateReference(pDict, pDoc, pMapPtrToPtr))
    282 					return FALSE;
    283 			}
    284 			else
    285 				return FALSE;
    286 			break;
    287 		}
    288 	default:	break;
    289 	}
    290 
    291 	return TRUE;
    292 }
    293 
    294 int	CPDF_PageOrganizer::GetNewObjId(CPDF_Document *pDoc, CFX_MapPtrToPtr* pMapPtrToPtr,
    295 									CPDF_Reference *pRef)
    296 {
    297 	size_t dwObjnum = 0;
    298 	if(!pRef)
    299 		return 0;
    300 	dwObjnum = pRef->GetRefObjNum();
    301 
    302 	size_t dwNewObjNum = 0;
    303 
    304 	pMapPtrToPtr->Lookup((FX_LPVOID)dwObjnum, (FX_LPVOID&)dwNewObjNum);
    305 	if(dwNewObjNum)
    306 	{
    307 		return (int)dwNewObjNum;
    308 	}
    309 	else
    310 	{
    311 		CPDF_Object* pDirect = pRef->GetDirect();
    312 		if(!pDirect)
    313 		{
    314 			return 0;
    315 		}
    316 
    317 		CPDF_Object* pClone = pDirect->Clone();
    318 		if(!pClone)
    319 		{
    320 			return 0;
    321 		}
    322 
    323 		if(pClone->GetType() == PDFOBJ_DICTIONARY)
    324 		{
    325 			CPDF_Dictionary* pDictClone = (CPDF_Dictionary*)pClone;
    326 			if(pDictClone->KeyExist("Type"))
    327 			{
    328 				CFX_ByteString strType = pDictClone->GetString("Type");
    329 				if(!FXSYS_stricmp(strType, "Pages"))
    330 				{
    331 					pDictClone->Release();
    332 					return 4;
    333 				}
    334 				else if(!FXSYS_stricmp(strType, "Page"))
    335 				{
    336 					pDictClone->Release();
    337 					return  0;
    338 				}
    339 			}
    340 		}
    341 		dwNewObjNum = pDoc->AddIndirectObject(pClone);//, onum, gnum);
    342 		pMapPtrToPtr->SetAt((FX_LPVOID)dwObjnum, (FX_LPVOID)dwNewObjNum);
    343 
    344 		if(!UpdateReference(pClone, pDoc, pMapPtrToPtr))
    345 		{
    346 			pClone->Release();
    347 			return 0;
    348 		}
    349 		return (int)dwNewObjNum;
    350 	}
    351 	return 0;
    352 }
    353 
    354 FPDF_BOOL ParserPageRangeString(CFX_ByteString rangstring, CFX_WordArray* pageArray,int nCount)
    355 {
    356 
    357 	if(rangstring.GetLength() != 0)
    358 	{
    359 		rangstring.Remove(' ');
    360 		int nLength = rangstring.GetLength();
    361 		CFX_ByteString cbCompareString("0123456789-,");
    362 		for(int i=0; i<nLength; i++)
    363 		{
    364 			if(cbCompareString.Find(rangstring[i]) == -1)
    365 				return FALSE;
    366 		}
    367 		CFX_ByteString cbMidRange;
    368 		int nStringFrom = 0;
    369 		int nStringTo=0;
    370 		while(nStringTo < nLength)
    371 		{
    372 			nStringTo = rangstring.Find(',',nStringFrom);
    373 			if(nStringTo == -1)
    374 			{
    375 				nStringTo = nLength;
    376 			}
    377 			cbMidRange = rangstring.Mid(nStringFrom,nStringTo-nStringFrom);
    378 
    379 			int nMid = cbMidRange.Find('-');
    380 			if(nMid == -1)
    381 			{
    382 				long lPageNum = atol(cbMidRange);
    383 				if(lPageNum <= 0 || lPageNum > nCount)
    384 					return FALSE;
    385 				pageArray->Add((FX_WORD)lPageNum);
    386 			}
    387 			else
    388 			{
    389 				int nStartPageNum = atol(cbMidRange.Mid(0,nMid));
    390 				if (nStartPageNum ==0)
    391 				{
    392 					return FALSE;
    393 				}
    394 
    395 
    396 				nMid = nMid+1;
    397 				int nEnd = cbMidRange.GetLength()-nMid;
    398 
    399 				if(nEnd ==0)return FALSE;
    400 
    401 				//				int nEndPageNum = (nEnd == 0)?nCount:atol(cbMidRange.Mid(nMid,nEnd));
    402 				int nEndPageNum = atol(cbMidRange.Mid(nMid,nEnd));
    403 
    404 				if(nStartPageNum < 0 ||nStartPageNum >nEndPageNum|| nEndPageNum > nCount)
    405 				{
    406 					return FALSE;
    407 				}
    408 				else
    409 				{
    410 					for(int nIndex=nStartPageNum; nIndex <= nEndPageNum; nIndex ++)
    411 						pageArray->Add(nIndex);
    412 				}
    413 			}
    414 			nStringFrom = nStringTo +1;
    415 		}
    416 	}
    417 	return TRUE;
    418 }
    419 
    420 DLLEXPORT FPDF_BOOL STDCALL FPDF_ImportPages(FPDF_DOCUMENT dest_doc,FPDF_DOCUMENT src_doc,
    421 											 FPDF_BYTESTRING pagerange, int index)
    422 {
    423 	if(dest_doc == NULL || src_doc == NULL )
    424 		return FALSE;
    425 	CFX_WordArray pageArray;
    426 	CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
    427 	int nCount = pSrcDoc->GetPageCount();
    428 	if(pagerange)
    429 	{
    430 		if(ParserPageRangeString(pagerange,&pageArray,nCount) == FALSE)
    431 			return FALSE;
    432 	}
    433 	else
    434 	{
    435 		for(int i=1; i<=nCount; i++)
    436 		{
    437 			pageArray.Add(i);
    438 		}
    439 	}
    440 
    441 	CPDF_Document* pDestDoc = (CPDF_Document*)dest_doc;
    442 	CPDF_PageOrganizer pageOrg;
    443 
    444 	pageOrg.PDFDocInit(pDestDoc,pSrcDoc);
    445 
    446 	if(pageOrg.ExportPage(pSrcDoc,&pageArray,pDestDoc,index))
    447 		return TRUE;
    448 	return FALSE;
    449 }
    450 
    451 DLLEXPORT FPDF_BOOL STDCALL FPDF_CopyViewerPreferences(FPDF_DOCUMENT dest_doc, FPDF_DOCUMENT src_doc)
    452 {
    453 	if(src_doc == NULL || dest_doc == NULL)
    454 		return false;
    455 	CPDF_Document* pSrcDoc = (CPDF_Document*)src_doc;
    456 	CPDF_Dictionary* pSrcDict = pSrcDoc->GetRoot();
    457 	pSrcDict = pSrcDict->GetDict(FX_BSTRC("ViewerPreferences"));;
    458 	if(!pSrcDict)
    459 		return FALSE;
    460 	CPDF_Document* pDstDoc = (CPDF_Document*)dest_doc;
    461 	CPDF_Dictionary* pDstDict = pDstDoc->GetRoot();
    462 	if(!pDstDict)
    463 		return FALSE;
    464 	pDstDict->SetAt(FX_BSTRC("ViewerPreferences"), pSrcDict->Clone(TRUE));
    465 	return TRUE;
    466 }
    467 
    468