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 30 CPDF_DocPageData::CPDF_DocPageData(CPDF_Document* pPDFDoc) 31 : m_bForceClear(false), m_pPDFDoc(pPDFDoc) { 32 assert(m_pPDFDoc); 33 } 34 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 476 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 485 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