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_transformpage.h"
8#include "../include/fsdk_define.h"
9
10DLLEXPORT void STDCALL FPDFPage_SetMediaBox(FPDF_PAGE page, float left, float bottom, float right, float top)
11{
12	if(!page)
13		return;
14
15	CPDF_Page* pPage = (CPDF_Page*)page;
16	CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
17	CPDF_Array* pMediaBoxArray = new CPDF_Array;
18	pMediaBoxArray->Add(new CPDF_Number(left));
19	pMediaBoxArray->Add(new CPDF_Number(bottom));
20	pMediaBoxArray->Add(new CPDF_Number(FX_FLOAT(right)));
21	pMediaBoxArray->Add(new CPDF_Number(FX_FLOAT(top)));
22	pPageDict->SetAt("MediaBox", pMediaBoxArray);
23}
24
25
26DLLEXPORT void STDCALL FPDFPage_SetCropBox(FPDF_PAGE page, float left, float bottom, float right, float top)
27{
28	if(!page)
29		return;
30
31	CPDF_Page* pPage = (CPDF_Page*)page;
32	CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
33	CPDF_Array* pCropBoxArray = new CPDF_Array;
34	pCropBoxArray->Add(new CPDF_Number(left));
35	pCropBoxArray->Add(new CPDF_Number(bottom));
36	pCropBoxArray->Add(new CPDF_Number(FX_FLOAT(right)));
37	pCropBoxArray->Add(new CPDF_Number(FX_FLOAT(top)));
38	pPageDict->SetAt("CropBox", pCropBoxArray);
39}
40
41
42DLLEXPORT FX_BOOL STDCALL FPDFPage_GetMediaBox(FPDF_PAGE page, float* left, float* bottom, float* right, float* top)
43{
44	if(!page)
45		return FALSE;
46	CPDF_Page* pPage = (CPDF_Page*)page;
47	CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
48	CPDF_Array* pArray = pPageDict->GetArray("MediaBox");
49	if(pArray)
50	{
51		*left = pArray->GetFloat(0);
52		*bottom = pArray->GetFloat(1);
53		*right = pArray->GetFloat(2);
54		*top = pArray->GetFloat(3);
55		return TRUE;
56	}
57	return FALSE;
58}
59
60DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetCropBox(FPDF_PAGE page, float* left, float* bottom, float* right, float* top)
61{
62	if(!page)
63		return FALSE;
64	CPDF_Page* pPage = (CPDF_Page*)page;
65	CPDF_Dictionary* pPageDict = pPage->m_pFormDict;
66	CPDF_Array* pArray = pPageDict->GetArray("CropBox");
67	if(pArray)
68	{
69		*left = pArray->GetFloat(0);
70		*bottom = pArray->GetFloat(1);
71		*right = pArray->GetFloat(2);
72		*top = pArray->GetFloat(3);
73		return TRUE;
74	}
75	return FALSE;
76}
77
78DLLEXPORT FPDF_BOOL STDCALL FPDFPage_TransFormWithClip(FPDF_PAGE page, FS_MATRIX* matrix, FS_RECTF* clipRect)
79{
80	if(!page)
81		return FALSE;
82
83	CFX_ByteTextBuf textBuf;
84	textBuf<<"q ";
85	CFX_FloatRect rect(clipRect->left, clipRect->bottom, clipRect->right, clipRect->top);
86	rect.Normalize();
87	CFX_ByteString bsClipping;
88	bsClipping.Format("%f %f %f %f re W* n ", rect.left, rect.bottom, rect.Width(), rect.Height());
89	textBuf<<bsClipping;
90
91	CFX_ByteString bsMatix;
92	bsMatix.Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b,matrix->c,matrix->d,matrix->e,matrix->f);
93	textBuf<<bsMatix;
94
95
96	CPDF_Page* pPage = (CPDF_Page*)page;
97	CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
98	CPDF_Object* pContentObj = pPageDic ? pPageDic->GetElement("Contents") : NULL;
99	if(!pContentObj)
100		pContentObj = pPageDic ? pPageDic->GetArray("Contents") : NULL;
101	if(!pContentObj)
102		return FALSE;
103
104	CPDF_Dictionary* pDic = new CPDF_Dictionary;
105	CPDF_Stream* pStream = new CPDF_Stream(NULL,0, pDic);
106	pStream->SetData(textBuf.GetBuffer(), textBuf.GetSize(), FALSE, FALSE);
107	CPDF_Document* pDoc = pPage->m_pDocument;
108	if(!pDoc)
109		return FALSE;
110	pDoc->AddIndirectObject(pStream);
111
112	pDic = new CPDF_Dictionary;
113	CPDF_Stream* pEndStream = new CPDF_Stream(NULL,0, pDic);
114	pEndStream->SetData((FX_LPCBYTE)" Q", 2, FALSE, FALSE);
115	pDoc->AddIndirectObject(pEndStream);
116
117	CPDF_Array* pContentArray = NULL;
118	if (pContentObj && pContentObj->GetType() == PDFOBJ_ARRAY)
119	{
120		pContentArray = (CPDF_Array*)pContentObj;
121		CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
122		pContentArray->InsertAt(0, pRef);
123		pContentArray->AddReference(pDoc,pEndStream);
124
125	}
126	else if(pContentObj && pContentObj->GetType() == PDFOBJ_REFERENCE)
127	{
128		CPDF_Reference* pReference = (CPDF_Reference*)pContentObj;
129		CPDF_Object* pDirectObj = pReference->GetDirect();
130		if(pDirectObj != NULL)
131		{
132			if(pDirectObj->GetType() == PDFOBJ_ARRAY)
133			{
134				pContentArray = (CPDF_Array*)pDirectObj;
135				CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
136				pContentArray->InsertAt(0, pRef);
137				pContentArray->AddReference(pDoc,pEndStream);
138			}
139			else if(pDirectObj->GetType() == PDFOBJ_STREAM)
140			{
141				pContentArray = new CPDF_Array();
142				pContentArray->AddReference(pDoc,pStream->GetObjNum());
143				pContentArray->AddReference(pDoc,pDirectObj->GetObjNum());
144				pContentArray->AddReference(pDoc, pEndStream);
145				pPageDic->SetAtReference("Contents", pDoc, pDoc->AddIndirectObject(pContentArray));
146			}
147		}
148	}
149
150	//Need to transform the patterns as well.
151	CPDF_Dictionary* pRes = pPageDic->GetDict(FX_BSTRC("Resources"));
152	if(pRes)
153	{
154		CPDF_Dictionary* pPattenDict = pRes->GetDict(FX_BSTRC("Pattern"));
155		if(pPattenDict)
156		{
157			FX_POSITION pos = pPattenDict->GetStartPos();
158			while(pos)
159			{
160				CPDF_Dictionary* pDict = NULL;
161				CFX_ByteString key;
162				CPDF_Object* pObj = pPattenDict->GetNextElement(pos, key);
163				if(pObj->GetType() == PDFOBJ_REFERENCE)
164					pObj = pObj->GetDirect();
165				if(pObj->GetType() == PDFOBJ_DICTIONARY)
166				{
167					pDict = (CPDF_Dictionary*)pObj;
168				}
169				else if(pObj->GetType() == PDFOBJ_STREAM)
170				{
171					pDict = ((CPDF_Stream*)pObj)->GetDict();
172				}
173				else
174					continue;
175
176				CFX_AffineMatrix m = pDict->GetMatrix(FX_BSTRC("Matrix"));
177				CFX_AffineMatrix t = *(CFX_AffineMatrix*)matrix;
178				m.Concat(t);
179				pDict->SetAtMatrix(FX_BSTRC("Matrix"), m);
180			}
181		}
182	}
183
184	return TRUE;
185}
186
187DLLEXPORT void STDCALL FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,double a, double b, double c, double d, double e, double f)
188{
189	CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
190	if(pPageObj == NULL)
191		return;
192	CFX_AffineMatrix matrix((FX_FLOAT)a,(FX_FLOAT)b,(FX_FLOAT)c,(FX_FLOAT)d,(FX_FLOAT)e,(FX_FLOAT)f);
193
194	//Special treatment to shading object, because the ClipPath for shading object is already transformed.
195	if(pPageObj->m_Type != PDFPAGE_SHADING)
196		pPageObj->TransformClipPath(matrix);
197	pPageObj->TransformGeneralState(matrix);
198}
199
200
201DLLEXPORT FPDF_CLIPPATH STDCALL FPDF_CreateClipPath(float left, float bottom, float right, float top)
202{
203	CPDF_ClipPath* pNewClipPath = new CPDF_ClipPath();
204	pNewClipPath->GetModify();
205	CPDF_Path Path;
206	Path.GetModify();
207	Path.AppendRect(left, bottom, right, top);
208	pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, FALSE);
209	return pNewClipPath;
210}
211
212DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath)
213{
214	if(clipPath)
215		delete (CPDF_ClipPath*)clipPath;
216}
217
218void OutputPath(CFX_ByteTextBuf& buf, CPDF_Path path)
219{
220	const CFX_PathData* pPathData = path;
221	if (pPathData == NULL) return;
222
223	FX_PATHPOINT* pPoints = pPathData->GetPoints();
224
225	if (path.IsRect()) {
226		buf << (pPoints[0].m_PointX) << " " << (pPoints[0].m_PointY) << " "
227			<< (pPoints[2].m_PointX - pPoints[0].m_PointX) << " "
228			<< (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re\n";
229		return;
230	}
231
232	CFX_ByteString temp;
233	for (int i = 0; i < pPathData->GetPointCount(); i ++) {
234		buf << (pPoints[i].m_PointX) << " " << (pPoints[i].m_PointY);
235		int point_type = pPoints[i].m_Flag & FXPT_TYPE;
236		if (point_type == FXPT_MOVETO)
237			buf << " m\n";
238		else if (point_type == FXPT_BEZIERTO) {
239			buf << " " << (pPoints[i+1].m_PointX) << " " << (pPoints[i+1].m_PointY) << " " <<
240				(pPoints[i+2].m_PointX) << " " << (pPoints[i+2].m_PointY);
241			if (pPoints[i+2].m_Flag & FXPT_CLOSEFIGURE)
242				buf << " c h\n";
243			else
244				buf << " c\n";
245			i += 2;
246		} else if (point_type == FXPT_LINETO) {
247			if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE)
248				buf << " l h\n";
249			else
250				buf << " l\n";
251		}
252	}
253}
254
255DLLEXPORT void STDCALL FPDFPage_InsertClipPath(FPDF_PAGE page,FPDF_CLIPPATH clipPath)
256{
257	if(!page)
258		return;
259	CPDF_Page* pPage = (CPDF_Page*)page;
260	CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
261	CPDF_Object* pContentObj = pPageDic ? pPageDic->GetElement("Contents") : NULL;
262	if(!pContentObj)
263		pContentObj = pPageDic ? pPageDic->GetArray("Contents") : NULL;
264	if(!pContentObj)
265		return;
266
267	CFX_ByteTextBuf strClip;
268	CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
269	FX_DWORD i;
270	for (i = 0; i < pClipPath->GetPathCount(); i ++) {
271		CPDF_Path path = pClipPath->GetPath(i);
272		int iClipType = pClipPath->GetClipType(i);
273		if (path.GetPointCount() == 0) {
274			// Empty clipping (totally clipped out)
275			strClip << "0 0 m W n ";
276		} else {
277			OutputPath(strClip, path);
278			if (iClipType == FXFILL_WINDING)
279				strClip << "W n\n";
280			else
281				strClip << "W* n\n";
282		}
283	}
284	CPDF_Dictionary* pDic = new CPDF_Dictionary;
285	CPDF_Stream* pStream = new CPDF_Stream(NULL,0, pDic);
286	pStream->SetData(strClip.GetBuffer(), strClip.GetSize(), FALSE, FALSE);
287	CPDF_Document* pDoc = pPage->m_pDocument;
288	if(!pDoc)
289		return;
290	pDoc->AddIndirectObject(pStream);
291
292	CPDF_Array* pContentArray = NULL;
293	if (pContentObj && pContentObj->GetType() == PDFOBJ_ARRAY)
294	{
295		pContentArray = (CPDF_Array*)pContentObj;
296		CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
297		pContentArray->InsertAt(0, pRef);
298	}
299	else if(pContentObj && pContentObj->GetType() == PDFOBJ_REFERENCE)
300	{
301		CPDF_Reference* pReference = (CPDF_Reference*)pContentObj;
302		CPDF_Object* pDirectObj = pReference->GetDirect();
303		if(pDirectObj != NULL)
304		{
305			if(pDirectObj->GetType() == PDFOBJ_ARRAY)
306			{
307				pContentArray = (CPDF_Array*)pDirectObj;
308				CPDF_Reference* pRef = new CPDF_Reference(pDoc, pStream->GetObjNum());
309				pContentArray->InsertAt(0, pRef);
310			}
311			else if(pDirectObj->GetType() == PDFOBJ_STREAM)
312			{
313				pContentArray = new CPDF_Array();
314				pContentArray->AddReference(pDoc,pStream->GetObjNum());
315				pContentArray->AddReference(pDoc,pDirectObj->GetObjNum());
316				pPageDic->SetAtReference("Contents", pDoc, pDoc->AddIndirectObject(pContentArray));
317			}
318		}
319	}
320}
321
322