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