1 // Copyright 2016 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 "core/fpdfapi/page/cpdf_image.h"
8
9 #include <stdint.h>
10
11 #include <algorithm>
12 #include <memory>
13 #include <utility>
14
15 #include "constants/stream_dict_common.h"
16 #include "core/fpdfapi/page/cpdf_dib.h"
17 #include "core/fpdfapi/page/cpdf_page.h"
18 #include "core/fpdfapi/page/cpdf_pageimagecache.h"
19 #include "core/fpdfapi/parser/cpdf_array.h"
20 #include "core/fpdfapi/parser/cpdf_boolean.h"
21 #include "core/fpdfapi/parser/cpdf_dictionary.h"
22 #include "core/fpdfapi/parser/cpdf_document.h"
23 #include "core/fpdfapi/parser/cpdf_name.h"
24 #include "core/fpdfapi/parser/cpdf_number.h"
25 #include "core/fpdfapi/parser/cpdf_reference.h"
26 #include "core/fpdfapi/parser/cpdf_stream.h"
27 #include "core/fpdfapi/parser/cpdf_string.h"
28 #include "core/fxcodec/jpeg/jpegmodule.h"
29 #include "core/fxcrt/data_vector.h"
30 #include "core/fxcrt/fx_2d_size.h"
31 #include "core/fxcrt/fx_memory_wrappers.h"
32 #include "core/fxcrt/fx_stream.h"
33 #include "core/fxcrt/span_util.h"
34 #include "core/fxge/dib/cfx_dibitmap.h"
35 #include "core/fxge/dib/fx_dib.h"
36 #include "third_party/base/check.h"
37 #include "third_party/base/numerics/safe_conversions.h"
38
39 // static
IsValidJpegComponent(int32_t comps)40 bool CPDF_Image::IsValidJpegComponent(int32_t comps) {
41 return comps == 1 || comps == 3 || comps == 4;
42 }
43
44 // static
IsValidJpegBitsPerComponent(int32_t bpc)45 bool CPDF_Image::IsValidJpegBitsPerComponent(int32_t bpc) {
46 return bpc == 1 || bpc == 2 || bpc == 4 || bpc == 8 || bpc == 16;
47 }
48
CPDF_Image(CPDF_Document * pDoc)49 CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {
50 DCHECK(m_pDocument);
51 }
52
CPDF_Image(CPDF_Document * pDoc,RetainPtr<CPDF_Stream> pStream)53 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, RetainPtr<CPDF_Stream> pStream)
54 : m_bIsInline(true), m_pDocument(pDoc), m_pStream(std::move(pStream)) {
55 DCHECK(m_pDocument);
56 FinishInitialization();
57 }
58
CPDF_Image(CPDF_Document * pDoc,uint32_t dwStreamObjNum)59 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum)
60 : m_pDocument(pDoc),
61 m_pStream(ToStream(pDoc->GetMutableIndirectObject(dwStreamObjNum))) {
62 DCHECK(m_pDocument);
63 FinishInitialization();
64 }
65
66 CPDF_Image::~CPDF_Image() = default;
67
FinishInitialization()68 void CPDF_Image::FinishInitialization() {
69 RetainPtr<CPDF_Dictionary> pStreamDict = m_pStream->GetMutableDict();
70 m_pOC = pStreamDict->GetMutableDictFor("OC");
71 m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
72 pStreamDict->GetBooleanFor("ImageMask", /*bDefault=*/false);
73 m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
74 m_Height = pStreamDict->GetIntegerFor("Height");
75 m_Width = pStreamDict->GetIntegerFor("Width");
76 }
77
ConvertStreamToIndirectObject()78 void CPDF_Image::ConvertStreamToIndirectObject() {
79 if (!m_pStream->IsInline())
80 return;
81
82 m_pDocument->AddIndirectObject(m_pStream);
83 }
84
GetDict() const85 RetainPtr<const CPDF_Dictionary> CPDF_Image::GetDict() const {
86 return m_pStream ? m_pStream->GetDict() : nullptr;
87 }
88
GetStream() const89 RetainPtr<const CPDF_Stream> CPDF_Image::GetStream() const {
90 return m_pStream;
91 }
92
GetOC() const93 RetainPtr<const CPDF_Dictionary> CPDF_Image::GetOC() const {
94 return m_pOC;
95 }
96
InitJPEG(pdfium::span<uint8_t> src_span)97 RetainPtr<CPDF_Dictionary> CPDF_Image::InitJPEG(
98 pdfium::span<uint8_t> src_span) {
99 absl::optional<JpegModule::ImageInfo> info_opt =
100 JpegModule::LoadInfo(src_span);
101 if (!info_opt.has_value())
102 return nullptr;
103
104 const JpegModule::ImageInfo& info = info_opt.value();
105 if (!IsValidJpegComponent(info.num_components) ||
106 !IsValidJpegBitsPerComponent(info.bits_per_components)) {
107 return nullptr;
108 }
109
110 RetainPtr<CPDF_Dictionary> pDict =
111 CreateXObjectImageDict(info.width, info.height);
112 const char* csname = nullptr;
113 if (info.num_components == 1) {
114 csname = "DeviceGray";
115 } else if (info.num_components == 3) {
116 csname = "DeviceRGB";
117 } else if (info.num_components == 4) {
118 csname = "DeviceCMYK";
119 auto pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
120 for (int n = 0; n < 4; n++) {
121 pDecode->AppendNew<CPDF_Number>(1);
122 pDecode->AppendNew<CPDF_Number>(0);
123 }
124 }
125 pDict->SetNewFor<CPDF_Name>("ColorSpace", csname);
126 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", info.bits_per_components);
127 pDict->SetNewFor<CPDF_Name>("Filter", "DCTDecode");
128 if (!info.color_transform) {
129 auto pParms =
130 pDict->SetNewFor<CPDF_Dictionary>(pdfium::stream::kDecodeParms);
131 pParms->SetNewFor<CPDF_Number>("ColorTransform", 0);
132 }
133 m_bIsMask = false;
134 m_Width = info.width;
135 m_Height = info.height;
136 if (!m_pStream)
137 m_pStream = pdfium::MakeRetain<CPDF_Stream>();
138 return pDict;
139 }
140
SetJpegImage(RetainPtr<IFX_SeekableReadStream> pFile)141 void CPDF_Image::SetJpegImage(RetainPtr<IFX_SeekableReadStream> pFile) {
142 uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
143 if (!size)
144 return;
145
146 uint32_t dwEstimateSize = std::min(size, 8192U);
147 DataVector<uint8_t> data(dwEstimateSize);
148 if (!pFile->ReadBlockAtOffset(data, 0))
149 return;
150
151 RetainPtr<CPDF_Dictionary> pDict = InitJPEG(data);
152 if (!pDict && size > dwEstimateSize) {
153 data.resize(size);
154 if (pFile->ReadBlockAtOffset(data, 0))
155 pDict = InitJPEG(data);
156 }
157 if (!pDict)
158 return;
159
160 m_pStream->InitStreamFromFile(std::move(pFile), std::move(pDict));
161 }
162
SetJpegImageInline(RetainPtr<IFX_SeekableReadStream> pFile)163 void CPDF_Image::SetJpegImageInline(RetainPtr<IFX_SeekableReadStream> pFile) {
164 uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
165 if (!size)
166 return;
167
168 DataVector<uint8_t> data(size);
169 if (!pFile->ReadBlockAtOffset(data, 0))
170 return;
171
172 RetainPtr<CPDF_Dictionary> pDict = InitJPEG(data);
173 if (!pDict)
174 return;
175
176 m_pStream =
177 pdfium::MakeRetain<CPDF_Stream>(std::move(data), std::move(pDict));
178 }
179
SetImage(const RetainPtr<CFX_DIBitmap> & pBitmap)180 void CPDF_Image::SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap) {
181 int32_t BitmapWidth = pBitmap->GetWidth();
182 int32_t BitmapHeight = pBitmap->GetHeight();
183 if (BitmapWidth < 1 || BitmapHeight < 1)
184 return;
185
186 RetainPtr<CPDF_Dictionary> pDict =
187 CreateXObjectImageDict(BitmapWidth, BitmapHeight);
188 const int32_t bpp = pBitmap->GetBPP();
189 size_t dest_pitch = 0;
190 bool bCopyWithoutAlpha = true;
191 if (bpp == 1) {
192 int32_t reset_a = 0;
193 int32_t reset_r = 0;
194 int32_t reset_g = 0;
195 int32_t reset_b = 0;
196 int32_t set_a = 0;
197 int32_t set_r = 0;
198 int32_t set_g = 0;
199 int32_t set_b = 0;
200 if (!pBitmap->IsMaskFormat()) {
201 std::tie(reset_a, reset_r, reset_g, reset_b) =
202 ArgbDecode(pBitmap->GetPaletteArgb(0));
203 std::tie(set_a, set_r, set_g, set_b) =
204 ArgbDecode(pBitmap->GetPaletteArgb(1));
205 }
206 if (set_a == 0 || reset_a == 0) {
207 pDict->SetNewFor<CPDF_Boolean>("ImageMask", true);
208 if (reset_a == 0) {
209 auto pArray = pDict->SetNewFor<CPDF_Array>("Decode");
210 pArray->AppendNew<CPDF_Number>(1);
211 pArray->AppendNew<CPDF_Number>(0);
212 }
213 } else {
214 auto pCS = pDict->SetNewFor<CPDF_Array>("ColorSpace");
215 pCS->AppendNew<CPDF_Name>("Indexed");
216 pCS->AppendNew<CPDF_Name>("DeviceRGB");
217 pCS->AppendNew<CPDF_Number>(1);
218 ByteString ct;
219 {
220 // Span's lifetime must end before ReleaseBuffer() below.
221 pdfium::span<char> pBuf = ct.GetBuffer(6);
222 pBuf[0] = static_cast<char>(reset_r);
223 pBuf[1] = static_cast<char>(reset_g);
224 pBuf[2] = static_cast<char>(reset_b);
225 pBuf[3] = static_cast<char>(set_r);
226 pBuf[4] = static_cast<char>(set_g);
227 pBuf[5] = static_cast<char>(set_b);
228 }
229 ct.ReleaseBuffer(6);
230 pCS->AppendNew<CPDF_String>(ct, true);
231 }
232 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
233 dest_pitch = (BitmapWidth + 7) / 8;
234 } else if (bpp == 8) {
235 size_t palette_size = pBitmap->GetRequiredPaletteSize();
236 if (palette_size > 0) {
237 DCHECK(palette_size <= 256);
238 auto pCS = m_pDocument->NewIndirect<CPDF_Array>();
239 pCS->AppendNew<CPDF_Name>("Indexed");
240 pCS->AppendNew<CPDF_Name>("DeviceRGB");
241 pCS->AppendNew<CPDF_Number>(static_cast<int>(palette_size - 1));
242 DataVector<uint8_t> color_table(Fx2DSizeOrDie(palette_size, 3));
243 auto color_table_span = pdfium::make_span(color_table);
244 for (size_t i = 0; i < palette_size; i++) {
245 uint32_t argb = pBitmap->GetPaletteArgb(i);
246 color_table_span[0] = FXARGB_R(argb);
247 color_table_span[1] = FXARGB_G(argb);
248 color_table_span[2] = FXARGB_B(argb);
249 color_table_span = color_table_span.subspan(3);
250 }
251 auto pNewDict = m_pDocument->New<CPDF_Dictionary>();
252 auto pCTS = m_pDocument->NewIndirect<CPDF_Stream>(std::move(color_table),
253 std::move(pNewDict));
254 pCS->AppendNew<CPDF_Reference>(m_pDocument, pCTS->GetObjNum());
255 pDict->SetNewFor<CPDF_Reference>("ColorSpace", m_pDocument,
256 pCS->GetObjNum());
257 } else {
258 pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
259 }
260 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
261 dest_pitch = BitmapWidth;
262 } else {
263 pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceRGB");
264 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
265 dest_pitch = BitmapWidth * 3;
266 bCopyWithoutAlpha = false;
267 }
268
269 RetainPtr<CFX_DIBitmap> pMaskBitmap;
270 if (pBitmap->IsAlphaFormat())
271 pMaskBitmap = pBitmap->CloneAlphaMask();
272
273 if (pMaskBitmap) {
274 const int32_t mask_width = pMaskBitmap->GetWidth();
275 const int32_t mask_height = pMaskBitmap->GetHeight();
276 DataVector<uint8_t> mask_buf;
277 RetainPtr<CPDF_Dictionary> pMaskDict =
278 CreateXObjectImageDict(mask_width, mask_height);
279 pMaskDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
280 pMaskDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
281 if (pMaskBitmap->GetFormat() != FXDIB_Format::k1bppMask) {
282 mask_buf.resize(Fx2DSizeOrDie(mask_width, mask_height));
283 for (int32_t a = 0; a < mask_height; a++) {
284 fxcrt::spancpy(pdfium::make_span(mask_buf).subspan(a * mask_width),
285 pMaskBitmap->GetScanline(a).first(mask_width));
286 }
287 }
288 pMaskDict->SetNewFor<CPDF_Number>(
289 "Length", pdfium::base::checked_cast<int>(mask_buf.size()));
290 auto pNewStream = m_pDocument->NewIndirect<CPDF_Stream>(
291 std::move(mask_buf), std::move(pMaskDict));
292 pDict->SetNewFor<CPDF_Reference>("SMask", m_pDocument,
293 pNewStream->GetObjNum());
294 }
295
296 DataVector<uint8_t> dest_buf(Fx2DSizeOrDie(dest_pitch, BitmapHeight));
297 pdfium::span<uint8_t> dest_span = pdfium::make_span(dest_buf);
298 pdfium::span<const uint8_t> src_span = pBitmap->GetBuffer();
299 const int32_t src_pitch = pBitmap->GetPitch();
300 if (bCopyWithoutAlpha) {
301 for (int32_t i = 0; i < BitmapHeight; i++) {
302 fxcrt::spancpy(dest_span, src_span.first(dest_pitch));
303 dest_span = dest_span.subspan(dest_pitch);
304 src_span = src_span.subspan(src_pitch);
305 }
306 } else {
307 const size_t src_step = bpp == 24 ? 3 : 4;
308 for (int32_t row = 0; row < BitmapHeight; row++) {
309 uint8_t* dest_ptr = dest_span.data();
310 const uint8_t* src_ptr = src_span.data();
311 for (int32_t column = 0; column < BitmapWidth; column++) {
312 dest_ptr[0] = src_ptr[2];
313 dest_ptr[1] = src_ptr[1];
314 dest_ptr[2] = src_ptr[0];
315 dest_ptr += 3;
316 src_ptr += src_step;
317 }
318 dest_span = dest_span.subspan(dest_pitch);
319 src_span = src_span.subspan(src_pitch);
320 }
321 }
322
323 m_pStream =
324 pdfium::MakeRetain<CPDF_Stream>(std::move(dest_buf), std::move(pDict));
325 m_bIsMask = pBitmap->IsMaskFormat();
326 m_Width = BitmapWidth;
327 m_Height = BitmapHeight;
328 }
329
ResetCache(CPDF_Page * pPage)330 void CPDF_Image::ResetCache(CPDF_Page* pPage) {
331 RetainPtr<CPDF_Image> pHolder(this);
332 pPage->GetPageImageCache()->ResetBitmapForImage(std::move(pHolder));
333 }
334
CreateNewDIB() const335 RetainPtr<CPDF_DIB> CPDF_Image::CreateNewDIB() const {
336 return pdfium::MakeRetain<CPDF_DIB>(GetDocument(), GetStream());
337 }
338
LoadDIBBase() const339 RetainPtr<CFX_DIBBase> CPDF_Image::LoadDIBBase() const {
340 RetainPtr<CPDF_DIB> source = CreateNewDIB();
341 if (!source->Load())
342 return nullptr;
343
344 if (!source->IsJBigImage())
345 return source;
346
347 CPDF_DIB::LoadState ret = CPDF_DIB::LoadState::kContinue;
348 while (ret == CPDF_DIB::LoadState::kContinue)
349 ret = source->ContinueLoadDIBBase(nullptr);
350 return ret == CPDF_DIB::LoadState::kSuccess ? source : nullptr;
351 }
352
DetachBitmap()353 RetainPtr<CFX_DIBBase> CPDF_Image::DetachBitmap() {
354 return std::move(m_pDIBBase);
355 }
356
DetachMask()357 RetainPtr<CFX_DIBBase> CPDF_Image::DetachMask() {
358 return std::move(m_pMask);
359 }
360
StartLoadDIBBase(const CPDF_Dictionary * pFormResource,const CPDF_Dictionary * pPageResource,bool bStdCS,CPDF_ColorSpace::Family GroupFamily,bool bLoadMask,const CFX_Size & max_size_required)361 bool CPDF_Image::StartLoadDIBBase(const CPDF_Dictionary* pFormResource,
362 const CPDF_Dictionary* pPageResource,
363 bool bStdCS,
364 CPDF_ColorSpace::Family GroupFamily,
365 bool bLoadMask,
366 const CFX_Size& max_size_required) {
367 RetainPtr<CPDF_DIB> source = CreateNewDIB();
368 CPDF_DIB::LoadState ret =
369 source->StartLoadDIBBase(true, pFormResource, pPageResource, bStdCS,
370 GroupFamily, bLoadMask, max_size_required);
371 if (ret == CPDF_DIB::LoadState::kFail) {
372 m_pDIBBase.Reset();
373 return false;
374 }
375 m_pDIBBase = source;
376 if (ret == CPDF_DIB::LoadState::kContinue)
377 return true;
378
379 m_pMask = source->DetachMask();
380 m_MatteColor = source->GetMatteColor();
381 return false;
382 }
383
Continue(PauseIndicatorIface * pPause)384 bool CPDF_Image::Continue(PauseIndicatorIface* pPause) {
385 RetainPtr<CPDF_DIB> pSource = m_pDIBBase.As<CPDF_DIB>();
386 CPDF_DIB::LoadState ret = pSource->ContinueLoadDIBBase(pPause);
387 if (ret == CPDF_DIB::LoadState::kContinue)
388 return true;
389
390 if (ret == CPDF_DIB::LoadState::kSuccess) {
391 m_pMask = pSource->DetachMask();
392 m_MatteColor = pSource->GetMatteColor();
393 } else {
394 m_pDIBBase.Reset();
395 }
396 return false;
397 }
398
CreateXObjectImageDict(int width,int height)399 RetainPtr<CPDF_Dictionary> CPDF_Image::CreateXObjectImageDict(int width,
400 int height) {
401 auto dict = m_pDocument->New<CPDF_Dictionary>();
402 dict->SetNewFor<CPDF_Name>("Type", "XObject");
403 dict->SetNewFor<CPDF_Name>("Subtype", "Image");
404 dict->SetNewFor<CPDF_Number>("Width", width);
405 dict->SetNewFor<CPDF_Number>("Height", height);
406 return dict;
407 }
408