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