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 <memory> 10 #include <sstream> 11 #include <vector> 12 13 #include "constants/page_object.h" 14 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h" 15 #include "core/fpdfapi/page/cpdf_clippath.h" 16 #include "core/fpdfapi/page/cpdf_page.h" 17 #include "core/fpdfapi/page/cpdf_pageobject.h" 18 #include "core/fpdfapi/page/cpdf_path.h" 19 #include "core/fpdfapi/parser/cpdf_array.h" 20 #include "core/fpdfapi/parser/cpdf_dictionary.h" 21 #include "core/fpdfapi/parser/cpdf_document.h" 22 #include "core/fpdfapi/parser/cpdf_number.h" 23 #include "core/fpdfapi/parser/cpdf_reference.h" 24 #include "core/fpdfapi/parser/cpdf_stream.h" 25 #include "core/fxge/cfx_pathdata.h" 26 #include "core/fxge/render_defines.h" 27 #include "fpdfsdk/cpdfsdk_helpers.h" 28 #include "third_party/base/ptr_util.h" 29 30 namespace { 31 SetBoundingBox(CPDF_Page * page,const ByteString & key,const CFX_FloatRect & rect)32 void SetBoundingBox(CPDF_Page* page, 33 const ByteString& key, 34 const CFX_FloatRect& rect) { 35 if (!page) 36 return; 37 38 page->GetDict()->SetRectFor(key, rect); 39 page->UpdateDimensions(); 40 } 41 GetBoundingBox(CPDF_Page * page,const ByteString & key,float * left,float * bottom,float * right,float * top)42 bool GetBoundingBox(CPDF_Page* page, 43 const ByteString& key, 44 float* left, 45 float* bottom, 46 float* right, 47 float* top) { 48 if (!page || !left || !bottom || !right || !top) 49 return false; 50 51 CPDF_Array* pArray = page->GetDict()->GetArrayFor(key); 52 if (!pArray) 53 return false; 54 55 *left = pArray->GetNumberAt(0); 56 *bottom = pArray->GetNumberAt(1); 57 *right = pArray->GetNumberAt(2); 58 *top = pArray->GetNumberAt(3); 59 return true; 60 } 61 GetPageContent(CPDF_Dictionary * pPageDict)62 CPDF_Object* GetPageContent(CPDF_Dictionary* pPageDict) { 63 return pPageDict->GetDirectObjectFor(pdfium::page_object::kContents); 64 } 65 OutputPath(std::ostringstream & buf,CPDF_Path path)66 void OutputPath(std::ostringstream& buf, CPDF_Path path) { 67 const CFX_PathData* pPathData = path.GetObject(); 68 if (!pPathData) 69 return; 70 71 const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints(); 72 if (path.IsRect()) { 73 CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point; 74 buf << pPoints[0].m_Point.x << " " << pPoints[0].m_Point.y << " " << diff.x 75 << " " << diff.y << " re\n"; 76 return; 77 } 78 79 ByteString temp; 80 for (size_t i = 0; i < pPoints.size(); i++) { 81 buf << pPoints[i].m_Point.x << " " << pPoints[i].m_Point.y; 82 FXPT_TYPE point_type = pPoints[i].m_Type; 83 if (point_type == FXPT_TYPE::MoveTo) { 84 buf << " m\n"; 85 } else if (point_type == FXPT_TYPE::BezierTo) { 86 buf << " " << pPoints[i + 1].m_Point.x << " " << pPoints[i + 1].m_Point.y 87 << " " << pPoints[i + 2].m_Point.x << " " << pPoints[i + 2].m_Point.y; 88 buf << " c"; 89 if (pPoints[i + 2].m_CloseFigure) 90 buf << " h"; 91 buf << "\n"; 92 93 i += 2; 94 } else if (point_type == FXPT_TYPE::LineTo) { 95 buf << " l"; 96 if (pPoints[i].m_CloseFigure) 97 buf << " h"; 98 buf << "\n"; 99 } 100 } 101 } 102 103 } // namespace 104 FPDFPage_SetMediaBox(FPDF_PAGE page,float left,float bottom,float right,float top)105 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page, 106 float left, 107 float bottom, 108 float right, 109 float top) { 110 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kMediaBox, 111 CFX_FloatRect(left, bottom, right, top)); 112 } 113 FPDFPage_SetCropBox(FPDF_PAGE page,float left,float bottom,float right,float top)114 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page, 115 float left, 116 float bottom, 117 float right, 118 float top) { 119 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kCropBox, 120 CFX_FloatRect(left, bottom, right, top)); 121 } 122 FPDFPage_SetBleedBox(FPDF_PAGE page,float left,float bottom,float right,float top)123 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetBleedBox(FPDF_PAGE page, 124 float left, 125 float bottom, 126 float right, 127 float top) { 128 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kBleedBox, 129 CFX_FloatRect(left, bottom, right, top)); 130 } 131 FPDFPage_SetTrimBox(FPDF_PAGE page,float left,float bottom,float right,float top)132 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetTrimBox(FPDF_PAGE page, 133 float left, 134 float bottom, 135 float right, 136 float top) { 137 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kTrimBox, 138 CFX_FloatRect(left, bottom, right, top)); 139 } 140 FPDFPage_SetArtBox(FPDF_PAGE page,float left,float bottom,float right,float top)141 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetArtBox(FPDF_PAGE page, 142 float left, 143 float bottom, 144 float right, 145 float top) { 146 SetBoundingBox(CPDFPageFromFPDFPage(page), pdfium::page_object::kArtBox, 147 CFX_FloatRect(left, bottom, right, top)); 148 } 149 FPDFPage_GetMediaBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)150 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page, 151 float* left, 152 float* bottom, 153 float* right, 154 float* top) { 155 return GetBoundingBox(CPDFPageFromFPDFPage(page), 156 pdfium::page_object::kMediaBox, left, bottom, right, 157 top); 158 } 159 FPDFPage_GetCropBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)160 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page, 161 float* left, 162 float* bottom, 163 float* right, 164 float* top) { 165 return GetBoundingBox(CPDFPageFromFPDFPage(page), 166 pdfium::page_object::kCropBox, left, bottom, right, 167 top); 168 } 169 FPDFPage_GetBleedBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)170 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetBleedBox(FPDF_PAGE page, 171 float* left, 172 float* bottom, 173 float* right, 174 float* top) { 175 return GetBoundingBox(CPDFPageFromFPDFPage(page), 176 pdfium::page_object::kBleedBox, left, bottom, right, 177 top); 178 } 179 FPDFPage_GetTrimBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)180 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetTrimBox(FPDF_PAGE page, 181 float* left, 182 float* bottom, 183 float* right, 184 float* top) { 185 return GetBoundingBox(CPDFPageFromFPDFPage(page), 186 pdfium::page_object::kTrimBox, left, bottom, right, 187 top); 188 } 189 FPDFPage_GetArtBox(FPDF_PAGE page,float * left,float * bottom,float * right,float * top)190 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetArtBox(FPDF_PAGE page, 191 float* left, 192 float* bottom, 193 float* right, 194 float* top) { 195 return GetBoundingBox(CPDFPageFromFPDFPage(page), 196 pdfium::page_object::kArtBox, left, bottom, right, top); 197 } 198 199 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_TransFormWithClip(FPDF_PAGE page,const FS_MATRIX * matrix,const FS_RECTF * clipRect)200 FPDFPage_TransFormWithClip(FPDF_PAGE page, 201 const FS_MATRIX* matrix, 202 const FS_RECTF* clipRect) { 203 if (!matrix && !clipRect) 204 return false; 205 206 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 207 if (!pPage) 208 return false; 209 210 CPDF_Dictionary* pPageDict = pPage->GetDict(); 211 CPDF_Object* pContentObj = GetPageContent(pPageDict); 212 if (!pContentObj) 213 return false; 214 215 CPDF_Document* pDoc = pPage->GetDocument(); 216 if (!pDoc) 217 return false; 218 219 std::ostringstream text_buf; 220 text_buf << "q "; 221 222 if (clipRect) { 223 CFX_FloatRect rect = CFXFloatRectFromFSRectF(*clipRect); 224 rect.Normalize(); 225 226 WriteFloat(text_buf, rect.left) << " "; 227 WriteFloat(text_buf, rect.bottom) << " "; 228 WriteFloat(text_buf, rect.Width()) << " "; 229 WriteFloat(text_buf, rect.Height()) << " re W* n "; 230 } 231 if (matrix) { 232 CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix); 233 text_buf << m << " cm "; 234 } 235 236 CPDF_Stream* pStream = 237 pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>()); 238 pStream->SetDataFromStringstream(&text_buf); 239 240 CPDF_Stream* pEndStream = 241 pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>()); 242 pEndStream->SetData(ByteStringView(" Q").raw_span()); 243 244 if (CPDF_Array* pContentArray = ToArray(pContentObj)) { 245 pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum()); 246 pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum()); 247 } else if (pContentObj->IsStream() && !pContentObj->IsInline()) { 248 pContentArray = pDoc->NewIndirect<CPDF_Array>(); 249 pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum()); 250 pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum()); 251 pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum()); 252 pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc, 253 pContentArray->GetObjNum()); 254 } 255 256 // Need to transform the patterns as well. 257 CPDF_Dictionary* pRes = 258 pPageDict->GetDictFor(pdfium::page_object::kResources); 259 if (!pRes) 260 return true; 261 262 CPDF_Dictionary* pPatternDict = pRes->GetDictFor("Pattern"); 263 if (!pPatternDict) 264 return true; 265 266 CPDF_DictionaryLocker locker(pPatternDict); 267 for (const auto& it : locker) { 268 CPDF_Object* pObj = it.second.Get(); 269 if (pObj->IsReference()) 270 pObj = pObj->GetDirect(); 271 272 CPDF_Dictionary* pDict = nullptr; 273 if (pObj->IsDictionary()) 274 pDict = pObj->AsDictionary(); 275 else if (CPDF_Stream* pObjStream = pObj->AsStream()) 276 pDict = pObjStream->GetDict(); 277 else 278 continue; 279 280 if (matrix) { 281 CFX_Matrix m = CFXMatrixFromFSMatrix(*matrix); 282 pDict->SetMatrixFor("Matrix", pDict->GetMatrixFor("Matrix") * m); 283 } 284 } 285 286 return true; 287 } 288 289 FPDF_EXPORT void FPDF_CALLCONV FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object,double a,double b,double c,double d,double e,double f)290 FPDFPageObj_TransformClipPath(FPDF_PAGEOBJECT page_object, 291 double a, 292 double b, 293 double c, 294 double d, 295 double e, 296 double f) { 297 CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); 298 if (!pPageObj) 299 return; 300 301 CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f); 302 303 // Special treatment to shading object, because the ClipPath for shading 304 // object is already transformed. 305 if (!pPageObj->IsShading()) 306 pPageObj->TransformClipPath(matrix); 307 pPageObj->TransformGeneralState(matrix); 308 } 309 310 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object)311 FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object) { 312 CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object); 313 if (!pPageObj) 314 return nullptr; 315 316 return FPDFClipPathFromCPDFClipPath(&pPageObj->m_ClipPath); 317 } 318 FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path)319 FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path) { 320 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path); 321 if (!pClipPath || !pClipPath->HasRef()) 322 return -1; 323 324 return pClipPath->GetPathCount(); 325 } 326 327 FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path,int path_index)328 FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index) { 329 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path); 330 if (!pClipPath || !pClipPath->HasRef()) 331 return -1; 332 333 if (path_index < 0 || 334 static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) { 335 return -1; 336 } 337 338 return pdfium::CollectionSize<int>( 339 pClipPath->GetPath(path_index).GetPoints()); 340 } 341 342 FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,int path_index,int segment_index)343 FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path, 344 int path_index, 345 int segment_index) { 346 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path); 347 if (!pClipPath || !pClipPath->HasRef()) 348 return nullptr; 349 350 if (path_index < 0 || 351 static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) { 352 return nullptr; 353 } 354 355 const std::vector<FX_PATHPOINT>& points = 356 pClipPath->GetPath(path_index).GetPoints(); 357 if (!pdfium::IndexInBounds(points, segment_index)) 358 return nullptr; 359 360 return FPDFPathSegmentFromFXPathPoint(&points[segment_index]); 361 } 362 FPDF_CreateClipPath(float left,float bottom,float right,float top)363 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left, 364 float bottom, 365 float right, 366 float top) { 367 CPDF_Path Path; 368 Path.AppendRect(left, bottom, right, top); 369 370 auto pNewClipPath = pdfium::MakeUnique<CPDF_ClipPath>(); 371 pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false); 372 373 // Caller takes ownership. 374 return FPDFClipPathFromCPDFClipPath(pNewClipPath.release()); 375 } 376 FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath)377 FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) { 378 // Take ownership back from caller and destroy. 379 std::unique_ptr<CPDF_ClipPath>(CPDFClipPathFromFPDFClipPath(clipPath)); 380 } 381 FPDFPage_InsertClipPath(FPDF_PAGE page,FPDF_CLIPPATH clipPath)382 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page, 383 FPDF_CLIPPATH clipPath) { 384 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 385 if (!pPage) 386 return; 387 388 CPDF_Dictionary* pPageDict = pPage->GetDict(); 389 CPDF_Object* pContentObj = GetPageContent(pPageDict); 390 if (!pContentObj) 391 return; 392 393 std::ostringstream strClip; 394 CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clipPath); 395 for (size_t i = 0; i < pClipPath->GetPathCount(); ++i) { 396 CPDF_Path path = pClipPath->GetPath(i); 397 if (path.GetPoints().empty()) { 398 // Empty clipping (totally clipped out) 399 strClip << "0 0 m W n "; 400 } else { 401 OutputPath(strClip, path); 402 if (pClipPath->GetClipType(i) == FXFILL_WINDING) 403 strClip << "W n\n"; 404 else 405 strClip << "W* n\n"; 406 } 407 } 408 CPDF_Document* pDoc = pPage->GetDocument(); 409 if (!pDoc) 410 return; 411 412 CPDF_Stream* pStream = 413 pDoc->NewIndirect<CPDF_Stream>(nullptr, 0, pDoc->New<CPDF_Dictionary>()); 414 pStream->SetDataFromStringstream(&strClip); 415 416 if (CPDF_Array* pArray = ToArray(pContentObj)) { 417 pArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum()); 418 } else if (pContentObj->IsStream() && !pContentObj->IsInline()) { 419 CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>(); 420 pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum()); 421 pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum()); 422 pPageDict->SetNewFor<CPDF_Reference>(pdfium::page_object::kContents, pDoc, 423 pContentArray->GetObjNum()); 424 } 425 } 426