• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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