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
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	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
90FX_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
191CPDF_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
224FX_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
294int	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
354FPDF_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
420DLLEXPORT 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
451DLLEXPORT 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