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