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 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 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 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 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 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 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 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 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 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 221 DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) { 222 delete (CPDF_ClipPath*)clipPath; 223 } 224 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 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