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