// Copyright 2016 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 "core/fpdfdoc/cpdf_nametree.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
namespace {
const int nMaxRecursion = 32;
CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode,
const CFX_ByteString& csName,
size_t& nIndex,
CPDF_Array** ppFind,
int nLevel = 0) {
if (nLevel > nMaxRecursion)
return nullptr;
CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
if (pLimits) {
CFX_ByteString csLeft = pLimits->GetStringAt(0);
CFX_ByteString csRight = pLimits->GetStringAt(1);
if (csLeft.Compare(csRight.AsStringC()) > 0) {
CFX_ByteString csTmp = csRight;
csRight = csLeft;
csLeft = csTmp;
}
if (csName.Compare(csLeft.AsStringC()) < 0 ||
csName.Compare(csRight.AsStringC()) > 0) {
return nullptr;
}
}
CPDF_Array* pNames = pNode->GetArrayFor("Names");
if (pNames) {
size_t dwCount = pNames->GetCount() / 2;
for (size_t i = 0; i < dwCount; i++) {
CFX_ByteString csValue = pNames->GetStringAt(i * 2);
int32_t iCompare = csValue.Compare(csName.AsStringC());
if (iCompare <= 0) {
if (ppFind)
*ppFind = pNames;
if (iCompare < 0)
continue;
} else {
break;
}
nIndex += i;
return pNames->GetDirectObjectAt(i * 2 + 1);
}
nIndex += dwCount;
return nullptr;
}
CPDF_Array* pKids = pNode->GetArrayFor("Kids");
if (!pKids)
return nullptr;
for (size_t i = 0; i < pKids->GetCount(); i++) {
CPDF_Dictionary* pKid = pKids->GetDictAt(i);
if (!pKid)
continue;
CPDF_Object* pFound =
SearchNameNode(pKid, csName, nIndex, ppFind, nLevel + 1);
if (pFound)
return pFound;
}
return nullptr;
}
CPDF_Object* SearchNameNode(CPDF_Dictionary* pNode,
size_t nIndex,
size_t& nCurIndex,
CFX_ByteString& csName,
CPDF_Array** ppFind,
int nLevel = 0) {
if (nLevel > nMaxRecursion)
return nullptr;
CPDF_Array* pNames = pNode->GetArrayFor("Names");
if (pNames) {
size_t nCount = pNames->GetCount() / 2;
if (nIndex >= nCurIndex + nCount) {
nCurIndex += nCount;
return nullptr;
}
if (ppFind)
*ppFind = pNames;
csName = pNames->GetStringAt((nIndex - nCurIndex) * 2);
return pNames->GetDirectObjectAt((nIndex - nCurIndex) * 2 + 1);
}
CPDF_Array* pKids = pNode->GetArrayFor("Kids");
if (!pKids)
return nullptr;
for (size_t i = 0; i < pKids->GetCount(); i++) {
CPDF_Dictionary* pKid = pKids->GetDictAt(i);
if (!pKid)
continue;
CPDF_Object* pFound =
SearchNameNode(pKid, nIndex, nCurIndex, csName, ppFind, nLevel + 1);
if (pFound)
return pFound;
}
return nullptr;
}
size_t CountNames(CPDF_Dictionary* pNode, int nLevel = 0) {
if (nLevel > nMaxRecursion)
return 0;
CPDF_Array* pNames = pNode->GetArrayFor("Names");
if (pNames)
return pNames->GetCount() / 2;
CPDF_Array* pKids = pNode->GetArrayFor("Kids");
if (!pKids)
return 0;
size_t nCount = 0;
for (size_t i = 0; i < pKids->GetCount(); i++) {
CPDF_Dictionary* pKid = pKids->GetDictAt(i);
if (!pKid)
continue;
nCount += CountNames(pKid, nLevel + 1);
}
return nCount;
}
} // namespace
CPDF_NameTree::CPDF_NameTree(CPDF_Document* pDoc,
const CFX_ByteString& category)
: m_pRoot(nullptr) {
CPDF_Dictionary* pRoot = pDoc->GetRoot();
if (!pRoot)
return;
CPDF_Dictionary* pNames = pRoot->GetDictFor("Names");
if (!pNames)
return;
m_pRoot = pNames->GetDictFor(category);
}
size_t CPDF_NameTree::GetCount() const {
return m_pRoot ? ::CountNames(m_pRoot) : 0;
}
int CPDF_NameTree::GetIndex(const CFX_ByteString& csName) const {
if (!m_pRoot)
return -1;
size_t nIndex = 0;
if (!SearchNameNode(m_pRoot, csName, nIndex, nullptr))
return -1;
return nIndex;
}
CPDF_Object* CPDF_NameTree::LookupValue(int nIndex,
CFX_ByteString& csName) const {
if (!m_pRoot)
return nullptr;
size_t nCurIndex = 0;
return SearchNameNode(m_pRoot, nIndex, nCurIndex, csName, nullptr);
}
CPDF_Object* CPDF_NameTree::LookupValue(const CFX_ByteString& csName) const {
if (!m_pRoot)
return nullptr;
size_t nIndex = 0;
return SearchNameNode(m_pRoot, csName, nIndex, nullptr);
}
CPDF_Array* CPDF_NameTree::LookupNamedDest(CPDF_Document* pDoc,
const CFX_ByteString& sName) {
CPDF_Object* pValue = LookupValue(sName);
if (!pValue) {
CPDF_Dictionary* pDests = pDoc->GetRoot()->GetDictFor("Dests");
if (!pDests)
return nullptr;
pValue = pDests->GetDirectObjectFor(sName);
}
if (!pValue)
return nullptr;
if (CPDF_Array* pArray = pValue->AsArray())
return pArray;
if (CPDF_Dictionary* pDict = pValue->AsDictionary())
return pDict->GetArrayFor("D");
return nullptr;
}