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