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 "core/fpdfapi/page/cpdf_clippath.h" 14 #include "core/fpdfapi/page/cpdf_page.h" 15 #include "core/fpdfapi/page/cpdf_pageobject.h" 16 #include "core/fpdfapi/page/cpdf_path.h" 17 #include "core/fpdfapi/parser/cpdf_array.h" 18 #include "core/fpdfapi/parser/cpdf_document.h" 19 #include "core/fpdfapi/parser/cpdf_number.h" 20 #include "core/fpdfapi/parser/cpdf_reference.h" 21 #include "core/fpdfapi/parser/cpdf_stream.h" 22 #include "core/fxge/cfx_pathdata.h" 23 #include "fpdfsdk/fsdk_define.h" 24 25 namespace { 26 27 void SetBoundingBox(CPDF_Page* page, 28 const ByteString& key, 29 float left, 30 float bottom, 31 float right, 32 float top) { 33 CPDF_Array* pBoundingBoxArray = page->m_pFormDict->SetNewFor<CPDF_Array>(key); 34 pBoundingBoxArray->AddNew<CPDF_Number>(left); 35 pBoundingBoxArray->AddNew<CPDF_Number>(bottom); 36 pBoundingBoxArray->AddNew<CPDF_Number>(right); 37 pBoundingBoxArray->AddNew<CPDF_Number>(top); 38 } 39 40 bool GetBoundingBox(CPDF_Page* page, 41 const ByteString& key, 42 float* left, 43 float* bottom, 44 float* right, 45 float* top) { 46 CPDF_Array* pArray = page->m_pFormDict->GetArrayFor(key); 47 if (!pArray) 48 return false; 49 50 *left = pArray->GetFloatAt(0); 51 *bottom = pArray->GetFloatAt(1); 52 *right = pArray->GetFloatAt(2); 53 *top = pArray->GetFloatAt(3); 54 return true; 55 } 56 57 CPDF_Object* GetPageContent(CPDF_Dictionary* pPageDict) { 58 return pPageDict ? pPageDict->GetDirectObjectFor("Contents") : nullptr; 59 } 60 61 } // namespace 62 63 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetMediaBox(FPDF_PAGE page, 64 float left, 65 float bottom, 66 float right, 67 float top) { 68 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 69 if (!pPage) 70 return; 71 72 SetBoundingBox(pPage, "MediaBox", left, bottom, right, top); 73 } 74 75 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetCropBox(FPDF_PAGE page, 76 float left, 77 float bottom, 78 float right, 79 float top) { 80 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 81 if (!pPage) 82 return; 83 84 SetBoundingBox(pPage, "CropBox", left, bottom, right, top); 85 } 86 87 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetMediaBox(FPDF_PAGE page, 88 float* left, 89 float* bottom, 90 float* right, 91 float* top) { 92 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 93 return pPage && GetBoundingBox(pPage, "MediaBox", left, bottom, right, top); 94 } 95 96 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_GetCropBox(FPDF_PAGE page, 97 float* left, 98 float* bottom, 99 float* right, 100 float* top) { 101 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 102 return pPage && GetBoundingBox(pPage, "CropBox", left, bottom, right, top); 103 } 104 105 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV 106 FPDFPage_TransFormWithClip(FPDF_PAGE page, 107 FS_MATRIX* matrix, 108 FS_RECTF* clipRect) { 109 if (!matrix && !clipRect) 110 return false; 111 112 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 113 if (!pPage) 114 return false; 115 116 std::ostringstream textBuf; 117 textBuf << "q "; 118 119 if (clipRect) { 120 CFX_FloatRect rect = CFXFloatRectFromFSRECTF(*clipRect); 121 rect.Normalize(); 122 123 textBuf << ByteString::Format("%f %f %f %f re W* n ", rect.left, 124 rect.bottom, rect.Width(), rect.Height()); 125 } 126 if (matrix) { 127 textBuf << ByteString::Format("%f %f %f %f %f %f cm ", matrix->a, matrix->b, 128 matrix->c, matrix->d, matrix->e, matrix->f); 129 } 130 131 CPDF_Dictionary* pPageDict = pPage->m_pFormDict.Get(); 132 CPDF_Object* pContentObj = GetPageContent(pPageDict); 133 if (!pContentObj) 134 return false; 135 136 CPDF_Document* pDoc = pPage->m_pDocument.Get(); 137 if (!pDoc) 138 return false; 139 140 CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>( 141 nullptr, 0, 142 pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool())); 143 pStream->SetData(&textBuf); 144 145 CPDF_Stream* pEndStream = pDoc->NewIndirect<CPDF_Stream>( 146 nullptr, 0, 147 pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool())); 148 pEndStream->SetData((const uint8_t*)" Q", 2); 149 150 if (CPDF_Array* pContentArray = ToArray(pContentObj)) { 151 pContentArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum()); 152 pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum()); 153 } else if (pContentObj->IsStream() && !pContentObj->IsInline()) { 154 CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>(); 155 pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum()); 156 pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum()); 157 pContentArray->AddNew<CPDF_Reference>(pDoc, pEndStream->GetObjNum()); 158 pPageDict->SetNewFor<CPDF_Reference>("Contents", pDoc, 159 pContentArray->GetObjNum()); 160 } 161 162 // Need to transform the patterns as well. 163 CPDF_Dictionary* pRes = pPageDict->GetDictFor("Resources"); 164 if (pRes) { 165 CPDF_Dictionary* pPattenDict = pRes->GetDictFor("Pattern"); 166 if (pPattenDict) { 167 for (const auto& it : *pPattenDict) { 168 CPDF_Object* pObj = it.second.get(); 169 if (pObj->IsReference()) 170 pObj = pObj->GetDirect(); 171 172 CPDF_Dictionary* pDict = nullptr; 173 if (pObj->IsDictionary()) 174 pDict = pObj->AsDictionary(); 175 else if (CPDF_Stream* pObjStream = pObj->AsStream()) 176 pDict = pObjStream->GetDict(); 177 else 178 continue; 179 180 CFX_Matrix m = pDict->GetMatrixFor("Matrix"); 181 CFX_Matrix t = *(CFX_Matrix*)matrix; 182 m.Concat(t); 183 pDict->SetMatrixFor("Matrix", m); 184 } 185 } 186 } 187 188 return true; 189 } 190 191 FPDF_EXPORT void FPDF_CALLCONV 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) 201 return; 202 CFX_Matrix matrix((float)a, (float)b, (float)c, (float)d, (float)e, (float)f); 203 204 // Special treatment to shading object, because the ClipPath for shading 205 // object is already transformed. 206 if (!pPageObj->IsShading()) 207 pPageObj->TransformClipPath(matrix); 208 pPageObj->TransformGeneralState(matrix); 209 } 210 211 FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left, 212 float bottom, 213 float right, 214 float top) { 215 CPDF_Path Path; 216 Path.AppendRect(left, bottom, right, top); 217 218 auto pNewClipPath = pdfium::MakeUnique<CPDF_ClipPath>(); 219 pNewClipPath->AppendPath(Path, FXFILL_ALTERNATE, false); 220 return pNewClipPath.release(); // Caller takes ownership. 221 } 222 223 FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyClipPath(FPDF_CLIPPATH clipPath) { 224 // Take ownership back from caller and destroy. 225 std::unique_ptr<CPDF_ClipPath>(static_cast<CPDF_ClipPath*>(clipPath)); 226 } 227 228 void OutputPath(std::ostringstream& buf, CPDF_Path path) { 229 const CFX_PathData* pPathData = path.GetObject(); 230 if (!pPathData) 231 return; 232 233 const std::vector<FX_PATHPOINT>& pPoints = pPathData->GetPoints(); 234 if (path.IsRect()) { 235 CFX_PointF diff = pPoints[2].m_Point - pPoints[0].m_Point; 236 buf << pPoints[0].m_Point.x << " " << pPoints[0].m_Point.y << " " << diff.x 237 << " " << diff.y << " re\n"; 238 return; 239 } 240 241 ByteString temp; 242 for (size_t i = 0; i < pPoints.size(); i++) { 243 buf << pPoints[i].m_Point.x << " " << pPoints[i].m_Point.y; 244 FXPT_TYPE point_type = pPoints[i].m_Type; 245 if (point_type == FXPT_TYPE::MoveTo) { 246 buf << " m\n"; 247 } else if (point_type == FXPT_TYPE::BezierTo) { 248 buf << " " << pPoints[i + 1].m_Point.x << " " << pPoints[i + 1].m_Point.y 249 << " " << pPoints[i + 2].m_Point.x << " " << pPoints[i + 2].m_Point.y; 250 buf << " c"; 251 if (pPoints[i + 2].m_CloseFigure) 252 buf << " h"; 253 buf << "\n"; 254 255 i += 2; 256 } else if (point_type == FXPT_TYPE::LineTo) { 257 buf << " l"; 258 if (pPoints[i].m_CloseFigure) 259 buf << " h"; 260 buf << "\n"; 261 } 262 } 263 } 264 265 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertClipPath(FPDF_PAGE page, 266 FPDF_CLIPPATH clipPath) { 267 CPDF_Page* pPage = CPDFPageFromFPDFPage(page); 268 if (!pPage) 269 return; 270 271 CPDF_Dictionary* pPageDict = pPage->m_pFormDict.Get(); 272 CPDF_Object* pContentObj = GetPageContent(pPageDict); 273 if (!pContentObj) 274 return; 275 276 std::ostringstream strClip; 277 CPDF_ClipPath* pClipPath = (CPDF_ClipPath*)clipPath; 278 for (size_t i = 0; i < pClipPath->GetPathCount(); ++i) { 279 CPDF_Path path = pClipPath->GetPath(i); 280 if (path.GetPoints().empty()) { 281 // Empty clipping (totally clipped out) 282 strClip << "0 0 m W n "; 283 } else { 284 OutputPath(strClip, path); 285 if (pClipPath->GetClipType(i) == FXFILL_WINDING) 286 strClip << "W n\n"; 287 else 288 strClip << "W* n\n"; 289 } 290 } 291 CPDF_Document* pDoc = pPage->m_pDocument.Get(); 292 if (!pDoc) 293 return; 294 295 CPDF_Stream* pStream = pDoc->NewIndirect<CPDF_Stream>( 296 nullptr, 0, 297 pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool())); 298 pStream->SetData(&strClip); 299 300 if (CPDF_Array* pArray = ToArray(pContentObj)) { 301 pArray->InsertNewAt<CPDF_Reference>(0, pDoc, pStream->GetObjNum()); 302 } else if (pContentObj->IsStream() && !pContentObj->IsInline()) { 303 CPDF_Array* pContentArray = pDoc->NewIndirect<CPDF_Array>(); 304 pContentArray->AddNew<CPDF_Reference>(pDoc, pStream->GetObjNum()); 305 pContentArray->AddNew<CPDF_Reference>(pDoc, pContentObj->GetObjNum()); 306 pPageDict->SetNewFor<CPDF_Reference>("Contents", pDoc, 307 pContentArray->GetObjNum()); 308 } 309 } 310