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