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