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