Home | History | Annotate | Download | only in page
      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