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