| // 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" |
| |
| CPDF_Document::CPDF_Document(CPDF_Parser* 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(); |
| CPDF_Object* indirectObj = GetIndirectObject(m_pParser->GetRootObjNum()); |
| m_pRootDict = indirectObj ? indirectObj->GetDict() : NULL; |
| if (m_pRootDict == NULL) { |
| return; |
| } |
| indirectObj = GetIndirectObject(m_pParser->GetInfoObjNum()); |
| m_pInfoDict = indirectObj ? indirectObj->GetDict() : NULL; |
| 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_pDocPage) { |
| CPDF_ModuleMgr::Get()->GetPageModule()->ReleaseDoc(this); |
| CPDF_ModuleMgr::Get()->GetPageModule()->ClearStockFont(this); |
| } |
| if (m_pDocRender) { |
| CPDF_ModuleMgr::Get()->GetRenderModule()->DestroyDocData(m_pDocRender); |
| } |
| } |
| #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 nullptr; |
| |
| if (m_bLinearized && (iPage == (int)m_dwFirstPageNo)) { |
| CPDF_Object* pObj = GetIndirectObject(m_dwFirstPageObjNum); |
| if (pObj && pObj->GetType() == PDFOBJ_DICTIONARY) { |
| return static_cast<CPDF_Dictionary*>(pObj); |
| } |
| } |
| |
| int objnum = m_PageList.GetAt(iPage); |
| if (objnum) { |
| CPDF_Object* pObj = GetIndirectObject(objnum); |
| if (pObj && pObj->GetType() == PDFOBJ_DICTIONARY) { |
| return static_cast<CPDF_Dictionary*>(pObj); |
| } |
| } |
| |
| CPDF_Dictionary* pRoot = GetRoot(); |
| if (!pRoot) |
| return nullptr; |
| |
| CPDF_Dictionary* pPages = pRoot->GetDict(FX_BSTRC("Pages")); |
| if (!pPages) |
| return nullptr; |
| |
| CPDF_Dictionary* pPage = _FindPDFPage(pPages, iPage, iPage, 0); |
| if (!pPage) |
| return nullptr; |
| |
| 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_Object* pKid = pKidList->GetElement(i); |
| if (pKid && pKid->GetType() == PDFOBJ_REFERENCE) { |
| if (((CPDF_Reference*)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); |
| } |
| 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 ? pPageDict->GetElement(FX_BSTRC("Contents")) : NULL; |
| 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_Object* pRef = pArray->GetElement(j); |
| if (pRef == NULL || pRef->GetType() != PDFOBJ_REFERENCE) { |
| continue; |
| } |
| if (((CPDF_Reference*)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((void*)(uintptr_t)objnum, (void*&)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); |
| } |
| } |