• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CPDF_DocPageData(CPDF_Document * pPDFDoc)30 CPDF_DocPageData::CPDF_DocPageData(CPDF_Document* pPDFDoc)
31     : m_bForceClear(false), m_pPDFDoc(pPDFDoc) {
32   assert(m_pPDFDoc);
33 }
34 
~CPDF_DocPageData()35 CPDF_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 
Clear(bool bForceRelease)52 void 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 
GetFont(CPDF_Dictionary * pFontDict)121 CPDF_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 
GetStandardFont(const ByteString & fontName,CPDF_FontEncoding * pEncoding)147 CPDF_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 
ReleaseFont(const CPDF_Dictionary * pFontDict)191 void 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 
GetColorSpace(CPDF_Object * pCSObj,const CPDF_Dictionary * pResources)211 CPDF_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 
GetColorSpaceGuarded(CPDF_Object * pCSObj,const CPDF_Dictionary * pResources,std::set<CPDF_Object * > * pVisited)218 CPDF_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 
GetCopiedColorSpace(CPDF_Object * pCSObj)297 CPDF_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 
ReleaseColorSpace(const CPDF_Object * pColorSpace)308 void 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 
GetPattern(CPDF_Object * pPatternObj,bool bShading,const CFX_Matrix & matrix)329 CPDF_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 
ReleasePattern(const CPDF_Object * pPatternObj)373 void 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 
GetImage(uint32_t dwStreamObjNum)393 RetainPtr<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 
MaybePurgeImage(uint32_t dwStreamObjNum)404 void 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 
GetIccProfile(CPDF_Stream * pProfileStream)411 RetainPtr<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 
MaybePurgeIccProfile(CPDF_Stream * pProfileStream)440 void 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 
GetFontFileStreamAcc(CPDF_Stream * pFontStream)447 RetainPtr<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 
MaybePurgeFontFileStreamAcc(const CPDF_Stream * pFontStream)466 void 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 
FindColorSpacePtr(CPDF_Object * pCSObj) const476 CPDF_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 
FindPatternPtr(CPDF_Object * pPatternObj) const485 CPDF_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