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