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/font/font_int.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_pPDFDoc(pPDFDoc), m_bForceClear(false) {}
32
~CPDF_DocPageData()33 CPDF_DocPageData::~CPDF_DocPageData() {
34 Clear(false);
35 Clear(true);
36
37 for (auto& it : m_PatternMap)
38 delete it.second;
39 m_PatternMap.clear();
40
41 for (auto& it : m_FontMap)
42 delete it.second;
43 m_FontMap.clear();
44
45 for (auto& it : m_ColorSpaceMap)
46 delete it.second;
47 m_ColorSpaceMap.clear();
48 }
49
Clear(bool bForceRelease)50 void CPDF_DocPageData::Clear(bool bForceRelease) {
51 m_bForceClear = bForceRelease;
52
53 for (auto& it : m_PatternMap) {
54 CPDF_CountedPattern* ptData = it.second;
55 if (!ptData->get())
56 continue;
57
58 if (bForceRelease || ptData->use_count() < 2)
59 ptData->clear();
60 }
61
62 for (auto& it : m_FontMap) {
63 CPDF_CountedFont* fontData = it.second;
64 if (!fontData->get())
65 continue;
66
67 if (bForceRelease || fontData->use_count() < 2) {
68 fontData->clear();
69 }
70 }
71
72 for (auto& it : m_ColorSpaceMap) {
73 CPDF_CountedColorSpace* csData = it.second;
74 if (!csData->get())
75 continue;
76
77 if (bForceRelease || csData->use_count() < 2) {
78 csData->get()->Release();
79 csData->reset(nullptr);
80 }
81 }
82
83 for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end();) {
84 auto curr_it = it++;
85 CPDF_CountedIccProfile* ipData = curr_it->second;
86 if (!ipData->get())
87 continue;
88
89 if (bForceRelease || ipData->use_count() < 2) {
90 for (auto hash_it = m_HashProfileMap.begin();
91 hash_it != m_HashProfileMap.end(); ++hash_it) {
92 if (curr_it->first == hash_it->second) {
93 m_HashProfileMap.erase(hash_it);
94 break;
95 }
96 }
97 delete ipData->get();
98 delete ipData;
99 m_IccProfileMap.erase(curr_it);
100 }
101 }
102
103 for (auto it = m_FontFileMap.begin(); it != m_FontFileMap.end();) {
104 auto curr_it = it++;
105 CPDF_CountedStreamAcc* pCountedFont = curr_it->second;
106 if (!pCountedFont->get())
107 continue;
108
109 if (bForceRelease || pCountedFont->use_count() < 2) {
110 delete pCountedFont->get();
111 delete pCountedFont;
112 m_FontFileMap.erase(curr_it);
113 }
114 }
115
116 for (auto it = m_ImageMap.begin(); it != m_ImageMap.end();) {
117 auto curr_it = it++;
118 CPDF_CountedImage* pCountedImage = curr_it->second;
119 if (!pCountedImage->get())
120 continue;
121
122 if (bForceRelease || pCountedImage->use_count() < 2) {
123 delete pCountedImage->get();
124 delete pCountedImage;
125 m_ImageMap.erase(curr_it);
126 }
127 }
128 }
129
GetFont(CPDF_Dictionary * pFontDict)130 CPDF_Font* CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict) {
131 if (!pFontDict)
132 return nullptr;
133
134 CPDF_CountedFont* pFontData = nullptr;
135 auto it = m_FontMap.find(pFontDict);
136 if (it != m_FontMap.end()) {
137 pFontData = it->second;
138 if (pFontData->get()) {
139 return pFontData->AddRef();
140 }
141 }
142 std::unique_ptr<CPDF_Font> pFont = CPDF_Font::Create(m_pPDFDoc, pFontDict);
143 if (!pFont)
144 return nullptr;
145
146 if (pFontData) {
147 pFontData->reset(std::move(pFont));
148 } else {
149 pFontData = new CPDF_CountedFont(std::move(pFont));
150 m_FontMap[pFontDict] = pFontData;
151 }
152 return pFontData->AddRef();
153 }
154
GetStandardFont(const CFX_ByteString & fontName,CPDF_FontEncoding * pEncoding)155 CPDF_Font* CPDF_DocPageData::GetStandardFont(const CFX_ByteString& fontName,
156 CPDF_FontEncoding* pEncoding) {
157 if (fontName.IsEmpty())
158 return nullptr;
159
160 for (auto& it : m_FontMap) {
161 CPDF_CountedFont* fontData = it.second;
162 CPDF_Font* pFont = fontData->get();
163 if (!pFont)
164 continue;
165 if (pFont->GetBaseFont() != fontName)
166 continue;
167 if (pFont->IsEmbedded())
168 continue;
169 if (!pFont->IsType1Font())
170 continue;
171 if (pFont->GetFontDict()->KeyExist("Widths"))
172 continue;
173
174 CPDF_Type1Font* pT1Font = pFont->AsType1Font();
175 if (pEncoding && !pT1Font->GetEncoding()->IsIdentical(pEncoding))
176 continue;
177
178 return fontData->AddRef();
179 }
180
181 CPDF_Dictionary* pDict = m_pPDFDoc->NewIndirect<CPDF_Dictionary>();
182 pDict->SetNewFor<CPDF_Name>("Type", "Font");
183 pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
184 pDict->SetNewFor<CPDF_Name>("BaseFont", fontName);
185 if (pEncoding) {
186 pDict->SetFor("Encoding",
187 pEncoding->Realize(m_pPDFDoc->GetByteStringPool()));
188 }
189
190 std::unique_ptr<CPDF_Font> pFont = CPDF_Font::Create(m_pPDFDoc, pDict);
191 if (!pFont)
192 return nullptr;
193
194 CPDF_CountedFont* fontData = new CPDF_CountedFont(std::move(pFont));
195 m_FontMap[pDict] = fontData;
196 return fontData->AddRef();
197 }
198
ReleaseFont(const CPDF_Dictionary * pFontDict)199 void CPDF_DocPageData::ReleaseFont(const CPDF_Dictionary* pFontDict) {
200 if (!pFontDict)
201 return;
202
203 auto it = m_FontMap.find(pFontDict);
204 if (it == m_FontMap.end())
205 return;
206
207 CPDF_CountedFont* pFontData = it->second;
208 if (!pFontData->get())
209 return;
210
211 pFontData->RemoveRef();
212 if (pFontData->use_count() > 1)
213 return;
214
215 // We have font data only in m_FontMap cache. Clean it.
216 pFontData->clear();
217 }
218
GetColorSpace(CPDF_Object * pCSObj,const CPDF_Dictionary * pResources)219 CPDF_ColorSpace* CPDF_DocPageData::GetColorSpace(
220 CPDF_Object* pCSObj,
221 const CPDF_Dictionary* pResources) {
222 std::set<CPDF_Object*> visited;
223 return GetColorSpaceImpl(pCSObj, pResources, &visited);
224 }
225
GetColorSpaceImpl(CPDF_Object * pCSObj,const CPDF_Dictionary * pResources,std::set<CPDF_Object * > * pVisited)226 CPDF_ColorSpace* CPDF_DocPageData::GetColorSpaceImpl(
227 CPDF_Object* pCSObj,
228 const CPDF_Dictionary* pResources,
229 std::set<CPDF_Object*>* pVisited) {
230 if (!pCSObj)
231 return nullptr;
232
233 if (pdfium::ContainsKey(*pVisited, pCSObj))
234 return nullptr;
235
236 if (pCSObj->IsName()) {
237 CFX_ByteString name = pCSObj->GetString();
238 CPDF_ColorSpace* pCS = CPDF_ColorSpace::ColorspaceFromName(name);
239 if (!pCS && pResources) {
240 CPDF_Dictionary* pList = pResources->GetDictFor("ColorSpace");
241 if (pList) {
242 pdfium::ScopedSetInsertion<CPDF_Object*> insertion(pVisited, pCSObj);
243 return GetColorSpaceImpl(pList->GetDirectObjectFor(name), nullptr,
244 pVisited);
245 }
246 }
247 if (!pCS || !pResources)
248 return pCS;
249
250 CPDF_Dictionary* pColorSpaces = pResources->GetDictFor("ColorSpace");
251 if (!pColorSpaces)
252 return pCS;
253
254 CPDF_Object* pDefaultCS = nullptr;
255 switch (pCS->GetFamily()) {
256 case PDFCS_DEVICERGB:
257 pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultRGB");
258 break;
259 case PDFCS_DEVICEGRAY:
260 pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultGray");
261 break;
262 case PDFCS_DEVICECMYK:
263 pDefaultCS = pColorSpaces->GetDirectObjectFor("DefaultCMYK");
264 break;
265 }
266 if (!pDefaultCS)
267 return pCS;
268
269 pdfium::ScopedSetInsertion<CPDF_Object*> insertion(pVisited, pCSObj);
270 return GetColorSpaceImpl(pDefaultCS, nullptr, pVisited);
271 }
272
273 CPDF_Array* pArray = pCSObj->AsArray();
274 if (!pArray || pArray->IsEmpty())
275 return nullptr;
276
277 if (pArray->GetCount() == 1) {
278 pdfium::ScopedSetInsertion<CPDF_Object*> insertion(pVisited, pCSObj);
279 return GetColorSpaceImpl(pArray->GetDirectObjectAt(0), pResources,
280 pVisited);
281 }
282
283 CPDF_CountedColorSpace* csData = nullptr;
284 auto it = m_ColorSpaceMap.find(pCSObj);
285 if (it != m_ColorSpaceMap.end()) {
286 csData = it->second;
287 if (csData->get()) {
288 return csData->AddRef();
289 }
290 }
291
292 std::unique_ptr<CPDF_ColorSpace> pCS =
293 CPDF_ColorSpace::Load(m_pPDFDoc, pArray);
294 if (!pCS)
295 return nullptr;
296
297 if (csData) {
298 csData->reset(std::move(pCS));
299 } else {
300 csData = new CPDF_CountedColorSpace(std::move(pCS));
301 m_ColorSpaceMap[pCSObj] = csData;
302 }
303 return csData->AddRef();
304 }
305
GetCopiedColorSpace(CPDF_Object * pCSObj)306 CPDF_ColorSpace* CPDF_DocPageData::GetCopiedColorSpace(CPDF_Object* pCSObj) {
307 if (!pCSObj)
308 return nullptr;
309
310 auto it = m_ColorSpaceMap.find(pCSObj);
311 if (it != m_ColorSpaceMap.end())
312 return it->second->AddRef();
313
314 return nullptr;
315 }
316
ReleaseColorSpace(const CPDF_Object * pColorSpace)317 void CPDF_DocPageData::ReleaseColorSpace(const CPDF_Object* pColorSpace) {
318 if (!pColorSpace)
319 return;
320
321 auto it = m_ColorSpaceMap.find(pColorSpace);
322 if (it == m_ColorSpaceMap.end())
323 return;
324
325 CPDF_CountedColorSpace* pCountedColorSpace = it->second;
326 if (!pCountedColorSpace->get())
327 return;
328
329 pCountedColorSpace->RemoveRef();
330 if (pCountedColorSpace->use_count() > 1)
331 return;
332
333 // We have item only in m_ColorSpaceMap cache. Clean it.
334 pCountedColorSpace->get()->Release();
335 pCountedColorSpace->reset(nullptr);
336 }
337
GetPattern(CPDF_Object * pPatternObj,bool bShading,const CFX_Matrix & matrix)338 CPDF_Pattern* CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj,
339 bool bShading,
340 const CFX_Matrix& matrix) {
341 if (!pPatternObj)
342 return nullptr;
343
344 CPDF_CountedPattern* ptData = nullptr;
345 auto it = m_PatternMap.find(pPatternObj);
346 if (it != m_PatternMap.end()) {
347 ptData = it->second;
348 if (ptData->get()) {
349 return ptData->AddRef();
350 }
351 }
352 std::unique_ptr<CPDF_Pattern> pPattern;
353 if (bShading) {
354 pPattern = pdfium::MakeUnique<CPDF_ShadingPattern>(m_pPDFDoc, pPatternObj,
355 true, matrix);
356 } else {
357 CPDF_Dictionary* pDict = pPatternObj ? pPatternObj->GetDict() : nullptr;
358 if (pDict) {
359 int type = pDict->GetIntegerFor("PatternType");
360 if (type == CPDF_Pattern::TILING) {
361 pPattern = pdfium::MakeUnique<CPDF_TilingPattern>(m_pPDFDoc,
362 pPatternObj, matrix);
363 } else if (type == CPDF_Pattern::SHADING) {
364 pPattern = pdfium::MakeUnique<CPDF_ShadingPattern>(
365 m_pPDFDoc, pPatternObj, false, matrix);
366 }
367 }
368 }
369 if (!pPattern)
370 return nullptr;
371
372 if (ptData) {
373 ptData->reset(std::move(pPattern));
374 } else {
375 ptData = new CPDF_CountedPattern(std::move(pPattern));
376 m_PatternMap[pPatternObj] = ptData;
377 }
378 return ptData->AddRef();
379 }
380
ReleasePattern(const CPDF_Object * pPatternObj)381 void CPDF_DocPageData::ReleasePattern(const CPDF_Object* pPatternObj) {
382 if (!pPatternObj)
383 return;
384
385 auto it = m_PatternMap.find(pPatternObj);
386 if (it == m_PatternMap.end())
387 return;
388
389 CPDF_CountedPattern* pPattern = it->second;
390 if (!pPattern->get())
391 return;
392
393 pPattern->RemoveRef();
394 if (pPattern->use_count() > 1)
395 return;
396
397 // We have item only in m_PatternMap cache. Clean it.
398 pPattern->clear();
399 }
400
GetImage(uint32_t dwStreamObjNum)401 CPDF_Image* CPDF_DocPageData::GetImage(uint32_t dwStreamObjNum) {
402 ASSERT(dwStreamObjNum);
403 auto it = m_ImageMap.find(dwStreamObjNum);
404 if (it != m_ImageMap.end())
405 return it->second->AddRef();
406
407 CPDF_CountedImage* pCountedImage = new CPDF_CountedImage(
408 pdfium::MakeUnique<CPDF_Image>(m_pPDFDoc, dwStreamObjNum));
409 m_ImageMap[dwStreamObjNum] = pCountedImage;
410 return pCountedImage->AddRef();
411 }
412
ReleaseImage(uint32_t dwStreamObjNum)413 void CPDF_DocPageData::ReleaseImage(uint32_t dwStreamObjNum) {
414 ASSERT(dwStreamObjNum);
415 auto it = m_ImageMap.find(dwStreamObjNum);
416 if (it == m_ImageMap.end())
417 return;
418
419 CPDF_CountedImage* pCountedImage = it->second;
420 if (!pCountedImage)
421 return;
422
423 pCountedImage->RemoveRef();
424 if (pCountedImage->use_count() > 1)
425 return;
426
427 // We have item only in m_ImageMap cache. Clean it.
428 delete pCountedImage->get();
429 delete pCountedImage;
430 m_ImageMap.erase(it);
431 }
432
GetIccProfile(CPDF_Stream * pIccProfileStream)433 CPDF_IccProfile* CPDF_DocPageData::GetIccProfile(
434 CPDF_Stream* pIccProfileStream) {
435 if (!pIccProfileStream)
436 return nullptr;
437
438 auto it = m_IccProfileMap.find(pIccProfileStream);
439 if (it != m_IccProfileMap.end())
440 return it->second->AddRef();
441
442 CPDF_StreamAcc stream;
443 stream.LoadAllData(pIccProfileStream, false);
444 uint8_t digest[20];
445 CRYPT_SHA1Generate(stream.GetData(), stream.GetSize(), digest);
446 CFX_ByteString bsDigest(digest, 20);
447 auto hash_it = m_HashProfileMap.find(bsDigest);
448 if (hash_it != m_HashProfileMap.end()) {
449 auto it_copied_stream = m_IccProfileMap.find(hash_it->second);
450 if (it_copied_stream != m_IccProfileMap.end())
451 return it_copied_stream->second->AddRef();
452 }
453 CPDF_CountedIccProfile* ipData = new CPDF_CountedIccProfile(
454 pdfium::MakeUnique<CPDF_IccProfile>(stream.GetData(), stream.GetSize()));
455 m_IccProfileMap[pIccProfileStream] = ipData;
456 m_HashProfileMap[bsDigest] = pIccProfileStream;
457 return ipData->AddRef();
458 }
459
ReleaseIccProfile(const CPDF_IccProfile * pIccProfile)460 void CPDF_DocPageData::ReleaseIccProfile(const CPDF_IccProfile* pIccProfile) {
461 ASSERT(pIccProfile);
462
463 for (auto it = m_IccProfileMap.begin(); it != m_IccProfileMap.end(); ++it) {
464 CPDF_CountedIccProfile* profile = it->second;
465 if (profile->get() != pIccProfile)
466 continue;
467
468 profile->RemoveRef();
469 if (profile->use_count() > 1)
470 continue;
471 // We have item only in m_IccProfileMap cache. Clean it.
472 delete profile->get();
473 delete profile;
474 m_IccProfileMap.erase(it);
475 return;
476 }
477 }
478
GetFontFileStreamAcc(CPDF_Stream * pFontStream)479 CPDF_StreamAcc* CPDF_DocPageData::GetFontFileStreamAcc(
480 CPDF_Stream* pFontStream) {
481 ASSERT(pFontStream);
482
483 auto it = m_FontFileMap.find(pFontStream);
484 if (it != m_FontFileMap.end())
485 return it->second->AddRef();
486
487 CPDF_Dictionary* pFontDict = pFontStream->GetDict();
488 int32_t org_size = pFontDict->GetIntegerFor("Length1") +
489 pFontDict->GetIntegerFor("Length2") +
490 pFontDict->GetIntegerFor("Length3");
491 org_size = std::max(org_size, 0);
492
493 auto pFontAcc = pdfium::MakeUnique<CPDF_StreamAcc>();
494 pFontAcc->LoadAllData(pFontStream, false, org_size);
495
496 CPDF_CountedStreamAcc* pCountedFont =
497 new CPDF_CountedStreamAcc(std::move(pFontAcc));
498 m_FontFileMap[pFontStream] = pCountedFont;
499 return pCountedFont->AddRef();
500 }
501
ReleaseFontFileStreamAcc(const CPDF_Stream * pFontStream)502 void CPDF_DocPageData::ReleaseFontFileStreamAcc(
503 const CPDF_Stream* pFontStream) {
504 if (!pFontStream)
505 return;
506
507 auto it = m_FontFileMap.find(pFontStream);
508 if (it == m_FontFileMap.end())
509 return;
510
511 CPDF_CountedStreamAcc* pCountedStream = it->second;
512 if (!pCountedStream)
513 return;
514
515 pCountedStream->RemoveRef();
516 if (pCountedStream->use_count() > 1)
517 return;
518
519 // We have item only in m_FontFileMap cache. Clean it.
520 delete pCountedStream->get();
521 delete pCountedStream;
522 m_FontFileMap.erase(it);
523 }
524
FindColorSpacePtr(CPDF_Object * pCSObj) const525 CPDF_CountedColorSpace* CPDF_DocPageData::FindColorSpacePtr(
526 CPDF_Object* pCSObj) const {
527 if (!pCSObj)
528 return nullptr;
529
530 auto it = m_ColorSpaceMap.find(pCSObj);
531 return it != m_ColorSpaceMap.end() ? it->second : nullptr;
532 }
533
FindPatternPtr(CPDF_Object * pPatternObj) const534 CPDF_CountedPattern* CPDF_DocPageData::FindPatternPtr(
535 CPDF_Object* pPatternObj) const {
536 if (!pPatternObj)
537 return nullptr;
538
539 auto it = m_PatternMap.find(pPatternObj);
540 return it != m_PatternMap.end() ? it->second : nullptr;
541 }
542