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