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 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 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 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 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 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 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 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 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 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 231 DLLEXPORT void STDCALL FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) { 232 delete (CPDF_ClipPath*)clipPath; 233 } 234 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 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