• Home
  • 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 "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