Index: core/fpdfapi/parser/cpdf_document.cpp |
diff --git a/core/fpdfapi/parser/cpdf_document.cpp b/core/fpdfapi/parser/cpdf_document.cpp |
index 64574047e5733cc4ebb8d2f07a6f7811d2ba849f..8e181de97cf54a94010c3ff88c9e0b360cf80fa7 100644 |
--- a/core/fpdfapi/parser/cpdf_document.cpp |
+++ b/core/fpdfapi/parser/cpdf_document.cpp |
@@ -336,6 +336,7 @@ CPDF_Document::CPDF_Document(std::unique_ptr<CPDF_Parser> pParser) |
m_pParser(std::move(pParser)), |
m_pRootDict(nullptr), |
m_pInfoDict(nullptr), |
+ m_iNextPageToTraverse(0), |
m_bLinearized(false), |
m_iFirstPageNo(0), |
m_dwFirstPageObjNum(0), |
@@ -400,40 +401,72 @@ void CPDF_Document::LoadPages() { |
m_PageList.SetSize(RetrievePageCount()); |
} |
-CPDF_Dictionary* CPDF_Document::FindPDFPage(CPDF_Dictionary* pPages, |
- int iPage, |
- int nPagesToGo, |
- int level) { |
+CPDF_Dictionary* CPDF_Document::TraversePDFPages(int iPage, |
+ int* nPagesToGo, |
+ size_t level) { |
+ if (*nPagesToGo < 0) |
+ return nullptr; |
+ CPDF_Dictionary* pPages = m_pTreeTraversal[level].first; |
CPDF_Array* pKidList = pPages->GetArrayFor("Kids"); |
- if (!pKidList) |
- return nPagesToGo == 0 ? pPages : nullptr; |
+ if (!pKidList) { |
+ if (*nPagesToGo != 1) |
+ return nullptr; |
+ m_PageList.SetAt(iPage, pPages->GetObjNum()); |
+ return pPages; |
+ } |
- if (level >= FX_MAX_PAGE_LEVEL) |
+ if (level >= FX_MAX_PAGE_LEVEL) { |
+ m_pTreeTraversal.pop_back(); |
return nullptr; |
+ } |
- for (size_t i = 0; i < pKidList->GetCount(); i++) { |
+ CPDF_Dictionary* page = nullptr; |
+ for (size_t i = m_pTreeTraversal[level].second; i < pKidList->GetCount(); |
+ i++) { |
+ if (*nPagesToGo == 0) |
+ break; |
CPDF_Dictionary* pKid = pKidList->GetDictAt(i); |
if (!pKid) { |
- nPagesToGo--; |
+ (*nPagesToGo)--; |
+ m_pTreeTraversal[level].second++; |
continue; |
} |
- if (pKid == pPages) |
+ if (pKid == pPages) { |
+ m_pTreeTraversal[level].second++; |
continue; |
+ } |
if (!pKid->KeyExist("Kids")) { |
- if (nPagesToGo == 0) |
- return pKid; |
- |
- m_PageList.SetAt(iPage - nPagesToGo, pKid->GetObjNum()); |
- nPagesToGo--; |
+ m_PageList.SetAt(iPage - (*nPagesToGo) + 1, pKid->GetObjNum()); |
+ (*nPagesToGo)--; |
+ m_pTreeTraversal[level].second++; |
+ if (*nPagesToGo == 0) { |
+ page = pKid; |
+ break; |
+ } |
} else { |
- int nPages = pKid->GetIntegerFor("Count"); |
- if (nPagesToGo < nPages) |
- return FindPDFPage(pKid, iPage, nPagesToGo, level + 1); |
- |
- nPagesToGo -= nPages; |
+ // If the vector has size level+1, the child is not in yet |
+ if (m_pTreeTraversal.size() == level + 1) |
+ m_pTreeTraversal.push_back(std::make_pair(pKid, 0)); |
+ // Now m_pTreeTraversal[level+1] should exist and be equal to pKid. |
+ CPDF_Dictionary* pageKid = TraversePDFPages(iPage, nPagesToGo, level + 1); |
+ // Check if child was completely processed, i.e. it popped itself out |
+ if (m_pTreeTraversal.size() == level + 1) |
+ m_pTreeTraversal[level].second++; |
+ // If child did not finish or if no pages to go, we are done |
+ if (m_pTreeTraversal.size() != level + 1 || *nPagesToGo == 0) { |
+ page = pageKid; |
+ break; |
+ } |
} |
} |
- return nullptr; |
+ if (m_pTreeTraversal[level].second == pKidList->GetCount()) |
+ m_pTreeTraversal.pop_back(); |
+ return page; |
+} |
+ |
+void CPDF_Document::ResetTraversal() { |
+ m_iNextPageToTraverse = 0; |
+ m_pTreeTraversal.clear(); |
} |
CPDF_Dictionary* CPDF_Document::GetPagesDict() const { |
@@ -460,17 +493,18 @@ CPDF_Dictionary* CPDF_Document::GetPage(int iPage) { |
if (objnum) { |
if (CPDF_Dictionary* pDict = ToDictionary(GetOrParseIndirectObject(objnum))) |
return pDict; |
+ return nullptr; |
} |
CPDF_Dictionary* pPages = GetPagesDict(); |
if (!pPages) |
return nullptr; |
- CPDF_Dictionary* pPage = FindPDFPage(pPages, iPage, iPage, 0); |
- if (!pPage) |
- return nullptr; |
- |
- m_PageList.SetAt(iPage, pPage->GetObjNum()); |
+ if (m_pTreeTraversal.empty()) |
+ m_pTreeTraversal.push_back(std::make_pair(pPages, 0)); |
+ int nPagesToGo = iPage - m_iNextPageToTraverse + 1; |
+ CPDF_Dictionary* pPage = TraversePDFPages(iPage, &nPagesToGo, 0); |
+ m_iNextPageToTraverse = iPage + 1; |
return pPage; |
} |
@@ -664,6 +698,7 @@ bool CPDF_Document::InsertDeletePDFPage(CPDF_Dictionary* pPages, |
} |
pPages->SetIntegerFor( |
"Count", pPages->GetIntegerFor("Count") + (bInsert ? 1 : -1)); |
+ ResetTraversal(); |
break; |
} |
int nPages = pKid->GetIntegerFor("Count"); |
@@ -704,6 +739,7 @@ bool CPDF_Document::InsertNewPage(int iPage, CPDF_Dictionary* pPageDict) { |
pPagesList->Add(new CPDF_Reference(this, pPageDict->GetObjNum())); |
pPages->SetIntegerFor("Count", nPages + 1); |
pPageDict->SetReferenceFor("Parent", this, pPages->GetObjNum()); |
+ ResetTraversal(); |
} else { |
std::set<CPDF_Dictionary*> stack = {pPages}; |
if (!InsertDeletePDFPage(pPages, iPage, pPageDict, true, &stack)) |