1// Copyright 2014 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_docpagedata.h"
8
9#include <algorithm>
10#include <memory>
11#include <set>
12#include <utility>
13
14#include "core/fdrm/crypto/fx_crypt.h"
15#include "core/fpdfapi/cpdf_modulemgr.h"
16#include "core/fpdfapi/font/cpdf_type1font.h"
17#include "core/fpdfapi/page/cpdf_iccprofile.h"
18#include "core/fpdfapi/page/cpdf_image.h"
19#include "core/fpdfapi/page/cpdf_pagemodule.h"
20#include "core/fpdfapi/page/cpdf_pattern.h"
21#include "core/fpdfapi/page/cpdf_shadingpattern.h"
22#include "core/fpdfapi/page/cpdf_tilingpattern.h"
23#include "core/fpdfapi/parser/cpdf_array.h"
24#include "core/fpdfapi/parser/cpdf_dictionary.h"
25#include "core/fpdfapi/parser/cpdf_document.h"
26#include "core/fpdfapi/parser/cpdf_name.h"
27#include "core/fpdfapi/parser/cpdf_stream_acc.h"
28#include "third_party/base/stl_util.h"
29
30CPDF_DocPageData::CPDF_DocPageData(CPDF_Document* pPDFDoc)
31    : m_bForceClear(false), m_pPDFDoc(pPDFDoc) {
32  assert(m_pPDFDoc);
33}
34
35CPDF_DocPageData::~CPDF_DocPageData() {
36  Clear(false);
37  Clear(true);
38
39  for (auto& it : m_PatternMap)
40    delete it.second;
41  m_PatternMap.clear();
42
43  for (auto& it : m_FontMap)
44    delete it.second;
45  m_FontMap.clear();
46
47  for (auto& it : m_ColorSpaceMap)
48    delete it.second;
49  m_ColorSpaceMap.clear();
50}
51
52void CPDF_DocPageData::Clear(bool bForceRelease) {
53  m_bForceClear = bForceRelease;
54
55  // This is needed because if |bForceRelease| is true we will destroy any
56  // pattern we see regardless of the ref-count. The tiling pattern owns a
57  // Form object which owns a ShadingObject. The ShadingObject has an unowned
58  // pointer to a ShadingPattern. The ShadingPattern is owned by the
59  // DocPageData. So, we loop through and clear any tiling patterns before we
60  // do the same for any shading patterns, otherwise we may free the
61  // ShadingPattern before the ShadingObject and trigger an unowned pointer
62  // probe warning.
63  for (auto& it : m_PatternMap) {
64    CPDF_CountedPattern* ptData = it.second;
65    if (!ptData->get() || !ptData->get()->AsTilingPattern())
66      continue;
67    if (bForceRelease || ptData->use_count() < 2)
68      ptData->clear();
69  }
70
71  for (auto& it : m_PatternMap) {
72    CPDF_CountedPattern* ptData = it.second;
73    if (!ptData->get())
74      continue;
75    if (bForceRelease || ptData->use_count() < 2)
76      ptData->clear();
77  }
78
79  for (auto& it : m_FontMap) {
80    CPDF_CountedFont* fontData = it.second;
81    if (!fontData->get())
82      continue;
83    if (bForceRelease || fontData->use_count() < 2) {
84      fontData->clear();
85    }
86  }
87
88  for (auto& it : m_ColorSpaceMap) {
89    CPDF_CountedColorSpace* csData = it.second;
90    if (!csData->get())
91      continue;
92    if (bForceRelease || csData->use_count() < 2) {
93      csData->get()->Release();
94      csData->reset(nullptr);
95    }
96  }
97
98  for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end();) {
99    auto curr_it = it++;
100    if (bForceRelease || curr_it->second->HasOneRef()) {
101      for (auto hash_it = m_HashProfileMap.begin();
102           hash_it != m_HashProfileMap.end(); ++hash_it) {
103        if (curr_it->first == hash_it->second) {
104          m_HashProfileMap.erase(hash_it);
105          break;
106        }
107      }
108      m_IccProfileMap.erase(curr_it);
109    }
110  }
111
112  for (auto it = m_FontFileMap.begin(); it != m_FontFileMap.end();) {
113    auto curr_it = it++;
114    if (bForceRelease || curr_it->second->HasOneRef())
115      m_FontFileMap.erase(curr_it);
116  }
117
118  m_ImageMap.clear();
119}
120
121CPDF_Font* CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) {
122  if (!pFontDict)
123    return nullptr;
124
125  CPDF_CountedFont* pFontData = nullptr;
126  auto it = m_FontMap.find(pFontDict);
127  if (it != m_FontMap.end()) {
128    pFontData = it->second;
129    if (pFontData->get()) {
130      return pFontData->AddRef();
131    }
132  }
133  std::unique_ptr<CPDF_Font> pFont =
134      CPDF_Font::Create(m_pPDFDoc.Get(), pFontDict);
135  if (!pFont)
136    return nullptr;
137
138  if (pFontData) {
139    pFontData->reset(std::move(pFont));
140  } else {
141    pFontData = new CPDF_CountedFont(std::move(pFont));
142    m_FontMap[pFontDict] = pFontData;
143  }
144  return pFontData->AddRef();
145}
146
147CPDF_Font* CPDF_DocPageData::GetStandardFont(const ByteString& fontName,
148                                             CPDF_FontEncoding* pEncoding) {
149  if (fontName.IsEmpty())
150    return nullptr;
151
152  for (auto& it : m_FontMap) {
153    CPDF_CountedFont* fontData = it.second;
154    CPDF_Font* pFont = fontData->get();
155    if (!pFont)
156      continue;
157    if (pFont->GetBaseFont() != fontName)
158      continue;
159    if (pFont->IsEmbedded())
160      continue;
161    if (!pFont->IsType1Font())
162      continue;
163    if (pFont->GetFontDict()->KeyExist("Widths"))
164      continue;
165
166    CPDF_Type1Font* pT1Font = pFont->AsType1Font();
167    if (pEncoding && !pT1Font->GetEncoding()->IsIdentical(pEncoding))
168      continue;
169
170    return fontData->AddRef();
171  }
172
173  CPDF_Dictionary* pDict = m_pPDFDoc->NewIndirect<CPDF_Dictionary>();
174  pDict->SetNewFor<CPDF_Name>("Type", "Font");
175  pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
176  pDict->SetNewFor<CPDF_Name>("BaseFont", fontName);
177  if (pEncoding) {
178    pDict->SetFor("Encoding",
179                  pEncoding->Realize(m_pPDFDoc->GetByteStringPool()));
180  }
181
182  std::unique_ptr<CPDF_Font> pFont = CPDF_Font::Create(m_pPDFDoc.Get(), pDict);
183  if (!pFont)
184    return nullptr;
185
186  CPDF_CountedFont* fontData = new CPDF_CountedFont(std::move(pFont));
187  m_FontMap[pDict] = fontData;
188  return fontData->AddRef();
189}
190
191void CPDF_DocPageData::ReleaseFont(const CPDF_Dictionary* pFontDict) {
192  if (!pFontDict)
193    return;
194
195  auto it = m_FontMap.find(pFontDict);
196  if (it == m_FontMap.end())
197    return;
198
199  CPDF_CountedFont* pFontData = it->second;
200  if (!pFontData->get())
201    return;
202
203  pFontData->RemoveRef();
204  if (pFontData->use_count() > 1)
205    return;
206
207  // We have font data only in m_FontMap cache. Clean it.
208  pFontData->clear();
209}
210
211CPDF_ColorSpace* CPDF_DocPageData::GetColorSpace(
212    CPDF_Object* pCSObj,
213    const CPDF_Dictionary* pResources) {
214  std::set<CPDF_Object*> visited;
215  return GetColorSpaceGuarded(pCSObj, pResources, &visited);
216}
217
218CPDF_ColorSpace* CPDF_DocPageData::GetColorSpaceGuarded(
219    CPDF_Object* pCSObj,
220    const CPDF_Dictionary* pResources,
221    std::set<CPDF_Object*>* pVisited) {
222  if (!pCSObj)
223    return nullptr;
224
225  if (pdfium::ContainsKey(*pVisited, pCSObj))
226    return nullptr;
227
228  pdfium::ScopedSetInsertion<CPDF_Object*> insertion(pVisited, pCSObj);
229
230  if (pCSObj->IsName()) {
231    ByteString name = pCSObj->GetString();
232    CPDF_ColorSpace* pCS = CPDF_ColorSpace::ColorspaceFromName(name);
233    if (!pCS && pResources) {
234      CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace");
235      if (pList) {
236        return GetColorSpaceGuarded(pList->GetDirectObjectFor(name), nullptr,
237                                    pVisited);
238      }
239    }
240    if (!pCS || !pResources)
241      return pCS;
242
243    CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace");
244    if (!pColorSpaces)
245      return pCS;
246
247    CPDF_Object* pDefaultCS = nullptr;
248    switch (pCS->GetFamily()) {
249      case PDFCS_DEVICERGB:
250        pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultRGB");
251        break;
252      case PDFCS_DEVICEGRAY:
253        pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultGray");
254        break;
255      case PDFCS_DEVICECMYK:
256        pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultCMYK");
257        break;
258    }
259    if (!pDefaultCS)
260      return pCS;
261
262    return GetColorSpaceGuarded(pDefaultCS, nullptr, pVisited);
263  }
264
265  CPDF_Array* pArray = pCSObj->AsArray();
266  if (!pArray || pArray->IsEmpty())
267    return nullptr;
268
269  if (pArray->GetCount() == 1) {
270    return GetColorSpaceGuarded(pArray->GetDirectObjectAt(0), pResources,
271                                pVisited);
272  }
273
274  CPDF_CountedColorSpace* csData = nullptr;
275  auto it = m_ColorSpaceMap.find(pCSObj);
276  if (it != m_ColorSpaceMap.end()) {
277    csData = it->second;
278    if (csData->get()) {
279      return csData->AddRef();
280    }
281  }
282
283  std::unique_ptr<CPDF_ColorSpace> pCS =
284      CPDF_ColorSpace::Load(m_pPDFDoc.Get(), pArray, pVisited);
285  if (!pCS)
286    return nullptr;
287
288  if (csData) {
289    csData->reset(std::move(pCS));
290  } else {
291    csData = new CPDF_CountedColorSpace(std::move(pCS));
292    m_ColorSpaceMap[pCSObj] = csData;
293  }
294  return csData->AddRef();
295}
296
297CPDF_ColorSpace* CPDF_DocPageData::GetCopiedColorSpace(CPDF_Object* pCSObj) {
298  if (!pCSObj)
299    return nullptr;
300
301  auto it = m_ColorSpaceMap.find(pCSObj);
302  if (it != m_ColorSpaceMap.end())
303    return it->second->AddRef();
304
305  return nullptr;
306}
307
308void CPDF_DocPageData::ReleaseColorSpace(const CPDF_Object* pColorSpace) {
309  if (!pColorSpace)
310    return;
311
312  auto it = m_ColorSpaceMap.find(pColorSpace);
313  if (it == m_ColorSpaceMap.end())
314    return;
315
316  CPDF_CountedColorSpace* pCountedColorSpace = it->second;
317  if (!pCountedColorSpace->get())
318    return;
319
320  pCountedColorSpace->RemoveRef();
321  if (pCountedColorSpace->use_count() > 1)
322    return;
323
324  // We have item only in m_ColorSpaceMap cache. Clean it.
325  pCountedColorSpace->get()->Release();
326  pCountedColorSpace->reset(nullptr);
327}
328
329CPDF_Pattern* CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj,
330                                           bool bShading,
331                                           const CFX_Matrix& matrix) {
332  if (!pPatternObj)
333    return nullptr;
334
335  CPDF_CountedPattern* ptData = nullptr;
336  auto it = m_PatternMap.find(pPatternObj);
337  if (it != m_PatternMap.end()) {
338    ptData = it->second;
339    if (ptData->get()) {
340      return ptData->AddRef();
341    }
342  }
343  std::unique_ptr<CPDF_Pattern> pPattern;
344  if (bShading) {
345    pPattern = pdfium::MakeUnique<CPDF_ShadingPattern>(
346        m_pPDFDoc.Get(), pPatternObj, true, matrix);
347  } else {
348    CPDF_Dictionary* pDict = pPatternObj->GetDict();
349    if (!pDict)
350      return nullptr;
351
352    int type = pDict->GetIntegerFor("PatternType");
353    if (type == CPDF_Pattern::TILING) {
354      pPattern = pdfium::MakeUnique<CPDF_TilingPattern>(m_pPDFDoc.Get(),
355                                                        pPatternObj, matrix);
356    } else if (type == CPDF_Pattern::SHADING) {
357      pPattern = pdfium::MakeUnique<CPDF_ShadingPattern>(
358          m_pPDFDoc.Get(), pPatternObj, false, matrix);
359    } else {
360      return nullptr;
361    }
362  }
363
364  if (ptData) {
365    ptData->reset(std::move(pPattern));
366  } else {
367    ptData = new CPDF_CountedPattern(std::move(pPattern));
368    m_PatternMap[pPatternObj] = ptData;
369  }
370  return ptData->AddRef();
371}
372
373void CPDF_DocPageData::ReleasePattern(const CPDF_Object* pPatternObj) {
374  if (!pPatternObj)
375    return;
376
377  auto it = m_PatternMap.find(pPatternObj);
378  if (it == m_PatternMap.end())
379    return;
380
381  CPDF_CountedPattern* pPattern = it->second;
382  if (!pPattern->get())
383    return;
384
385  pPattern->RemoveRef();
386  if (pPattern->use_count() > 1)
387    return;
388
389  // We have item only in m_PatternMap cache. Clean it.
390  pPattern->clear();
391}
392
393RetainPtr<CPDF_Image> CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) {
394  ASSERT(dwStreamObjNum);
395  auto it = m_ImageMap.find(dwStreamObjNum);
396  if (it != m_ImageMap.end())
397    return it->second;
398
399  auto pImage = pdfium::MakeRetain<CPDF_Image>(m_pPDFDoc.Get(), dwStreamObjNum);
400  m_ImageMap[dwStreamObjNum] = pImage;
401  return pImage;
402}
403
404void CPDF_DocPageData::MaybePurgeImage(uint32_t dwStreamObjNum) {
405  ASSERT(dwStreamObjNum);
406  auto it = m_ImageMap.find(dwStreamObjNum);
407  if (it != m_ImageMap.end() && it->second->HasOneRef())
408    m_ImageMap.erase(it);
409}
410
411RetainPtr<CPDF_IccProfile> CPDF_DocPageData::GetIccProfile(
412    CPDF_Stream* pProfileStream) {
413  if (!pProfileStream)
414    return nullptr;
415
416  auto it = m_IccProfileMap.find(pProfileStream);
417  if (it != m_IccProfileMap.end())
418    return it->second;
419
420  auto pAccessor = pdfium::MakeRetain<CPDF_StreamAcc>(pProfileStream);
421  pAccessor->LoadAllDataFiltered();
422
423  uint8_t digest[20];
424  CRYPT_SHA1Generate(pAccessor->GetData(), pAccessor->GetSize(), digest);
425
426  ByteString bsDigest(digest, 20);
427  auto hash_it = m_HashProfileMap.find(bsDigest);
428  if (hash_it != m_HashProfileMap.end()) {
429    auto it_copied_stream = m_IccProfileMap.find(hash_it->second);
430    if (it_copied_stream != m_IccProfileMap.end())
431      return it_copied_stream->second;
432  }
433  auto pProfile = pdfium::MakeRetain<CPDF_IccProfile>(
434      pProfileStream, pAccessor->GetData(), pAccessor->GetSize());
435  m_IccProfileMap[pProfileStream] = pProfile;
436  m_HashProfileMap[bsDigest] = pProfileStream;
437  return pProfile;
438}
439
440void CPDF_DocPageData::MaybePurgeIccProfile(CPDF_Stream* pProfileStream) {
441  ASSERT(pProfileStream);
442  auto it = m_IccProfileMap.find(pProfileStream);
443  if (it != m_IccProfileMap.end() && it->second->HasOneRef())
444    m_IccProfileMap.erase(it);
445}
446
447RetainPtr<CPDF_StreamAcc> CPDF_DocPageData::GetFontFileStreamAcc(
448    CPDF_Stream* pFontStream) {
449  ASSERT(pFontStream);
450  auto it = m_FontFileMap.find(pFontStream);
451  if (it != m_FontFileMap.end())
452    return it->second;
453
454  CPDF_Dictionary* pFontDict = pFontStream->GetDict();
455  int32_t org_size = pFontDict->GetIntegerFor("Length1") +
456                     pFontDict->GetIntegerFor("Length2") +
457                     pFontDict->GetIntegerFor("Length3");
458  org_size = std::max(org_size, 0);
459
460  auto pFontAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pFontStream);
461  pFontAcc->LoadAllData(false, org_size, false);
462  m_FontFileMap[pFontStream] = pFontAcc;
463  return pFontAcc;
464}
465
466void CPDF_DocPageData::MaybePurgeFontFileStreamAcc(
467    const CPDF_Stream* pFontStream) {
468  if (!pFontStream)
469    return;
470
471  auto it = m_FontFileMap.find(pFontStream);
472  if (it != m_FontFileMap.end() && it->second->HasOneRef())
473    m_FontFileMap.erase(it);
474}
475
476CPDF_CountedColorSpace* CPDF_DocPageData::FindColorSpacePtr(
477    CPDF_Object* pCSObj) const {
478  if (!pCSObj)
479    return nullptr;
480
481  auto it = m_ColorSpaceMap.find(pCSObj);
482  return it != m_ColorSpaceMap.end() ? it->second : nullptr;
483}
484
485CPDF_CountedPattern* CPDF_DocPageData::FindPatternPtr(
486    CPDF_Object* pPatternObj) const {
487  if (!pPatternObj)
488    return nullptr;
489
490  auto it = m_PatternMap.find(pPatternObj);
491  return it != m_PatternMap.end() ? it->second : nullptr;
492}
493