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