• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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