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_edit.h"
8 
9 #include <utility>
10 
11 #include "core/fpdfapi/page/cpdf_dib.h"
12 #include "core/fpdfapi/page/cpdf_image.h"
13 #include "core/fpdfapi/page/cpdf_imageobject.h"
14 #include "core/fpdfapi/page/cpdf_page.h"
15 #include "core/fpdfapi/page/cpdf_pageobject.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_dictionary.h"
18 #include "core/fpdfapi/parser/cpdf_name.h"
19 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
20 #include "fpdfsdk/cpdfsdk_customaccess.h"
21 #include "fpdfsdk/cpdfsdk_helpers.h"
22 #include "third_party/base/ptr_util.h"
23 
24 namespace {
25 
26 // These checks ensure the consistency of colorspace values across core/ and
27 // public/.
28 static_assert(PDFCS_DEVICEGRAY == FPDF_COLORSPACE_DEVICEGRAY,
29               "PDFCS_DEVICEGRAY value mismatch");
30 static_assert(PDFCS_DEVICERGB == FPDF_COLORSPACE_DEVICERGB,
31               "PDFCS_DEVICERGB value mismatch");
32 static_assert(PDFCS_DEVICECMYK == FPDF_COLORSPACE_DEVICECMYK,
33               "PDFCS_DEVICECMYK value mismatch");
34 static_assert(PDFCS_CALGRAY == FPDF_COLORSPACE_CALGRAY,
35               "PDFCS_CALGRAY value mismatch");
36 static_assert(PDFCS_CALRGB == FPDF_COLORSPACE_CALRGB,
37               "PDFCS_CALRGB value mismatch");
38 static_assert(PDFCS_LAB == FPDF_COLORSPACE_LAB, "PDFCS_LAB value mismatch");
39 static_assert(PDFCS_ICCBASED == FPDF_COLORSPACE_ICCBASED,
40               "PDFCS_ICCBASED value mismatch");
41 static_assert(PDFCS_SEPARATION == FPDF_COLORSPACE_SEPARATION,
42               "PDFCS_SEPARATION value mismatch");
43 static_assert(PDFCS_DEVICEN == FPDF_COLORSPACE_DEVICEN,
44               "PDFCS_DEVICEN value mismatch");
45 static_assert(PDFCS_INDEXED == FPDF_COLORSPACE_INDEXED,
46               "PDFCS_INDEXED value mismatch");
47 static_assert(PDFCS_PATTERN == FPDF_COLORSPACE_PATTERN,
48               "PDFCS_PATTERN value mismatch");
49 
MakeSeekableReadStream(FPDF_FILEACCESS * pFileAccess)50 RetainPtr<IFX_SeekableReadStream> MakeSeekableReadStream(
51     FPDF_FILEACCESS* pFileAccess) {
52   return pdfium::MakeRetain<CPDFSDK_CustomAccess>(pFileAccess);
53 }
54 
CPDFImageObjectFromFPDFPageObject(FPDF_PAGEOBJECT image_object)55 CPDF_ImageObject* CPDFImageObjectFromFPDFPageObject(
56     FPDF_PAGEOBJECT image_object) {
57   CPDF_PageObject* pPageObject = CPDFPageObjectFromFPDFPageObject(image_object);
58   return pPageObject ? pPageObject->AsImage() : nullptr;
59 }
60 
LoadJpegHelper(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_FILEACCESS * file_access,bool inline_jpeg)61 bool LoadJpegHelper(FPDF_PAGE* pages,
62                     int count,
63                     FPDF_PAGEOBJECT image_object,
64                     FPDF_FILEACCESS* file_access,
65                     bool inline_jpeg) {
66   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
67   if (!pImgObj)
68     return false;
69 
70   if (!file_access)
71     return false;
72 
73   if (pages) {
74     for (int index = 0; index < count; index++) {
75       CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
76       if (pPage)
77         pImgObj->GetImage()->ResetCache(pPage);
78     }
79   }
80 
81   RetainPtr<IFX_SeekableReadStream> pFile = MakeSeekableReadStream(file_access);
82   if (inline_jpeg)
83     pImgObj->GetImage()->SetJpegImageInline(pFile);
84   else
85     pImgObj->GetImage()->SetJpegImage(pFile);
86   pImgObj->SetDirty(true);
87   return true;
88 }
89 
90 }  // namespace
91 
92 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFPageObj_NewImageObj(FPDF_DOCUMENT document)93 FPDFPageObj_NewImageObj(FPDF_DOCUMENT document) {
94   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
95   if (!pDoc)
96     return nullptr;
97 
98   auto pImageObj = pdfium::MakeUnique<CPDF_ImageObject>();
99   pImageObj->SetImage(pdfium::MakeRetain<CPDF_Image>(pDoc));
100 
101   // Caller takes ownership.
102   return FPDFPageObjectFromCPDFPageObject(pImageObj.release());
103 }
104 
105 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_LoadJpegFile(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_FILEACCESS * file_access)106 FPDFImageObj_LoadJpegFile(FPDF_PAGE* pages,
107                           int count,
108                           FPDF_PAGEOBJECT image_object,
109                           FPDF_FILEACCESS* file_access) {
110   return LoadJpegHelper(pages, count, image_object, file_access, false);
111 }
112 
113 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_LoadJpegFileInline(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_FILEACCESS * file_access)114 FPDFImageObj_LoadJpegFileInline(FPDF_PAGE* pages,
115                                 int count,
116                                 FPDF_PAGEOBJECT image_object,
117                                 FPDF_FILEACCESS* file_access) {
118   return LoadJpegHelper(pages, count, image_object, file_access, true);
119 }
120 
121 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object,double * a,double * b,double * c,double * d,double * e,double * f)122 FPDFImageObj_GetMatrix(FPDF_PAGEOBJECT image_object,
123                        double* a,
124                        double* b,
125                        double* c,
126                        double* d,
127                        double* e,
128                        double* f) {
129   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
130   if (!pImgObj || !a || !b || !c || !d || !e || !f)
131     return false;
132 
133   const CFX_Matrix& matrix = pImgObj->matrix();
134   *a = matrix.a;
135   *b = matrix.b;
136   *c = matrix.c;
137   *d = matrix.d;
138   *e = matrix.e;
139   *f = matrix.f;
140   return true;
141 }
142 
143 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,double a,double b,double c,double d,double e,double f)144 FPDFImageObj_SetMatrix(FPDF_PAGEOBJECT image_object,
145                        double a,
146                        double b,
147                        double c,
148                        double d,
149                        double e,
150                        double f) {
151   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
152   if (!pImgObj)
153     return false;
154 
155   pImgObj->set_matrix(CFX_Matrix(static_cast<float>(a), static_cast<float>(b),
156                                  static_cast<float>(c), static_cast<float>(d),
157                                  static_cast<float>(e), static_cast<float>(f)));
158   pImgObj->CalcBoundingBox();
159   pImgObj->SetDirty(true);
160   return true;
161 }
162 
163 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_SetBitmap(FPDF_PAGE * pages,int count,FPDF_PAGEOBJECT image_object,FPDF_BITMAP bitmap)164 FPDFImageObj_SetBitmap(FPDF_PAGE* pages,
165                        int count,
166                        FPDF_PAGEOBJECT image_object,
167                        FPDF_BITMAP bitmap) {
168   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
169   if (!pImgObj)
170     return false;
171 
172   if (!bitmap)
173     return false;
174 
175   if (pages) {
176     for (int index = 0; index < count; index++) {
177       CPDF_Page* pPage = CPDFPageFromFPDFPage(pages[index]);
178       if (pPage)
179         pImgObj->GetImage()->ResetCache(pPage);
180     }
181   }
182 
183   RetainPtr<CFX_DIBitmap> holder(CFXDIBitmapFromFPDFBitmap(bitmap));
184   pImgObj->GetImage()->SetImage(holder);
185   pImgObj->CalcBoundingBox();
186   pImgObj->SetDirty(true);
187   return true;
188 }
189 
190 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV
FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object)191 FPDFImageObj_GetBitmap(FPDF_PAGEOBJECT image_object) {
192   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
193   if (!pImgObj)
194     return nullptr;
195 
196   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
197   if (!pImg)
198     return nullptr;
199 
200   RetainPtr<CFX_DIBBase> pSource = pImg->LoadDIBBase();
201   if (!pSource)
202     return nullptr;
203 
204   RetainPtr<CFX_DIBitmap> pBitmap;
205   // If the source image has a representation of 1 bit per pixel, then convert
206   // it to a grayscale bitmap having 1 byte per pixel, since bitmaps have no
207   // concept of bits. Otherwise, convert the source image to a bitmap directly,
208   // retaining its color representation.
209   if (pSource->GetBPP() == 1)
210     pBitmap = pSource->CloneConvert(FXDIB_8bppRgb);
211   else
212     pBitmap = pSource->Clone(nullptr);
213 
214   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
215 }
216 
217 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,void * buffer,unsigned long buflen)218 FPDFImageObj_GetImageDataDecoded(FPDF_PAGEOBJECT image_object,
219                                  void* buffer,
220                                  unsigned long buflen) {
221   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
222   if (!pImgObj)
223     return 0;
224 
225   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
226   if (!pImg)
227     return 0;
228 
229   CPDF_Stream* pImgStream = pImg->GetStream();
230   if (!pImgStream)
231     return 0;
232 
233   return DecodeStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
234 }
235 
236 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object,void * buffer,unsigned long buflen)237 FPDFImageObj_GetImageDataRaw(FPDF_PAGEOBJECT image_object,
238                              void* buffer,
239                              unsigned long buflen) {
240   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
241   if (!pImgObj)
242     return 0;
243 
244   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
245   if (!pImg)
246     return 0;
247 
248   CPDF_Stream* pImgStream = pImg->GetStream();
249   if (!pImgStream)
250     return 0;
251 
252   return GetRawStreamMaybeCopyAndReturnLength(pImgStream, buffer, buflen);
253 }
254 
255 FPDF_EXPORT int FPDF_CALLCONV
FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object)256 FPDFImageObj_GetImageFilterCount(FPDF_PAGEOBJECT image_object) {
257   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
258   if (!pImgObj)
259     return 0;
260 
261   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
262   if (!pImg)
263     return 0;
264 
265   CPDF_Dictionary* pDict = pImg->GetDict();
266   CPDF_Object* pFilter = pDict ? pDict->GetDirectObjectFor("Filter") : nullptr;
267   if (!pFilter)
268     return 0;
269 
270   if (pFilter->IsArray())
271     return pFilter->AsArray()->size();
272   if (pFilter->IsName())
273     return 1;
274 
275   return 0;
276 }
277 
278 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object,int index,void * buffer,unsigned long buflen)279 FPDFImageObj_GetImageFilter(FPDF_PAGEOBJECT image_object,
280                             int index,
281                             void* buffer,
282                             unsigned long buflen) {
283   if (index < 0 || index >= FPDFImageObj_GetImageFilterCount(image_object))
284     return 0;
285 
286   CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(image_object);
287   CPDF_Object* pFilter =
288       pObj->AsImage()->GetImage()->GetDict()->GetDirectObjectFor("Filter");
289   ByteString bsFilter;
290   if (pFilter->IsName())
291     bsFilter = pFilter->AsName()->GetString();
292   else
293     bsFilter = pFilter->AsArray()->GetStringAt(index);
294 
295   unsigned long len = bsFilter.GetLength() + 1;
296   if (buffer && len <= buflen)
297     memcpy(buffer, bsFilter.c_str(), len);
298   return len;
299 }
300 
301 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object,FPDF_PAGE page,FPDF_IMAGEOBJ_METADATA * metadata)302 FPDFImageObj_GetImageMetadata(FPDF_PAGEOBJECT image_object,
303                               FPDF_PAGE page,
304                               FPDF_IMAGEOBJ_METADATA* metadata) {
305   CPDF_ImageObject* pImgObj = CPDFImageObjectFromFPDFPageObject(image_object);
306   if (!pImgObj || !metadata)
307     return false;
308 
309   RetainPtr<CPDF_Image> pImg = pImgObj->GetImage();
310   if (!pImg)
311     return false;
312 
313   metadata->marked_content_id = pImgObj->m_ContentMarks.GetMarkedContentID();
314 
315   const int nPixelWidth = pImg->GetPixelWidth();
316   const int nPixelHeight = pImg->GetPixelHeight();
317   metadata->width = nPixelWidth;
318   metadata->height = nPixelHeight;
319 
320   const float nWidth = pImgObj->GetRect().Width();
321   const float nHeight = pImgObj->GetRect().Height();
322   constexpr int nPointsPerInch = 72;
323   if (nWidth != 0 && nHeight != 0) {
324     metadata->horizontal_dpi = nPixelWidth / nWidth * nPointsPerInch;
325     metadata->vertical_dpi = nPixelHeight / nHeight * nPointsPerInch;
326   }
327 
328   metadata->bits_per_pixel = 0;
329   metadata->colorspace = FPDF_COLORSPACE_UNKNOWN;
330 
331   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
332   if (!pPage || !pPage->GetDocument() || !pImg->GetStream())
333     return true;
334 
335   auto pSource = pdfium::MakeRetain<CPDF_DIB>();
336   CPDF_DIB::LoadState ret = pSource->StartLoadDIBBase(
337       pPage->GetDocument(), pImg->GetStream(), false, nullptr,
338       pPage->m_pPageResources.Get(), false, 0, false);
339   if (ret == CPDF_DIB::LoadState::kFail)
340     return true;
341 
342   metadata->bits_per_pixel = pSource->GetBPP();
343   if (pSource->GetColorSpace())
344     metadata->colorspace = pSource->GetColorSpace()->GetFamily();
345 
346   return true;
347 }
348