text: // Copyright 2014 PDFium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
   
  // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
  
  #include "../../../include/fpdfapi/fpdf_page.h"
  #include "../../../include/fpdfapi/fpdf_module.h"
  #include "../../../include/fdrm/fx_crypt.h"
  #include "../fpdf_font/font_int.h"
  #include "pageint.h"
  class CPDF_PageModule : public CPDF_PageModuleDef
  {
  public:
      CPDF_PageModule() : m_StockGrayCS(PDFCS_DEVICEGRAY), m_StockRGBCS(PDFCS_DEVICERGB),
          m_StockCMYKCS(PDFCS_DEVICECMYK) {}
      virtual ~CPDF_PageModule() {}
      virtual FX_BOOL		Installed()
      {
          return TRUE;
      }
      virtual CPDF_DocPageData*	CreateDocData(CPDF_Document* pDoc)
      {
          return FX_NEW CPDF_DocPageData(pDoc);
      }
      virtual void		ReleaseDoc(CPDF_Document* pDoc);
      virtual void		ClearDoc(CPDF_Document* pDoc);
      virtual CPDF_FontGlobals*	GetFontGlobals()
      {
          return &m_FontGlobals;
      }
      virtual void				ClearStockFont(CPDF_Document* pDoc)
      {
          m_FontGlobals.Clear(pDoc);
      }
      virtual CPDF_ColorSpace*	GetStockCS(int family);
      virtual void		NotifyCJKAvailable();
      CPDF_FontGlobals	m_FontGlobals;
      CPDF_DeviceCS		m_StockGrayCS;
      CPDF_DeviceCS		m_StockRGBCS;
      CPDF_DeviceCS		m_StockCMYKCS;
      CPDF_PatternCS		m_StockPatternCS;
  };
  CPDF_ColorSpace* CPDF_PageModule::GetStockCS(int family)
  {
      if (family == PDFCS_DEVICEGRAY) {
          return &m_StockGrayCS;
      }
      if (family == PDFCS_DEVICERGB) {
          return &m_StockRGBCS;
      }
      if (family == PDFCS_DEVICECMYK) {
          return &m_StockCMYKCS;
      }
      if (family == PDFCS_PATTERN) {
          return &m_StockPatternCS;
      }
      return NULL;
  }
  void CPDF_ModuleMgr::InitPageModule()
  {
      if (m_pPageModule) {
          delete m_pPageModule;
      }
      CPDF_PageModule* pPageModule = FX_NEW CPDF_PageModule;
      m_pPageModule = pPageModule;
  }
  void CPDF_PageModule::ReleaseDoc(CPDF_Document* pDoc)
  {
      delete pDoc->GetPageData();
  }
  void CPDF_PageModule::ClearDoc(CPDF_Document* pDoc)
  {
      pDoc->GetPageData()->Clear(FALSE);
  }
  void CPDF_PageModule::NotifyCJKAvailable()
  {
      m_FontGlobals.m_CMapManager.ReloadAll();
  }
  CPDF_Font* CPDF_Document::LoadFont(CPDF_Dictionary* pFontDict)
  {
      if (!pFontDict) {
          return NULL;
      }
      return GetValidatePageData()->GetFont(pFontDict, FALSE);
  }
  CPDF_Font* CPDF_Document::FindFont(CPDF_Dictionary* pFontDict)
  {
      if (!pFontDict) {
          return NULL;
      }
      return GetValidatePageData()->GetFont(pFontDict, TRUE);
  }
  CPDF_StreamAcc* CPDF_Document::LoadFontFile(CPDF_Stream* pStream)
  {
      if (pStream == NULL) {
          return NULL;
      }
      return GetValidatePageData()->GetFontFileStreamAcc(pStream);
  }
  CPDF_ColorSpace* _CSFromName(const CFX_ByteString& name);
  CPDF_ColorSpace* CPDF_Document::LoadColorSpace(CPDF_Object* pCSObj, CPDF_Dictionary* pResources)
  {
      return GetValidatePageData()->GetColorSpace(pCSObj, pResources);
  }
  CPDF_Pattern* CPDF_Document::LoadPattern(CPDF_Object* pPatternObj, FX_BOOL bShading, const CFX_AffineMatrix* matrix)
  {
      return GetValidatePageData()->GetPattern(pPatternObj, bShading, matrix);
  }
  CPDF_IccProfile* CPDF_Document::LoadIccProfile(CPDF_Stream* pStream, int nComponents)
  {
      return GetValidatePageData()->GetIccProfile(pStream, nComponents);
  }
  CPDF_Image* CPDF_Document::LoadImageF(CPDF_Object* pObj)
  {
      if (!pObj) {
          return NULL;
      }
      FXSYS_assert(pObj->GetObjNum());
      return GetValidatePageData()->GetImage(pObj);
  }
  void CPDF_Document::RemoveColorSpaceFromPageData(CPDF_Object* pCSObj)
  {
      if (!pCSObj) {
          return;
      }
      GetPageData()->ReleaseColorSpace(pCSObj);
  }
  CPDF_DocPageData::CPDF_DocPageData(CPDF_Document *pPDFDoc)
      : m_pPDFDoc(pPDFDoc)
      , m_FontMap()
      , m_ColorSpaceMap()
      , m_PatternMap()
      , m_ImageMap()
      , m_IccProfileMap()
      , m_FontFileMap()
  {
      m_FontMap.InitHashTable(64);
      m_ColorSpaceMap.InitHashTable(32);
      m_PatternMap.InitHashTable(16);
      m_ImageMap.InitHashTable(64);
      m_IccProfileMap.InitHashTable(16);
      m_FontFileMap.InitHashTable(32);
  }
  CPDF_DocPageData::~CPDF_DocPageData()
  {
      Clear(FALSE);
      Clear(TRUE);
      FX_POSITION pos = NULL;
  }
  void CPDF_DocPageData::Clear(FX_BOOL bRelease)
  {
      FX_POSITION pos;
      FX_DWORD	nCount;
      {
          pos = m_PatternMap.GetStartPosition();
          while (pos) {
              CPDF_Object* ptObj;
              CPDF_CountedObject<CPDF_Pattern*>* ptData;
              m_PatternMap.GetNextAssoc(pos, ptObj, ptData);
              nCount = ptData->m_nCount;
              if (bRelease || nCount < 2) {
                  delete ptData->m_Obj;
                  ptData->m_Obj = NULL;
              }
          }
      }
      {
          pos = m_FontMap.GetStartPosition();
          while (pos) {
              CPDF_Dictionary* fontDict;
              CPDF_CountedObject<CPDF_Font*>* fontData;
              m_FontMap.GetNextAssoc(pos, fontDict, fontData);
              nCount = fontData->m_nCount;
              if (bRelease || nCount < 2) {
                  delete fontData->m_Obj;
                  fontData->m_Obj = NULL;
              }
          }
      }
      {
          pos = m_ImageMap.GetStartPosition();
          while (pos) {
              FX_DWORD objNum;
              CPDF_CountedObject<CPDF_Image*>* imageData;
              m_ImageMap.GetNextAssoc(pos, objNum, imageData);
              nCount = imageData->m_nCount;
              if (bRelease || nCount < 2) {
                  delete imageData->m_Obj;
                  delete imageData;
                  m_ImageMap.RemoveKey(objNum);
              }
          }
      }
      {
          pos = m_ColorSpaceMap.GetStartPosition();
          while (pos) {
              CPDF_Object* csKey;
              CPDF_CountedObject<CPDF_ColorSpace*>* csData;
              m_ColorSpaceMap.GetNextAssoc(pos, csKey, csData);
              nCount = csData->m_nCount;
              if (bRelease || nCount < 2) {
                  csData->m_Obj->ReleaseCS();
                  csData->m_Obj = NULL;
              }
          }
      }
      {
          pos = m_IccProfileMap.GetStartPosition();
          while (pos) {
              CPDF_Stream* ipKey;
              CPDF_CountedObject<CPDF_IccProfile*>* ipData;
              m_IccProfileMap.GetNextAssoc(pos, ipKey, ipData);
              nCount = ipData->m_nCount;
              if (bRelease || nCount < 2) {
                  FX_POSITION pos2 = m_HashProfileMap.GetStartPosition();
                  while (pos2) {
                      CFX_ByteString bsKey;
                      CPDF_Stream* pFindStream = NULL;
                      m_HashProfileMap.GetNextAssoc(pos2, bsKey, (void*&)pFindStream);
                      if (ipKey == pFindStream) {
                          m_HashProfileMap.RemoveKey(bsKey);
                          break;
                      }
                  }
                  delete ipData->m_Obj;
                  delete ipData;
                  m_IccProfileMap.RemoveKey(ipKey);
              }
          }
      }
      {
          pos = m_FontFileMap.GetStartPosition();
          while (pos) {
              CPDF_Stream* ftKey;
              CPDF_CountedObject<CPDF_StreamAcc*>* ftData;
              m_FontFileMap.GetNextAssoc(pos, ftKey, ftData);
              nCount = ftData->m_nCount;
              if (bRelease || nCount < 2) {
                  delete ftData->m_Obj;
                  delete ftData;
                  m_FontFileMap.RemoveKey(ftKey);
              }
          }
      }
  }
  CPDF_Font* CPDF_DocPageData::GetFont(CPDF_Dictionary* pFontDict, FX_BOOL findOnly)
  {
      if (!pFontDict) {
          return NULL;
      }
      if (findOnly) {
          CPDF_CountedObject<CPDF_Font*>* fontData;
          if (m_FontMap.Lookup(pFontDict, fontData)) {
              if (!fontData->m_Obj) {
                  return NULL;
              }
              fontData->m_nCount ++;
              return fontData->m_Obj;
          }
          return NULL;
      }
      CPDF_CountedObject<CPDF_Font*>* fontData = NULL;
      if (m_FontMap.Lookup(pFontDict, fontData)) {
          if (fontData->m_Obj) {
              fontData->m_nCount ++;
              return fontData->m_Obj;
          }
      }
      FX_BOOL bNew = FALSE;
      if (!fontData) {
          fontData = FX_NEW CPDF_CountedObject<CPDF_Font*>;
          bNew = TRUE;
          if (!fontData) {
              return NULL;
          }
      }
      CPDF_Font* pFont = CPDF_Font::CreateFontF(m_pPDFDoc, pFontDict);
      if (!pFont) {
          if (bNew) {
              delete fontData;
          }
          return NULL;
      }
      fontData->m_nCount = 2;
      fontData->m_Obj = pFont;
      m_FontMap.SetAt(pFontDict, fontData);
      return pFont;
  }
  CPDF_Font* CPDF_DocPageData::GetStandardFont(FX_BSTR fontName, CPDF_FontEncoding* pEncoding)
  {
      if (fontName.IsEmpty()) {
          return NULL;
      }
      FX_POSITION pos = m_FontMap.GetStartPosition();
      while (pos) {
          CPDF_Dictionary* fontDict;
          CPDF_CountedObject<CPDF_Font*>* fontData;
          m_FontMap.GetNextAssoc(pos, fontDict, fontData);
          CPDF_Font* pFont = fontData->m_Obj;
          if (!pFont) {
              continue;
          }
          if (pFont->GetBaseFont() != fontName) {
              continue;
          }
          if (pFont->IsEmbedded()) {
              continue;
          }
          if (pFont->GetFontType() != PDFFONT_TYPE1) {
              continue;
          }
          if (pFont->GetFontDict()->KeyExist(FX_BSTRC("Widths"))) {
              continue;
          }
          CPDF_Type1Font* pT1Font = pFont->GetType1Font();
          if (pEncoding && !pT1Font->GetEncoding()->IsIdentical(pEncoding)) {
              continue;
          }
          fontData->m_nCount ++;
          return pFont;
      }
      CPDF_Dictionary* pDict = FX_NEW CPDF_Dictionary;
      pDict->SetAtName(FX_BSTRC("Type"), FX_BSTRC("Font"));
      pDict->SetAtName(FX_BSTRC("Subtype"), FX_BSTRC("Type1"));
      pDict->SetAtName(FX_BSTRC("BaseFont"), fontName);
      if (pEncoding) {
          pDict->SetAt(FX_BSTRC("Encoding"), pEncoding->Realize());
      }
      m_pPDFDoc->AddIndirectObject(pDict);
      CPDF_CountedObject<CPDF_Font*>* fontData = FX_NEW CPDF_CountedObject<CPDF_Font*>;
      if (!fontData) {
          return NULL;
      }
      CPDF_Font* pFont = CPDF_Font::CreateFontF(m_pPDFDoc, pDict);
      if (!pFont) {
          delete fontData;
          return NULL;
      }
      fontData->m_nCount = 2;
      fontData->m_Obj = pFont;
      m_FontMap.SetAt(pDict, fontData);
      return pFont;
  }
  void CPDF_DocPageData::ReleaseFont(CPDF_Dictionary* pFontDict)
  {
      if (!pFontDict) {
          return;
      }
      CPDF_CountedObject<CPDF_Font*>* fontData;
      if (!m_FontMap.Lookup(pFontDict, fontData)) {
          return;
      }
      if (fontData->m_Obj && --fontData->m_nCount == 0) {
          delete fontData->m_Obj;
          fontData->m_Obj = NULL;
      }
  }
  CPDF_ColorSpace* CPDF_DocPageData::GetColorSpace(CPDF_Object* pCSObj, CPDF_Dictionary* pResources)
  {
      if (!pCSObj) {
          return NULL;
      }
      if (pCSObj->GetType() == PDFOBJ_NAME) {
          CFX_ByteString name = pCSObj->GetConstString();
          CPDF_ColorSpace* pCS = _CSFromName(name);
          if (!pCS && pResources) {
              CPDF_Dictionary* pList = pResources->GetDict(FX_BSTRC("ColorSpace"));
              if (pList) {
                  pCSObj = pList->GetElementValue(name);
                  return GetColorSpace(pCSObj, NULL);
              }
          }
          if (pCS == NULL || pResources == NULL) {
              return pCS;
          }
          CPDF_Dictionary* pColorSpaces = pResources->GetDict(FX_BSTRC("ColorSpace"));
          if (pColorSpaces == NULL) {
              return pCS;
          }
          CPDF_Object* pDefaultCS = NULL;
          switch (pCS->GetFamily()) {
              case PDFCS_DEVICERGB:
                  pDefaultCS = pColorSpaces->GetElementValue(FX_BSTRC("DefaultRGB"));
                  break;
              case PDFCS_DEVICEGRAY:
                  pDefaultCS = pColorSpaces->GetElementValue(FX_BSTRC("DefaultGray"));
                  break;
              case PDFCS_DEVICECMYK:
                  pDefaultCS = pColorSpaces->GetElementValue(FX_BSTRC("DefaultCMYK"));
                  break;
          }
          if (pDefaultCS == NULL) {
              return pCS;
          }
          return GetColorSpace(pDefaultCS, NULL);
      }
      if (pCSObj->GetType() != PDFOBJ_ARRAY) {
          return NULL;
      }
      CPDF_Array* pArray = (CPDF_Array*)pCSObj;
      if (pArray->GetCount() == 0) {
          return NULL;
      }
      if (pArray->GetCount() == 1) {
          return GetColorSpace(pArray->GetElementValue(0), pResources);
      }
      CPDF_CountedObject<CPDF_ColorSpace*>* csData = NULL;
      if (m_ColorSpaceMap.Lookup(pCSObj, csData)) {
          if (csData->m_Obj) {
              csData->m_nCount++;
              return csData->m_Obj;
          }
      }
      FX_BOOL bNew = FALSE;
      if (!csData) {
          csData = FX_NEW CPDF_CountedObject<CPDF_ColorSpace*>;
          if (!csData) {
              return NULL;
          }
          bNew = TRUE;
      }
      CPDF_ColorSpace* pCS = CPDF_ColorSpace::Load(m_pPDFDoc, pArray);
      if (!pCS) {
          if (bNew) {
              delete csData;
          }
          return NULL;
      }
      csData->m_nCount = 2;
      csData->m_Obj = pCS;
      m_ColorSpaceMap.SetAt(pCSObj, csData);
      return pCS;
  }
  CPDF_ColorSpace* CPDF_DocPageData::GetCopiedColorSpace(CPDF_Object* pCSObj)
  {
      if (!pCSObj) {
          return NULL;
      }
      CPDF_CountedObject<CPDF_ColorSpace*>* csData;
      if (!m_ColorSpaceMap.Lookup(pCSObj, csData)) {
          return NULL;
      }
      if (!csData->m_Obj) {
          return NULL;
      }
      csData->m_nCount ++;
      return csData->m_Obj;
  }
  void CPDF_DocPageData::ReleaseColorSpace(CPDF_Object* pColorSpace)
  {
      if (!pColorSpace) {
          return;
      }
      CPDF_CountedObject<CPDF_ColorSpace*>* csData;
      if (!m_ColorSpaceMap.Lookup(pColorSpace, csData)) {
          return;
      }
      if (csData->m_Obj && --csData->m_nCount == 0) {
          csData->m_Obj->ReleaseCS();
          csData->m_Obj = NULL;
      }
  }
  CPDF_Pattern* CPDF_DocPageData::GetPattern(CPDF_Object* pPatternObj, FX_BOOL bShading, const CFX_AffineMatrix* matrix)
  {
      if (!pPatternObj) {
          return NULL;
      }
      CPDF_CountedObject<CPDF_Pattern*>* ptData = NULL;
      if (m_PatternMap.Lookup(pPatternObj, ptData)) {
          if (ptData->m_Obj) {
              ptData->m_nCount++;
              return ptData->m_Obj;
          }
      }
      FX_BOOL bNew = FALSE;
      if (!ptData) {
          ptData = FX_NEW CPDF_CountedObject<CPDF_Pattern*>;
          bNew = TRUE;
          if (!ptData) {
              return NULL;
          }
      }
      CPDF_Pattern* pPattern = NULL;
      if (bShading) {
          pPattern = FX_NEW CPDF_ShadingPattern(m_pPDFDoc, pPatternObj, bShading, matrix);
      } else {
          CPDF_Dictionary* pDict = pPatternObj->GetDict();
          if (pDict) {
              int type = pDict->GetInteger(FX_BSTRC("PatternType"));
              if (type == 1) {
                  pPattern = FX_NEW CPDF_TilingPattern(m_pPDFDoc, pPatternObj, matrix);
              } else if (type == 2) {
                  pPattern = FX_NEW CPDF_ShadingPattern(m_pPDFDoc, pPatternObj, FALSE, matrix);
              }
          }
      }
      if (!pPattern) {
          if (bNew) {
              delete ptData;
          }
          return NULL;
      }
      ptData->m_nCount = 2;
      ptData->m_Obj = pPattern;
      m_PatternMap.SetAt(pPatternObj, ptData);
      return pPattern;
  }
  void CPDF_DocPageData::ReleasePattern(CPDF_Object* pPatternObj)
  {
      if (!pPatternObj) {
          return;
      }
      CPDF_CountedObject<CPDF_Pattern*>* ptData;
      if (!m_PatternMap.Lookup(pPatternObj, ptData)) {
          return;
      }
      if (ptData->m_Obj && --ptData->m_nCount == 0) {
          delete ptData->m_Obj;
          ptData->m_Obj = NULL;
      }
  }
  CPDF_Image* CPDF_DocPageData::GetImage(CPDF_Object* pImageStream)
  {
      if (!pImageStream) {
          return NULL;
      }
      FX_DWORD dwImageObjNum = pImageStream->GetObjNum();
      CPDF_CountedObject<CPDF_Image*>* imageData;
      if (m_ImageMap.Lookup(dwImageObjNum, imageData)) {
          imageData->m_nCount ++;
          return imageData->m_Obj;
      }
      imageData = FX_NEW CPDF_CountedObject<CPDF_Image*>;
      if (!imageData) {
          return NULL;
      }
      CPDF_Image* pImage = FX_NEW CPDF_Image(m_pPDFDoc);
      if (!pImage) {
          delete imageData;
          return NULL;
      }
      pImage->LoadImageF((CPDF_Stream*)pImageStream, FALSE);
      imageData->m_nCount = 2;
      imageData->m_Obj = pImage;
      m_ImageMap.SetAt(dwImageObjNum, imageData);
      return pImage;
  }
  void CPDF_DocPageData::ReleaseImage(CPDF_Object* pImageStream)
  {
      if (!pImageStream) {
          return;
      }
      PDF_DocPageData_Release<FX_DWORD, CPDF_Image*>(m_ImageMap, pImageStream->GetObjNum(), NULL);
  }
  CPDF_IccProfile* CPDF_DocPageData::GetIccProfile(CPDF_Stream* pIccProfileStream, FX_INT32 nComponents)
  {
      if (!pIccProfileStream) {
          return NULL;
      }
      CPDF_CountedObject<CPDF_IccProfile*>* ipData = NULL;
      if (m_IccProfileMap.Lookup(pIccProfileStream, ipData)) {
          ipData->m_nCount++;
          return ipData->m_Obj;
      }
      CPDF_StreamAcc stream;
      stream.LoadAllData(pIccProfileStream, FALSE);
      FX_BYTE digest[20];
      CPDF_Stream* pCopiedStream = NULL;
      CRYPT_SHA1Generate(stream.GetData(), stream.GetSize(), digest);
      if (m_HashProfileMap.Lookup(CFX_ByteStringC(digest, 20), (void*&)pCopiedStream)) {
          m_IccProfileMap.Lookup(pCopiedStream, ipData);
          ipData->m_nCount++;
          return ipData->m_Obj;
      }
      CPDF_IccProfile* pProfile = FX_NEW CPDF_IccProfile(stream.GetData(), stream.GetSize(), nComponents);
      if (!pProfile) {
          return NULL;
      }
      ipData = FX_NEW CPDF_CountedObject<CPDF_IccProfile*>;
      if (!ipData) {
          delete pProfile;
          return NULL;
      }
      ipData->m_nCount = 2;
      ipData->m_Obj = pProfile;
      m_IccProfileMap.SetAt(pIccProfileStream, ipData);
      m_HashProfileMap.SetAt(CFX_ByteStringC(digest, 20), pIccProfileStream);
      return pProfile;
  }
  void CPDF_DocPageData::ReleaseIccProfile(CPDF_Stream* pIccProfileStream, CPDF_IccProfile* pIccProfile)
  {
      if (!pIccProfileStream && !pIccProfile) {
          return;
      }
      CPDF_CountedObject<CPDF_IccProfile*>* ipData = NULL;
      if (m_IccProfileMap.Lookup(pIccProfileStream, ipData) && ipData->m_nCount < 2) {
          FX_POSITION pos = m_HashProfileMap.GetStartPosition();
          while (pos) {
              CFX_ByteString key;
              CPDF_Stream* pFindStream = NULL;
              m_HashProfileMap.GetNextAssoc(pos, key, (void*&)pFindStream);
              if (pIccProfileStream == pFindStream) {
                  m_HashProfileMap.RemoveKey(key);
                  break;
              }
          }
      }
      PDF_DocPageData_Release<CPDF_Stream*, CPDF_IccProfile*>(m_IccProfileMap, pIccProfileStream, pIccProfile);
  }
  CPDF_StreamAcc* CPDF_DocPageData::GetFontFileStreamAcc(CPDF_Stream* pFontStream)
  {
      if (!pFontStream) {
          return NULL;
      }
      CPDF_CountedObject<CPDF_StreamAcc*>* ftData;
      if (m_FontFileMap.Lookup(pFontStream, ftData)) {
          ftData->m_nCount ++;
          return ftData->m_Obj;
      }
      ftData = FX_NEW CPDF_CountedObject<CPDF_StreamAcc*>;
      if (!ftData) {
          return NULL;
      }
      CPDF_StreamAcc* pFontFile = FX_NEW CPDF_StreamAcc;
      if (!pFontFile) {
          delete ftData;
          return NULL;
      }
      CPDF_Dictionary* pFontDict = pFontStream->GetDict();
      FX_INT32 org_size = pFontDict->GetInteger(FX_BSTRC("Length1")) + pFontDict->GetInteger(FX_BSTRC("Length2")) + pFontDict->GetInteger(FX_BSTRC("Length3"));
      if (org_size < 0) {
          org_size = 0;
      }
      pFontFile->LoadAllData(pFontStream, FALSE, org_size);
      ftData->m_nCount = 2;
      ftData->m_Obj = pFontFile;
      m_FontFileMap.SetAt(pFontStream, ftData);
      return pFontFile;
  }
  void CPDF_DocPageData::ReleaseFontFileStreamAcc(CPDF_Stream* pFontStream, FX_BOOL bForce)
  {
      if (!pFontStream) {
          return;
      }
      PDF_DocPageData_Release<CPDF_Stream*, CPDF_StreamAcc*>(m_FontFileMap, pFontStream, NULL, bForce);
  }
  