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