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 "constants/page_object.h"
14 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
15 #include "core/fpdfapi/page/cpdf_clippath.h"
16 #include "core/fpdfapi/page/cpdf_page.h"
17 #include "core/fpdfapi/page/cpdf_pageobject.h"
18 #include "core/fpdfapi/page/cpdf_path.h"
19 #include "core/fpdfapi/parser/cpdf_array.h"
20 #include "core/fpdfapi/parser/cpdf_dictionary.h"
21 #include "core/fpdfapi/parser/cpdf_document.h"
22 #include "core/fpdfapi/parser/cpdf_number.h"
23 #include "core/fpdfapi/parser/cpdf_reference.h"
24 #include "core/fpdfapi/parser/cpdf_stream.h"
25 #include "core/fxge/cfx_pathdata.h"
26 #include "core/fxge/render_defines.h"
27 #include "fpdfsdk/cpdfsdk_helpers.h"
28 #include "third_party/base/ptr_util.h"
29
30 namespace {
31
SetBoundingBox(CPDF_Page * page,const ByteString & key,const CFX_FloatRect & rect)32 void SetBoundingBox(CPDF_Page* page,
33 const ByteString& key,
34 const CFX_FloatRect& rect) {
35 if (!page)
36 return;
37
38 page->GetDict()->SetRectFor(key, rect);
39 page->UpdateDimensions();
40 }
41
GetBoundingBox(CPDF_Page * page,const ByteString & key,float * left,float * bottom,float * right,float * top)42 bool GetBoundingBox(CPDF_Page* page,
43 const ByteString& key,
44 float* left,
45 float* bottom,
46 float* right,
47 float* top) {
48 if (!page || !left || !bottom || !right || !top)
49 return false;
50
51 CPDF_Array* pArray = page->GetDict()->GetArrayFor(key);
52 if (!pArray)
53 return false;
54
55 *left = pArray->GetNumberAt(0);
56 *bottom = pArray->GetNumberAt(1);
57 *right = pArray->GetNumberAt(2);
58 *top = pArray->GetNumberAt(3);
59 return true;
60 }
61
GetPageContent(CPDF_Dictionary * pPageDict)62 CPDF_Object* GetPageContent(CPDF_Dictionary* pPageDict) {
63 return pPageDict->GetDirectObjectFor(pdfium::page_object::kContents);
64 }
65
OutputPath(std::ostringstream & buf,CPDF_Path path)66 void OutputPath(std::ostringstream& buf, CPDF_Path path) {
67 const CFX_PathData* pPathData = path.GetObject();
68 if (!pPathData)
69 return;
70
71 const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
72 if (path.IsRect()) {
73 CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point;
74 buf << pPoints[0].m_Point.x << " " << pPoints[0].m_Point.y << " " << diff.x
75 << " " << diff.y << " re\n";
76 return;
77 }
78
79 ByteString temp;
80 for (size_t i = 0; i < pPoints.size(); i++) {
81 buf << pPoints[i].m_Point.x << " " << pPoints[i].m_Point.y;
82 FXPT_TYPE point_type = pPoints[i].m_Type;
83 if (point_type == FXPT_TYPE::MoveTo) {
84 buf << " m\n";
85 } else if (point_type == FXPT_TYPE::BezierTo) {
86 buf << " " << pPoints[i + 1].m_Point.x << " " << pPoints[i + 1].m_Point.y
87 << " " << pPoints[i + 2].m_Point.x << " " << pPoints[i + 2].m_Point.y;
88 buf << " c";
89 if (pPoints[i + 2].m_CloseFigure)
90 buf << " h";
91 buf << "\n";
92
93 i += 2;
94 } else if (point_type == FXPT_TYPE::LineTo) {
95 buf << " l";
96 if (pPoints[i].m_CloseFigure)
97 buf << " h";
98 buf << "\n";
99 }
100 }
101 }
102
103 } // namespace
104
FPDFPage_SetMediaBox(FPDF_PAGE page,float left,float bottom,float right,float top)105 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page,
106 float left,
107 float bottom,
108 float right,
109 float top) {
110 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kMediaBox,
111 CFX_FloatRect(left, bottom, right, top));
112 }
113
FPDFPage_SetCropBox(FPDF_PAGE page,float left,float bottom,float right,float top)114 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page,
115 float left,
116 float bottom,
117 float right,
118 float top) {
119 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kCropBox,
120 CFX_FloatRect(left, bottom, right, top));
121 }
122
FPDFPage_SetBleedBox(FPDF_PAGE page,float left,float bottom,float right,float top)123 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetBleedBox(FPDF_PAGE page,
124 float left,
125 float bottom,
126 float right,
127 float top) {
128 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kBleedBox,
129 CFX_FloatRect(left, bottom, right, top));
130 }
131
FPDFPage_SetTrimBox(FPDF_PAGE page,float left,float bottom,float right,float top)132 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetTrimBox(FPDF_PAGE page,
133 float left,
134 float bottom,
135 float right,
136 float top) {
137 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kTrimBox,
138 CFX_FloatRect(left, bottom, right, top));
139 }
140
FPDFPage_SetArtBox(FPDF_PAGE page,float left,float bottom,float right,float top)141 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetArtBox(FPDF_PAGE page,
142 float left,
143 float bottom,
144 float right,
145 float top) {
146 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kArtBox,
147 CFX_FloatRect(left, bottom, right, top));
148 }
149
FPDFPage_GetMediaBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)150 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page,
151 float* left,
152 float* bottom,
153 float* right,
154 float* top) {
155 return GetBoundingBox(CPDFPageFromFPDFPage(page),
156 pdfium::page_object::kMediaBox, left, bottom, right,
157 top);
158 }
159
FPDFPage_GetCropBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)160 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page,
161 float* left,
162 float* bottom,
163 float* right,
164 float* top) {
165 return GetBoundingBox(CPDFPageFromFPDFPage(page),
166 pdfium::page_object::kCropBox, left, bottom, right,
167 top);
168 }
169
FPDFPage_GetBleedBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)170 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetBleedBox(FPDF_PAGE page,
171 float* left,
172 float* bottom,
173 float* right,
174 float* top) {
175 return GetBoundingBox(CPDFPageFromFPDFPage(page),
176 pdfium::page_object::kBleedBox, left, bottom, right,
177 top);
178 }
179
FPDFPage_GetTrimBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)180 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetTrimBox(FPDF_PAGE page,
181 float* left,
182 float* bottom,
183 float* right,
184 float* top) {
185 return GetBoundingBox(CPDFPageFromFPDFPage(page),
186 pdfium::page_object::kTrimBox, left, bottom, right,
187 top);
188 }
189
FPDFPage_GetArtBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)190 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetArtBox(FPDF_PAGE page,
191 float* left,
192 float* bottom,
193 float* right,
194 float* top) {
195 return GetBoundingBox(CPDFPageFromFPDFPage(page),
196 pdfium::page_object::kArtBox, left, bottom, right, top);
197 }
198
199 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFPage_TransFormWithClip(FPDF_PAGE page,const FS_MATRIX * matrix,const FS_RECTF * clipRect)200 FPDFPage_TransFormWithClip(FPDF_PAGE page,
201 const FS_MATRIX* matrix,
202 const FS_RECTF* clipRect) {
203 if (!matrix && !clipRect)
204 return false;
205
206 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
207 if (!pPage)
208 return false;
209
210 CPDF_Dictionary* pPageDict = pPage->GetDict();
211 CPDF_Object* pContentObj = GetPageContent(pPageDict);
212 if (!pContentObj)
213 return false;
214
215 CPDF_Document* pDoc = pPage->GetDocument();
216 if (!pDoc)
217 return false;
218
219 std::ostringstream text_buf;
220 text_buf << "q ";
221
222 if (clipRect) {
223 CFX_FloatRect rect = CFXFloatRectFromFSRectF(*clipRect);
224 rect.Normalize();
225
226 WriteFloat(text_buf, rect.left) << " ";
227 WriteFloat(text_buf, rect.bottom) << " ";
228 WriteFloat(text_buf, rect.Width()) << " ";
229 WriteFloat(text_buf, rect.Height()) << " re W* n ";
230 }
231 if (matrix) {
232 CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix);
233 text_buf << m << " cm ";
234 }
235
236 CPDF_Stream* pStream =
237 pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
238 pStream->SetDataFromStringstream(&text_buf);
239
240 CPDF_Stream* pEndStream =
241 pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
242 pEndStream->SetData(ByteStringView(" Q").raw_span());
243
244 if (CPDF_Array* pContentArray = ToArray(pContentObj)) {
245 pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
246 pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
247 } else if (pContentObj->IsStream() && !pContentObj->IsInline()) {
248 pContentArray = pDoc->NewIndirect<CPDF_Array>();
249 pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
250 pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
251 pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
252 pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc,
253 pContentArray->GetObjNum());
254 }
255
256 // Need to transform the patterns as well.
257 CPDF_Dictionary* pRes =
258 pPageDict->GetDictFor(pdfium::page_object::kResources);
259 if (!pRes)
260 return true;
261
262 CPDF_Dictionary* pPatternDict = pRes->GetDictFor("Pattern");
263 if (!pPatternDict)
264 return true;
265
266 CPDF_DictionaryLocker locker(pPatternDict);
267 for (const auto& it : locker) {
268 CPDF_Object* pObj = it.second.Get();
269 if (pObj->IsReference())
270 pObj = pObj->GetDirect();
271
272 CPDF_Dictionary* pDict = nullptr;
273 if (pObj->IsDictionary())
274 pDict = pObj->AsDictionary();
275 else if (CPDF_Stream* pObjStream = pObj->AsStream())
276 pDict = pObjStream->GetDict();
277 else
278 continue;
279
280 if (matrix) {
281 CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix);
282 pDict->SetMatrixFor("Matrix", pDict->GetMatrixFor("Matrix") * m);
283 }
284 }
285
286 return true;
287 }
288
289 FPDF_EXPORT void FPDF_CALLCONV
FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,double a,double b,double c,double d,double e,double f)290 FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
291 double a,
292 double b,
293 double c,
294 double d,
295 double e,
296 double f) {
297 CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
298 if (!pPageObj)
299 return;
300
301 CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f);
302
303 // Special treatment to shading object, because the ClipPath for shading
304 // object is already transformed.
305 if (!pPageObj->IsShading())
306 pPageObj->TransformClipPath(matrix);
307 pPageObj->TransformGeneralState(matrix);
308 }
309
310 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object)311 FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object) {
312 CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
313 if (!pPageObj)
314 return nullptr;
315
316 return FPDFClipPathFromCPDFClipPath(&pPageObj->m_ClipPath);
317 }
318
FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path)319 FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path) {
320 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
321 if (!pClipPath || !pClipPath->HasRef())
322 return -1;
323
324 return pClipPath->GetPathCount();
325 }
326
327 FPDF_EXPORT int FPDF_CALLCONV
FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path,int path_index)328 FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index) {
329 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
330 if (!pClipPath || !pClipPath->HasRef())
331 return -1;
332
333 if (path_index < 0 ||
334 static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) {
335 return -1;
336 }
337
338 return pdfium::CollectionSize<int>(
339 pClipPath->GetPath(path_index).GetPoints());
340 }
341
342 FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,int path_index,int segment_index)343 FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
344 int path_index,
345 int segment_index) {
346 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
347 if (!pClipPath || !pClipPath->HasRef())
348 return nullptr;
349
350 if (path_index < 0 ||
351 static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) {
352 return nullptr;
353 }
354
355 const std::vector<FX_PATHPOINT>& points =
356 pClipPath->GetPath(path_index).GetPoints();
357 if (!pdfium::IndexInBounds(points, segment_index))
358 return nullptr;
359
360 return FPDFPathSegmentFromFXPathPoint(&points[segment_index]);
361 }
362
FPDF_CreateClipPath(float left,float bottom,float right,float top)363 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left,
364 float bottom,
365 float right,
366 float top) {
367 CPDF_Path Path;
368 Path.AppendRect(left, bottom, right, top);
369
370 auto pNewClipPath = pdfium::MakeUnique<CPDF_ClipPath>();
371 pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false);
372
373 // Caller takes ownership.
374 return FPDFClipPathFromCPDFClipPath(pNewClipPath.release());
375 }
376
FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath)377 FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
378 // Take ownership back from caller and destroy.
379 std::unique_ptr<CPDF_ClipPath>(CPDFClipPathFromFPDFClipPath(clipPath));
380 }
381
FPDFPage_InsertClipPath(FPDF_PAGE page,FPDF_CLIPPATH clipPath)382 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page,
383 FPDF_CLIPPATH clipPath) {
384 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
385 if (!pPage)
386 return;
387
388 CPDF_Dictionary* pPageDict = pPage->GetDict();
389 CPDF_Object* pContentObj = GetPageContent(pPageDict);
390 if (!pContentObj)
391 return;
392
393 std::ostringstream strClip;
394 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clipPath);
395 for (size_t i = 0; i < pClipPath->GetPathCount(); ++i) {
396 CPDF_Path path = pClipPath->GetPath(i);
397 if (path.GetPoints().empty()) {
398 // Empty clipping (totally clipped out)
399 strClip << "0 0 m W n ";
400 } else {
401 OutputPath(strClip, path);
402 if (pClipPath->GetClipType(i) == FXFILL_WINDING)
403 strClip << "W n\n";
404 else
405 strClip << "W* n\n";
406 }
407 }
408 CPDF_Document* pDoc = pPage->GetDocument();
409 if (!pDoc)
410 return;
411
412 CPDF_Stream* pStream =
413 pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>());
414 pStream->SetDataFromStringstream(&strClip);
415
416 if (CPDF_Array* pArray = ToArray(pContentObj)) {
417 pArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
418 } else if (pContentObj->IsStream() && !pContentObj->IsInline()) {
419 CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>();
420 pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
421 pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum());
422 pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc,
423 pContentArray->GetObjNum());
424 }
425 }
426