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