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 <vector>
10 
11 #include "core/fpdfapi/page/cpdf_clippath.h"
12 #include "core/fpdfapi/page/cpdf_page.h"
13 #include "core/fpdfapi/page/cpdf_pageobject.h"
14 #include "core/fpdfapi/page/cpdf_path.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_document.h"
17 #include "core/fpdfapi/parser/cpdf_number.h"
18 #include "core/fpdfapi/parser/cpdf_reference.h"
19 #include "core/fpdfapi/parser/cpdf_stream.h"
20 #include "core/fxge/cfx_pathdata.h"
21 #include "fpdfsdk/fsdk_define.h"
22 
23 namespace {
24 
SetBoundingBox(CPDF_Page * page,const CFX_ByteString & key,float left,float bottom,float right,float top)25 void SetBoundingBox(CPDF_Page* page,
26                     const CFX_ByteString& key,
27                     float left,
28                     float bottom,
29                     float right,
30                     float top) {
31   CPDF_Array* pBoundingBoxArray = page->m_pFormDict->SetNewFor<CPDF_Array>(key);
32   pBoundingBoxArray->AddNew<CPDF_Number>(left);
33   pBoundingBoxArray->AddNew<CPDF_Number>(bottom);
34   pBoundingBoxArray->AddNew<CPDF_Number>(right);
35   pBoundingBoxArray->AddNew<CPDF_Number>(top);
36 }
37 
GetBoundingBox(CPDF_Page * page,const CFX_ByteString & key,float * left,float * bottom,float * right,float * top)38 bool GetBoundingBox(CPDF_Page* page,
39                     const CFX_ByteString& key,
40                     float* left,
41                     float* bottom,
42                     float* right,
43                     float* top) {
44   CPDF_Array* pArray = page->m_pFormDict->GetArrayFor(key);
45   if (!pArray)
46     return false;
47 
48   *left = pArray->GetFloatAt(0);
49   *bottom = pArray->GetFloatAt(1);
50   *right = pArray->GetFloatAt(2);
51   *top = pArray->GetFloatAt(3);
52   return true;
53 }
54 
55 }  // namespace
56 
FPDFPage_SetMediaBox(FPDF_PAGE page,float left,float bottom,float right,float top)57 DLLEXPORT void STDCALL FPDFPage_SetMediaBox(FPDF_PAGE page,
58                                             float left,
59                                             float bottom,
60                                             float right,
61                                             float top) {
62   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
63   if (!pPage)
64     return;
65 
66   SetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
67 }
68 
FPDFPage_SetCropBox(FPDF_PAGE page,float left,float bottom,float right,float top)69 DLLEXPORT void STDCALL FPDFPage_SetCropBox(FPDF_PAGE page,
70                                            float left,
71                                            float bottom,
72                                            float right,
73                                            float top) {
74   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
75   if (!pPage)
76     return;
77 
78   SetBoundingBox(pPage, "CropBox", left, bottom, right, top);
79 }
80 
FPDFPage_GetMediaBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)81 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetMediaBox(FPDF_PAGE page,
82                                                  float* left,
83                                                  float* bottom,
84                                                  float* right,
85                                                  float* top) {
86   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
87   return pPage && GetBoundingBox(pPage, "MediaBox", left, bottom, right, top);
88 }
89 
FPDFPage_GetCropBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)90 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_GetCropBox(FPDF_PAGE page,
91                                                 float* left,
92                                                 float* bottom,
93                                                 float* right,
94                                                 float* top) {
95   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
96   return pPage && GetBoundingBox(pPage, "CropBox", left, bottom, right, top);
97 }
98 
FPDFPage_TransFormWithClip(FPDF_PAGE page,FS_MATRIX * matrix,FS_RECTF * clipRect)99 DLLEXPORT FPDF_BOOL STDCALL FPDFPage_TransFormWithClip(FPDF_PAGE page,
100                                                        FS_MATRIX* matrix,
101                                                        FS_RECTF* clipRect) {
102   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
103   if (!pPage)
104     return false;
105 
106   CFX_ByteTextBuf textBuf;
107   textBuf << "q ";
108   CFX_FloatRect rect(clipRect->left, clipRect->bottom, clipRect->right,
109                      clipRect->top);
110   rect.Normalize();
111   CFX_ByteString bsClipping;
112   bsClipping.Format("%f %f %f %f re W* n ", rect.left, rect.bottom,
113                     rect.Width(), rect.Height());
114   textBuf << bsClipping;
115 
116   CFX_ByteString bsMatix;
117   bsMatix.Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b, matrix->c,
118                  matrix->d, matrix->e, matrix->f);
119   textBuf << bsMatix;
120 
121   CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
122   CPDF_Object* pContentObj =
123       pPageDic ? pPageDic->GetObjectFor("Contents") : nullptr;
124   if (!pContentObj)
125     pContentObj = pPageDic ? pPageDic->GetArrayFor("Contents") : nullptr;
126   if (!pContentObj)
127     return false;
128 
129   CPDF_Document* pDoc = pPage->m_pDocument;
130   if (!pDoc)
131     return false;
132 
133   CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>(
134       nullptr, 0,
135       pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool()));
136   pStream->SetData(textBuf.GetBuffer(), textBuf.GetSize());
137 
138   CPDF_Stream* pEndStream = pDoc->NewIndirect<CPDF_Stream>(
139       nullptr, 0,
140       pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool()));
141   pEndStream->SetData((const uint8_t*)" Q", 2);
142 
143   CPDF_Array* pContentArray = nullptr;
144   CPDF_Array* pArray = ToArray(pContentObj);
145   if (pArray) {
146     pContentArray = pArray;
147     pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
148     pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
149   } else if (CPDF_Reference* pReference = ToReference(pContentObj)) {
150     CPDF_Object* pDirectObj = pReference->GetDirect();
151     if (pDirectObj) {
152       CPDF_Array* pObjArray = pDirectObj->AsArray();
153       if (pObjArray) {
154         pContentArray = pObjArray;
155         pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc,
156                                                    pStream->GetObjNum());
157         pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
158       } else if (pDirectObj->IsStream()) {
159         pContentArray = pDoc->NewIndirect<CPDF_Array>();
160         pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
161         pContentArray->AddNew<CPDF_Reference>(pDoc, pDirectObj->GetObjNum());
162         pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum());
163         pPageDic->SetNewFor<CPDF_Reference>("Contents", pDoc,
164                                             pContentArray->GetObjNum());
165       }
166     }
167   }
168 
169   // Need to transform the patterns as well.
170   CPDF_Dictionary* pRes = pPageDic->GetDictFor("Resources");
171   if (pRes) {
172     CPDF_Dictionary* pPattenDict = pRes->GetDictFor("Pattern");
173     if (pPattenDict) {
174       for (const auto& it : *pPattenDict) {
175         CPDF_Object* pObj = it.second.get();
176         if (pObj->IsReference())
177           pObj = pObj->GetDirect();
178 
179         CPDF_Dictionary* pDict = nullptr;
180         if (pObj->IsDictionary())
181           pDict = pObj->AsDictionary();
182         else if (CPDF_Stream* pObjStream = pObj->AsStream())
183           pDict = pObjStream->GetDict();
184         else
185           continue;
186 
187         CFX_Matrix m = pDict->GetMatrixFor("Matrix");
188         CFX_Matrix t = *(CFX_Matrix*)matrix;
189         m.Concat(t);
190         pDict->SetMatrixFor("Matrix", m);
191       }
192     }
193   }
194 
195   return true;
196 }
197 
198 DLLEXPORT void STDCALL
FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,double a,double b,double c,double d,double e,double f)199 FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,
200                               double a,
201                               double b,
202                               double c,
203                               double d,
204                               double e,
205                               double f) {
206   CPDF_PageObject* pPageObj = (CPDF_PageObject*)page_object;
207   if (!pPageObj)
208     return;
209   CFX_Matrix matrix((FX_FLOAT)a, (FX_FLOAT)b, (FX_FLOAT)c, (FX_FLOAT)d,
210                     (FX_FLOAT)e, (FX_FLOAT)f);
211 
212   // Special treatment to shading object, because the ClipPath for shading
213   // object is already transformed.
214   if (!pPageObj->IsShading())
215     pPageObj->TransformClipPath(matrix);
216   pPageObj->TransformGeneralState(matrix);
217 }
218 
FPDF_CreateClipPath(float left,float bottom,float right,float top)219 DLLEXPORT FPDF_CLIPPATH STDCALL FPDF_CreateClipPath(float left,
220                                                     float bottom,
221                                                     float right,
222                                                     float top) {
223   CPDF_Path Path;
224   Path.AppendRect(left, bottom, right, top);
225 
226   CPDF_ClipPath* pNewClipPath = new CPDF_ClipPath();
227   pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false);
228   return pNewClipPath;
229 }
230 
FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath)231 DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) {
232   delete (CPDF_ClipPath*)clipPath;
233 }
234 
OutputPath(CFX_ByteTextBuf & buf,CPDF_Path path)235 void OutputPath(CFX_ByteTextBuf& buf, CPDF_Path path) {
236   const CFX_PathData* pPathData = path.GetObject();
237   if (!pPathData)
238     return;
239 
240   const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints();
241   if (path.IsRect()) {
242     CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point;
243     buf << pPoints[0].m_Point.x << " " << pPoints[0].m_Point.y << " " << diff.x
244         << " " << diff.y << " re\n";
245     return;
246   }
247 
248   CFX_ByteString temp;
249   for (size_t i = 0; i < pPoints.size(); i++) {
250     buf << pPoints[i].m_Point.x << " " << pPoints[i].m_Point.y;
251     FXPT_TYPE point_type = pPoints[i].m_Type;
252     if (point_type == FXPT_TYPE::MoveTo) {
253       buf << " m\n";
254     } else if (point_type == FXPT_TYPE::BezierTo) {
255       buf << " " << pPoints[i + 1].m_Point.x << " " << pPoints[i + 1].m_Point.y
256           << " " << pPoints[i + 2].m_Point.x << " " << pPoints[i + 2].m_Point.y;
257       buf << " c";
258       if (pPoints[i + 2].m_CloseFigure)
259         buf << " h";
260       buf << "\n";
261 
262       i += 2;
263     } else if (point_type == FXPT_TYPE::LineTo) {
264       buf << " l";
265       if (pPoints[i].m_CloseFigure)
266         buf << " h";
267       buf << "\n";
268     }
269   }
270 }
271 
FPDFPage_InsertClipPath(FPDF_PAGE page,FPDF_CLIPPATH clipPath)272 DLLEXPORT void STDCALL FPDFPage_InsertClipPath(FPDF_PAGE page,
273                                                FPDF_CLIPPATH clipPath) {
274   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
275   if (!pPage)
276     return;
277 
278   CPDF_Dictionary* pPageDic = pPage->m_pFormDict;
279   CPDF_Object* pContentObj =
280       pPageDic ? pPageDic->GetObjectFor("Contents") : nullptr;
281   if (!pContentObj)
282     pContentObj = pPageDic ? pPageDic->GetArrayFor("Contents") : nullptr;
283   if (!pContentObj)
284     return;
285 
286   CFX_ByteTextBuf strClip;
287   CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath;
288   uint32_t i;
289   for (i = 0; i < pClipPath->GetPathCount(); i++) {
290     CPDF_Path path = pClipPath->GetPath(i);
291     int iClipType = pClipPath->GetClipType(i);
292     if (path.GetPoints().empty()) {
293       // Empty clipping (totally clipped out)
294       strClip << "0 0 m W n ";
295     } else {
296       OutputPath(strClip, path);
297       if (iClipType == FXFILL_WINDING)
298         strClip << "W n\n";
299       else
300         strClip << "W* n\n";
301     }
302   }
303   CPDF_Document* pDoc = pPage->m_pDocument;
304   if (!pDoc)
305     return;
306 
307   CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>(
308       nullptr, 0,
309       pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool()));
310   pStream->SetData(strClip.GetBuffer(), strClip.GetSize());
311 
312   CPDF_Array* pArray = ToArray(pContentObj);
313   if (pArray) {
314     pArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
315     return;
316   }
317   CPDF_Reference* pReference = ToReference(pContentObj);
318   if (!pReference)
319     return;
320 
321   CPDF_Object* pDirectObj = pReference->GetDirect();
322   if (!pDirectObj)
323     return;
324 
325   CPDF_Array* pObjArray = pDirectObj->AsArray();
326   if (pObjArray) {
327     pObjArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum());
328     return;
329   }
330   if (pDirectObj->IsStream()) {
331     CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>();
332     pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum());
333     pContentArray->AddNew<CPDF_Reference>(pDoc, pDirectObj->GetObjNum());
334     pPageDic->SetNewFor<CPDF_Reference>("Contents", pDoc,
335                                         pContentArray->GetObjNum());
336   }
337 }
338