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
10class CPDF_PageOrganizer
11{
12public:
13	CPDF_PageOrganizer();
14	~CPDF_PageOrganizer();
15
16public:
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
26CPDF_PageOrganizer::CPDF_PageOrganizer()
27{
28
29}
30
31CPDF_PageOrganizer::~CPDF_PageOrganizer()
32{
33
34}
35
36FX_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
95FX_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
196CPDF_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
226FX_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
296int	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
347FPDF_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
413DLLEXPORT 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
444DLLEXPORT 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