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