xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdf_editimg.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_edit.h"
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "core/fpdfapi/page/cpdf_dib.h"
13 #include "core/fpdfapi/page/cpdf_image.h"
14 #include "core/fpdfapi/page/cpdf_imageobject.h"
15 #include "core/fpdfapi/page/cpdf_page.h"
16 #include "core/fpdfapi/page/cpdf_pageobject.h"
17 #include "core/fpdfapi/parser/cpdf_array.h"
18 #include "core/fpdfapi/parser/cpdf_dictionary.h"
19 #include "core/fpdfapi/parser/cpdf_name.h"
20 #include "core/fpdfapi/parser/cpdf_stream.h"
21 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
22 #include "core/fpdfapi/render/cpdf_imagerenderer.h"
23 #include "core/fpdfapi/render/cpdf_rendercontext.h"
24 #include "core/fpdfapi/render/cpdf_renderstatus.h"
25 #include "core/fxcrt/stl_util.h"
26 #include "core/fxge/cfx_defaultrenderdevice.h"
27 #include "core/fxge/dib/cfx_dibitmap.h"
28 #include "fpdfsdk/cpdfsdk_customaccess.h"
29 #include "fpdfsdk/cpdfsdk_helpers.h"
30 
31 namespace {
32 
33 // These checks ensure the consistency of colorspace values across core/ and
34 // public/.
35 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceGray) ==
36                   FPDF_COLORSPACE_DEVICEGRAY,
37               "kDeviceGray value mismatch");
38 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceRGB) ==
39                   FPDF_COLORSPACE_DEVICERGB,
40               "kDeviceRGB value mismatch");
41 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceCMYK) ==
42                   FPDF_COLORSPACE_DEVICECMYK,
43               "kDeviceCMYK value mismatch");
44 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kCalGray) ==
45                   FPDF_COLORSPACE_CALGRAY,
46               "kCalGray value mismatch");
47 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kCalRGB) ==
48                   FPDF_COLORSPACE_CALRGB,
49               "kCalRGB value mismatch");
50 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kLab) ==
51                   FPDF_COLORSPACE_LAB,
52               "kLab value mismatch");
53 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kICCBased) ==
54                   FPDF_COLORSPACE_ICCBASED,
55               "kICCBased value mismatch");
56 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kSeparation) ==
57                   FPDF_COLORSPACE_SEPARATION,
58               "kSeparation value mismatch");
59 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kDeviceN) ==
60                   FPDF_COLORSPACE_DEVICEN,
61               "kDeviceN value mismatch");
62 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kIndexed) ==
63                   FPDF_COLORSPACE_INDEXED,
64               "kIndexed value mismatch");
65 static_assert(static_cast<int>(CPDF_ColorSpace::Family::kPattern) ==
66                   FPDF_COLORSPACE_PATTERN,
67               "kPattern value mismatch");
68 
MakeSeekableReadStream(FPDF_FILEACCESS * pFileAccess)69 RetainPtr<IFX_SeekableReadStream> MakeSeekableReadStream(
70     FPDF_FILEACCESS* pFileAccess) {
71   return pdfium::MakeRetain<CPDFSDK_CustomAccess>(pFileAccess);
72 }
73 
CPDFImageObjectFromFPDFPageObject(FPDF_PAGEOBJECT image_object)74 CPDF_ImageObject* CPDFImageObjectFromFPDFPageObject(
75     FPDF_PAGEOBJECT image_object) {
76   CPDF_PageObject* pPageObject = CPDFPageObjectFromFPDFPageObject(image_object);
77   return pPageObject ? pPageObject->AsImage() : nullptr;
78 }
79 
LoadJpegHelper(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_FILEACCESS * file_access,bool inline_jpeg)80 bool LoadJpegHelper(FPDF_PAGE* pages,
81                     int count,
82                     FPDF_PAGEOBJECT image_object,
83                     FPDF_FILEACCESS* file_access,
84                     bool inline_jpeg) {
85   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
86   if (!pImgObj)
87     return false;
88 
89   if (!file_access)
90     return false;
91 
92   if (pages) {
93     for (int index = 0; index < count; index++) {
94       CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
95       if (pPage)
96         pImgObj->GetImage()->ResetCache(pPage);
97     }
98   }
99 
100   RetainPtr<IFX_SeekableReadStream> pFile = MakeSeekableReadStream(file_access);
101   if (inline_jpeg)
102     pImgObj->GetImage()->SetJpegImageInline(std::move(pFile));
103   else
104     pImgObj->GetImage()->SetJpegImage(std::move(pFile));
105 
106   pImgObj->SetDirty(true);
107   return true;
108 }
109 
110 }  // namespace
111 
112 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFPageObj_NewImageObj(FPDF_DOCUMENT document)113 FPDFPageObj_NewImageObj(FPDF_DOCUMENT document) {
114   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
115   if (!pDoc)
116     return nullptr;
117 
118   auto pImageObj = std::make_unique<CPDF_ImageObject>();
119   pImageObj->SetImage(pdfium::MakeRetain<CPDF_Image>(pDoc));
120 
121   // Caller takes ownership.
122   return FPDFPageObjectFromCPDFPageObject(pImageObj.release());
123 }
124 
125 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_LoadJpegFile(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_FILEACCESS * file_access)126 FPDFImageObj_LoadJpegFile(FPDF_PAGE* pages,
127                           int count,
128                           FPDF_PAGEOBJECT image_object,
129                           FPDF_FILEACCESS* file_access) {
130   return LoadJpegHelper(pages, count, image_object, file_access, false);
131 }
132 
133 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_LoadJpegFileInline(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_FILEACCESS * file_access)134 FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages,
135                                 int count,
136                                 FPDF_PAGEOBJECT image_object,
137                                 FPDF_FILEACCESS* file_access) {
138   return LoadJpegHelper(pages, count, image_object, file_access, true);
139 }
140 
141 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,double a,double b,double c,double d,double e,double f)142 FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,
143                        double a,
144                        double b,
145                        double c,
146                        double d,
147                        double e,
148                        double f) {
149   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
150   if (!pImgObj)
151     return false;
152 
153   pImgObj->SetImageMatrix(CFX_Matrix(
154       static_cast<float>(a), static_cast<float>(b), static_cast<float>(c),
155       static_cast<float>(d), static_cast<float>(e), static_cast<float>(f)));
156   pImgObj->SetDirty(true);
157   return true;
158 }
159 
160 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_SetBitmap(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_BITMAP bitmap)161 FPDFImageObj_SetBitmap(FPDF_PAGE* pages,
162                        int count,
163                        FPDF_PAGEOBJECT image_object,
164                        FPDF_BITMAP bitmap) {
165   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
166   if (!pImgObj)
167     return false;
168 
169   if (!bitmap)
170     return false;
171 
172   if (pages) {
173     for (int index = 0; index < count; index++) {
174       CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
175       if (pPage)
176         pImgObj->GetImage()->ResetCache(pPage);
177     }
178   }
179 
180   RetainPtr<CFX_DIBitmap> holder(CFXDIBitmapFromFPDFBitmap(bitmap));
181   pImgObj->GetImage()->SetImage(holder);
182   pImgObj->CalcBoundingBox();
183   pImgObj->SetDirty(true);
184   return true;
185 }
186 
187 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object)188 FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object) {
189   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
190   if (!pImgObj)
191     return nullptr;
192 
193   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
194   if (!pImg)
195     return nullptr;
196 
197   RetainPtr<CFX_DIBBase> pSource = pImg->LoadDIBBase();
198   if (!pSource)
199     return nullptr;
200 
201   // If the source image has a representation of 1 bit per pixel, then convert
202   // it to a grayscale bitmap having 1 byte per pixel, since bitmaps have no
203   // concept of bits. Otherwise, convert the source image to a bitmap directly,
204   // retaining its color representation.
205   RetainPtr<CFX_DIBitmap> pBitmap =
206       pSource->GetBPP() == 1 ? pSource->ConvertTo(FXDIB_Format::k8bppRgb)
207                              : pSource->Realize();
208 
209   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
210 }
211 
212 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
FPDFImageObj_GetRenderedBitmap(FPDF_DOCUMENT document,FPDF_PAGE page,FPDF_PAGEOBJECT image_object)213 FPDFImageObj_GetRenderedBitmap(FPDF_DOCUMENT document,
214                                FPDF_PAGE page,
215                                FPDF_PAGEOBJECT image_object) {
216   CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
217   if (!doc)
218     return nullptr;
219 
220   CPDF_Page* optional_page = CPDFPageFromFPDFPage(page);
221   if (optional_page && optional_page->GetDocument() != doc)
222     return nullptr;
223 
224   CPDF_ImageObject* image = CPDFImageObjectFromFPDFPageObject(image_object);
225   if (!image)
226     return nullptr;
227 
228   // Create |result_bitmap|.
229   const CFX_Matrix& image_matrix = image->matrix();
230   int output_width = image_matrix.a;
231   int output_height = image_matrix.d;
232   auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
233   if (!result_bitmap->Create(output_width, output_height, FXDIB_Format::kArgb))
234     return nullptr;
235 
236   // Set up all the rendering code.
237   RetainPtr<CPDF_Dictionary> page_resources =
238       optional_page ? optional_page->GetMutablePageResources() : nullptr;
239   CPDF_RenderContext context(doc, std::move(page_resources),
240                              /*pPageCache=*/nullptr);
241   CFX_DefaultRenderDevice device;
242   device.Attach(result_bitmap);
243   CPDF_RenderStatus status(&context, &device);
244   CPDF_ImageRenderer renderer(&status);
245 
246   // Need to first flip the image, as expected by |renderer|.
247   CFX_Matrix render_matrix(1, 0, 0, -1, 0, output_height);
248 
249   // Then take |image_matrix|'s offset into account.
250   render_matrix.Translate(-image_matrix.e, image_matrix.f);
251 
252   // Do the actual rendering.
253   bool should_continue = renderer.Start(image, render_matrix,
254                                         /*bStdCS=*/false, BlendMode::kNormal);
255   while (should_continue)
256     should_continue = renderer.Continue(/*pPause=*/nullptr);
257 
258   if (!renderer.GetResult())
259     return nullptr;
260 
261 #if defined(_SKIA_SUPPORT_)
262   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
263     result_bitmap->UnPreMultiply();
264 #endif
265 
266   // Caller takes ownership.
267   return FPDFBitmapFromCFXDIBitmap(result_bitmap.Leak());
268 }
269 
270 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,void * buffer,unsigned long buflen)271 FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,
272                                  void* buffer,
273                                  unsigned long buflen) {
274   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
275   if (!pImgObj)
276     return 0;
277 
278   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
279   if (!pImg)
280     return 0;
281 
282   RetainPtr<const CPDF_Stream> pImgStream = pImg->GetStream();
283   if (!pImgStream)
284     return 0;
285 
286   return DecodeStreamMaybeCopyAndReturnLength(
287       std::move(pImgStream),
288       {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
289 }
290 
291 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object,void * buffer,unsigned long buflen)292 FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object,
293                              void* buffer,
294                              unsigned long buflen) {
295   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
296   if (!pImgObj)
297     return 0;
298 
299   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
300   if (!pImg)
301     return 0;
302 
303   RetainPtr<const CPDF_Stream> pImgStream = pImg->GetStream();
304   if (!pImgStream)
305     return 0;
306 
307   return GetRawStreamMaybeCopyAndReturnLength(
308       std::move(pImgStream),
309       {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
310 }
311 
312 FPDF_EXPORT int FPDF_CALLCONV
FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object)313 FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object) {
314   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
315   if (!pImgObj)
316     return 0;
317 
318   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
319   if (!pImg)
320     return 0;
321 
322   RetainPtr<const CPDF_Dictionary> pDict = pImg->GetDict();
323   if (!pDict)
324     return 0;
325 
326   RetainPtr<const CPDF_Object> pFilter = pDict->GetDirectObjectFor("Filter");
327   if (!pFilter)
328     return 0;
329 
330   if (pFilter->IsArray())
331     return fxcrt::CollectionSize<int>(*pFilter->AsArray());
332 
333   if (pFilter->IsName())
334     return 1;
335 
336   return 0;
337 }
338 
339 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object,int index,void * buffer,unsigned long buflen)340 FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object,
341                             int index,
342                             void* buffer,
343                             unsigned long buflen) {
344   if (index < 0 || index >= FPDFImageObj_GetImageFilterCount(image_object))
345     return 0;
346 
347   CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
348   RetainPtr<const CPDF_Dictionary> pDict =
349       pObj->AsImage()->GetImage()->GetDict();
350   RetainPtr<const CPDF_Object> pFilter = pDict->GetDirectObjectFor("Filter");
351   ByteString bsFilter = pFilter->IsName()
352                             ? pFilter->AsName()->GetString()
353                             : pFilter->AsArray()->GetByteStringAt(index);
354 
355   return NulTerminateMaybeCopyAndReturnLength(bsFilter, buffer, buflen);
356 }
357 
358 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object,FPDF_PAGE page,FPDF_IMAGEOBJ_METADATA * metadata)359 FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object,
360                               FPDF_PAGE page,
361                               FPDF_IMAGEOBJ_METADATA* metadata) {
362   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
363   if (!pImgObj || !metadata)
364     return false;
365 
366   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
367   if (!pImg)
368     return false;
369 
370   metadata->marked_content_id =
371       pImgObj->GetContentMarks()->GetMarkedContentID();
372 
373   const int nPixelWidth = pImg->GetPixelWidth();
374   const int nPixelHeight = pImg->GetPixelHeight();
375   metadata->width = nPixelWidth;
376   metadata->height = nPixelHeight;
377 
378   const float nWidth = pImgObj->GetRect().Width();
379   const float nHeight = pImgObj->GetRect().Height();
380   constexpr int nPointsPerInch = 72;
381   if (nWidth != 0 && nHeight != 0) {
382     metadata->horizontal_dpi = nPixelWidth / nWidth * nPointsPerInch;
383     metadata->vertical_dpi = nPixelHeight / nHeight * nPointsPerInch;
384   }
385 
386   metadata->bits_per_pixel = 0;
387   metadata->colorspace = FPDF_COLORSPACE_UNKNOWN;
388 
389   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
390   if (!pPage || !pPage->GetDocument() || !pImg->GetStream())
391     return true;
392 
393   // A cross-document image may have come from the embedder.
394   if (pPage->GetDocument() != pImg->GetDocument())
395     return false;
396 
397   RetainPtr<CPDF_DIB> pSource = pImg->CreateNewDIB();
398   CPDF_DIB::LoadState ret = pSource->StartLoadDIBBase(
399       false, nullptr, pPage->GetPageResources().Get(), false,
400       CPDF_ColorSpace::Family::kUnknown, false, {0, 0});
401   if (ret == CPDF_DIB::LoadState::kFail)
402     return true;
403 
404   metadata->bits_per_pixel = pSource->GetBPP();
405   if (pSource->GetColorSpace()) {
406     metadata->colorspace =
407         static_cast<int>(pSource->GetColorSpace()->GetFamily());
408   }
409   return true;
410 }
411 
412 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_GetImagePixelSize(FPDF_PAGEOBJECT image_object,unsigned int * width,unsigned int * height)413 FPDFImageObj_GetImagePixelSize(FPDF_PAGEOBJECT image_object,
414                                unsigned int* width,
415                                unsigned int* height) {
416   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
417   if (!pImgObj || !width || !height) {
418     return false;
419   }
420 
421   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
422   if (!pImg) {
423     return false;
424   }
425 
426   *width = pImg->GetPixelWidth();
427   *height = pImg->GetPixelHeight();
428   return true;
429 }
430