// 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_parser.h" | |
#include "../../../include/fpdfapi/fpdf_module.h" | |
extern FX_LPVOID PDFPreviewInitCache(CPDF_Document* pDoc); | |
extern void PDFPreviewClearCache(FX_LPVOID pCache); | |
CPDF_Document::CPDF_Document(IPDF_DocParser* pParser) : CPDF_IndirectObjects(pParser) | |
{ | |
ASSERT(pParser != NULL); | |
m_pRootDict = NULL; | |
m_pInfoDict = NULL; | |
m_bLinearized = FALSE; | |
m_dwFirstPageNo = 0; | |
m_dwFirstPageObjNum = 0; | |
m_pDocPage = CPDF_ModuleMgr::Get()->GetPageModule()->CreateDocData(this); | |
m_pDocRender = CPDF_ModuleMgr::Get()->GetRenderModule()->CreateDocData(this); | |
} | |
CPDF_DocPageData* CPDF_Document::GetValidatePageData() | |
{ | |
if (m_pDocPage) { | |
return m_pDocPage; | |
} | |
m_pDocPage = CPDF_ModuleMgr::Get()->GetPageModule()->CreateDocData(this); | |
return m_pDocPage; | |
} | |
CPDF_DocRenderData* CPDF_Document::GetValidateRenderData() | |
{ | |
if (m_pDocRender) { | |
return m_pDocRender; | |
} | |
m_pDocRender = CPDF_ModuleMgr::Get()->GetRenderModule()->CreateDocData(this); | |
return m_pDocRender; | |
} | |
void CPDF_Document::LoadDoc() | |
{ | |
m_LastObjNum = m_pParser->GetLastObjNum(); | |
CPDF_Object* pRootObj = GetIndirectObject(m_pParser->GetRootObjNum()); | |
if (pRootObj == NULL) { | |
return; | |
} | |
m_pRootDict = pRootObj->GetDict(); | |
if (m_pRootDict == NULL) { | |
return; | |
} | |
CPDF_Object* pInfoObj = GetIndirectObject(m_pParser->GetInfoObjNum()); | |
if (pInfoObj) { | |
m_pInfoDict = pInfoObj->GetDict(); | |
} | |
CPDF_Array* pIDArray = m_pParser->GetIDArray(); | |
if (pIDArray) { | |
m_ID1 = pIDArray->GetString(0); | |
m_ID2 = pIDArray->GetString(1); | |
} | |
m_PageList.SetSize(_GetPageCount()); | |
} | |
void CPDF_Document::LoadAsynDoc(CPDF_Dictionary *pLinearized) | |
{ | |
m_bLinearized = TRUE; | |
m_LastObjNum = m_pParser->GetLastObjNum(); | |
m_pRootDict = GetIndirectObject(m_pParser->GetRootObjNum())->GetDict(); | |
if (m_pRootDict == NULL) { | |
return; | |
} | |
m_pInfoDict = GetIndirectObject(m_pParser->GetInfoObjNum())->GetDict(); | |
CPDF_Array* pIDArray = m_pParser->GetIDArray(); | |
if (pIDArray) { | |
m_ID1 = pIDArray->GetString(0); | |
m_ID2 = pIDArray->GetString(1); | |
} | |
FX_DWORD dwPageCount = 0; | |
CPDF_Object *pCount = pLinearized->GetElement(FX_BSTRC("N")); | |
if (pCount && pCount->GetType() == PDFOBJ_NUMBER) { | |
dwPageCount = pCount->GetInteger(); | |
} | |
m_PageList.SetSize(dwPageCount); | |
CPDF_Object *pNo = pLinearized->GetElement(FX_BSTRC("P")); | |
if (pNo && pNo->GetType() == PDFOBJ_NUMBER) { | |
m_dwFirstPageNo = pNo->GetInteger(); | |
} | |
CPDF_Object *pObjNum = pLinearized->GetElement(FX_BSTRC("O")); | |
if (pObjNum && pObjNum->GetType() == PDFOBJ_NUMBER) { | |
m_dwFirstPageObjNum = pObjNum->GetInteger(); | |
} | |
} | |
void CPDF_Document::LoadPages() | |
{ | |
m_PageList.SetSize(_GetPageCount()); | |
} | |
extern void FPDF_TTFaceMapper_ReleaseDoc(CPDF_Document*); | |
CPDF_Document::~CPDF_Document() | |
{ | |
if (m_pDocRender) { | |
CPDF_ModuleMgr::Get()->GetRenderModule()->DestroyDocData(m_pDocRender); | |
} | |
if (m_pDocPage) { | |
CPDF_ModuleMgr::Get()->GetPageModule()->ReleaseDoc(this); | |
CPDF_ModuleMgr::Get()->GetPageModule()->ClearStockFont(this); | |
} | |
} | |
#define FX_MAX_PAGE_LEVEL 1024 | |
CPDF_Dictionary* CPDF_Document::_FindPDFPage(CPDF_Dictionary* pPages, int iPage, int nPagesToGo, int level) | |
{ | |
CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids")); | |
if (pKidList == NULL) { | |
if (nPagesToGo == 0) { | |
return pPages; | |
} | |
return NULL; | |
} | |
if (level >= FX_MAX_PAGE_LEVEL) { | |
return NULL; | |
} | |
int nKids = pKidList->GetCount(); | |
for (int i = 0; i < nKids; i ++) { | |
CPDF_Dictionary* pKid = pKidList->GetDict(i); | |
if (pKid == NULL) { | |
nPagesToGo --; | |
continue; | |
} | |
if (pKid == pPages) { | |
continue; | |
} | |
if (!pKid->KeyExist(FX_BSTRC("Kids"))) { | |
if (nPagesToGo == 0) { | |
return pKid; | |
} | |
m_PageList.SetAt(iPage - nPagesToGo, pKid->GetObjNum()); | |
nPagesToGo --; | |
} else { | |
int nPages = pKid->GetInteger(FX_BSTRC("Count")); | |
if (nPagesToGo < nPages) { | |
return _FindPDFPage(pKid, iPage, nPagesToGo, level + 1); | |
} | |
nPagesToGo -= nPages; | |
} | |
} | |
return NULL; | |
} | |
CPDF_Dictionary* CPDF_Document::GetPage(int iPage) | |
{ | |
if (iPage < 0 || iPage >= m_PageList.GetSize()) { | |
return NULL; | |
} | |
if (m_bLinearized && (iPage == (int)m_dwFirstPageNo)) { | |
CPDF_Object* pObj = GetIndirectObject(m_dwFirstPageObjNum); | |
if (pObj && pObj->GetType() == PDFOBJ_DICTIONARY) { | |
return (CPDF_Dictionary*)pObj; | |
} | |
} | |
int objnum = m_PageList.GetAt(iPage); | |
if (objnum) { | |
CPDF_Object* pObj = GetIndirectObject(objnum); | |
ASSERT(pObj->GetType() == PDFOBJ_DICTIONARY); | |
return (CPDF_Dictionary*)pObj; | |
} | |
CPDF_Dictionary* pRoot = GetRoot(); | |
if (pRoot == NULL) { | |
return NULL; | |
} | |
CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages")); | |
if (pPages == NULL) { | |
return NULL; | |
} | |
CPDF_Dictionary* pPage = _FindPDFPage(pPages, iPage, iPage, 0); | |
if (pPage == NULL) { | |
return NULL; | |
} | |
m_PageList.SetAt(iPage, pPage->GetObjNum()); | |
return pPage; | |
} | |
int CPDF_Document::_FindPageIndex(CPDF_Dictionary* pNode, FX_DWORD& skip_count, FX_DWORD objnum, int& index, int level) | |
{ | |
if (pNode->KeyExist(FX_BSTRC("Kids"))) { | |
CPDF_Array* pKidList = pNode->GetArray(FX_BSTRC("Kids")); | |
if (pKidList == NULL) { | |
return -1; | |
} | |
if (level >= FX_MAX_PAGE_LEVEL) { | |
return -1; | |
} | |
FX_DWORD count = pNode->GetInteger(FX_BSTRC("Count")); | |
if (count <= skip_count) { | |
skip_count -= count; | |
index += count; | |
return -1; | |
} | |
if (count && count == pKidList->GetCount()) { | |
for (FX_DWORD i = 0; i < count; i ++) { | |
CPDF_Reference* pKid = (CPDF_Reference*)pKidList->GetElement(i); | |
if (pKid && pKid->GetType() == PDFOBJ_REFERENCE) { | |
if (pKid->GetRefObjNum() == objnum) { | |
m_PageList.SetAt(index + i, objnum); | |
return index + i; | |
} | |
} | |
} | |
} | |
for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) { | |
CPDF_Dictionary* pKid = pKidList->GetDict(i); | |
if (pKid == NULL) { | |
continue; | |
} | |
if (pKid == pNode) { | |
continue; | |
} | |
int found_index = _FindPageIndex(pKid, skip_count, objnum, index, level + 1); | |
if (found_index >= 0) { | |
return found_index; | |
} | |
} | |
} else { | |
if (objnum == pNode->GetObjNum()) { | |
return index; | |
} | |
if (skip_count) { | |
skip_count--; | |
} | |
index ++; | |
} | |
return -1; | |
} | |
int CPDF_Document::GetPageIndex(FX_DWORD objnum) | |
{ | |
FX_DWORD nPages = m_PageList.GetSize(); | |
FX_DWORD skip_count = 0; | |
FX_BOOL bSkipped = FALSE; | |
for (FX_DWORD i = 0; i < nPages; i ++) { | |
FX_DWORD objnum1 = m_PageList.GetAt(i); | |
if (objnum1 == objnum) { | |
return i; | |
} | |
if (!bSkipped && objnum1 == 0) { | |
skip_count = i; | |
bSkipped = TRUE; | |
} | |
} | |
CPDF_Dictionary* pRoot = GetRoot(); | |
if (pRoot == NULL) { | |
return -1; | |
} | |
CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages")); | |
if (pPages == NULL) { | |
return -1; | |
} | |
int index = 0; | |
return _FindPageIndex(pPages, skip_count, objnum, index); | |
} | |
int CPDF_Document::GetPageCount() const | |
{ | |
return m_PageList.GetSize(); | |
} | |
static int _CountPages(CPDF_Dictionary* pPages, int level) | |
{ | |
if (level > 128) { | |
return 0; | |
} | |
int count = pPages->GetInteger(FX_BSTRC("Count")); | |
if (count > 0 && count < FPDF_PAGE_MAX_NUM) { | |
return count; | |
} | |
CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids")); | |
if (pKidList == NULL) { | |
return 0; | |
} | |
count = 0; | |
for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) { | |
CPDF_Dictionary* pKid = pKidList->GetDict(i); | |
if (pKid == NULL) { | |
continue; | |
} | |
if (!pKid->KeyExist(FX_BSTRC("Kids"))) { | |
count ++; | |
} else { | |
count += _CountPages(pKid, level + 1); | |
} | |
} | |
pPages->SetAtInteger(FX_BSTRC("Count"), count); | |
return count; | |
} | |
int CPDF_Document::_GetPageCount() const | |
{ | |
CPDF_Dictionary* pRoot = GetRoot(); | |
if (pRoot == NULL) { | |
return 0; | |
} | |
CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages")); | |
if (pPages == NULL) { | |
return 0; | |
} | |
if (!pPages->KeyExist(FX_BSTRC("Kids"))) { | |
return 1; | |
} | |
return _CountPages(pPages, 0); | |
} | |
static FX_BOOL _EnumPages(CPDF_Dictionary* pPages, IPDF_EnumPageHandler* pHandler) | |
{ | |
CPDF_Array* pKidList = pPages->GetArray(FX_BSTRC("Kids")); | |
if (pKidList == NULL) { | |
return pHandler->EnumPage(pPages); | |
} | |
for (FX_DWORD i = 0; i < pKidList->GetCount(); i ++) { | |
CPDF_Dictionary* pKid = pKidList->GetDict(i); | |
if (pKid == NULL) { | |
continue; | |
} | |
if (!pKid->KeyExist(FX_BSTRC("Kids"))) { | |
if (!pHandler->EnumPage(pKid)) { | |
return FALSE; | |
} | |
} else { | |
return _EnumPages(pKid, pHandler); | |
} | |
} | |
return TRUE; | |
} | |
void CPDF_Document::EnumPages(IPDF_EnumPageHandler* pHandler) | |
{ | |
CPDF_Dictionary* pRoot = GetRoot(); | |
if (pRoot == NULL) { | |
return; | |
} | |
CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages")); | |
if (pPages == NULL) { | |
return; | |
} | |
_EnumPages(pPages, pHandler); | |
} | |
FX_BOOL CPDF_Document::IsContentUsedElsewhere(FX_DWORD objnum, CPDF_Dictionary* pThisPageDict) | |
{ | |
for (int i = 0; i < m_PageList.GetSize(); i ++) { | |
CPDF_Dictionary* pPageDict = GetPage(i); | |
if (pPageDict == pThisPageDict) { | |
continue; | |
} | |
CPDF_Object* pContents = pPageDict->GetElement(FX_BSTRC("Contents")); | |
if (pContents == NULL) { | |
continue; | |
} | |
if (pContents->GetDirectType() == PDFOBJ_ARRAY) { | |
CPDF_Array* pArray = (CPDF_Array*)pContents->GetDirect(); | |
for (FX_DWORD j = 0; j < pArray->GetCount(); j ++) { | |
CPDF_Reference* pRef = (CPDF_Reference*)pArray->GetElement(j); | |
if (pRef->GetRefObjNum() == objnum) { | |
return TRUE; | |
} | |
} | |
} else if (pContents->GetObjNum() == objnum) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
FX_DWORD CPDF_Document::GetUserPermissions(FX_BOOL bCheckRevision) const | |
{ | |
if (m_pParser == NULL) { | |
return (FX_DWORD) - 1; | |
} | |
return m_pParser->GetPermissions(bCheckRevision); | |
} | |
FX_BOOL CPDF_Document::IsOwner() const | |
{ | |
if (m_pParser == NULL) { | |
return TRUE; | |
} | |
return m_pParser->IsOwner(); | |
} | |
FX_BOOL CPDF_Document::IsFormStream(FX_DWORD objnum, FX_BOOL& bForm) const | |
{ | |
{ | |
CPDF_Object* pObj; | |
if (m_IndirectObjs.Lookup((FX_LPVOID)(FX_UINTPTR)objnum, (FX_LPVOID&)pObj)) { | |
bForm = pObj->GetType() == PDFOBJ_STREAM && | |
((CPDF_Stream*)pObj)->GetDict()->GetString(FX_BSTRC("Subtype")) == FX_BSTRC("Form"); | |
return TRUE; | |
} | |
} | |
if (m_pParser == NULL) { | |
bForm = FALSE; | |
return TRUE; | |
} | |
return m_pParser->IsFormStream(objnum, bForm); | |
} | |
void CPDF_Document::ClearPageData() | |
{ | |
if (m_pDocPage) { | |
CPDF_ModuleMgr::Get()->GetPageModule()->ClearDoc(this); | |
} | |
} | |
void CPDF_Document::ClearRenderData() | |
{ | |
if (m_pDocRender) { | |
CPDF_ModuleMgr::Get()->GetRenderModule()->ClearDocData(m_pDocRender); | |
} | |
} |