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