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