7#include "core/fpdfdoc/cpdf_nametree.h"
13#include "core/fpdfapi/parser/cpdf_array.h"
14#include "core/fpdfapi/parser/cpdf_dictionary.h"
15#include "core/fpdfapi/parser/cpdf_document.h"
16#include "core/fpdfapi/parser/cpdf_reference.h"
17#include "core/fpdfapi/parser/cpdf_string.h"
18#include "core/fpdfapi/parser/fpdf_parser_decode.h"
19#include "core/fxcrt/check.h"
20#include "core/fxcrt/ptr_util.h"
21#include "core/fxcrt/stl_util.h"
25constexpr int kNameTreeMaxRecursion = 32;
30 WideString csLeft = pLimits->GetUnicodeTextAt(0);
31 WideString csRight = pLimits->GetUnicodeTextAt(1);
34 pLimits->SetNewAt<CPDF_String>(0, csRight.AsStringView());
35 pLimits->SetNewAt<CPDF_String>(1, csLeft.AsStringView());
36 csLeft = pLimits->GetUnicodeTextAt(0);
37 csRight = pLimits->GetUnicodeTextAt(1);
39 while (pLimits->size() > 2)
40 pLimits->RemoveAt(pLimits->size() - 1);
41 return {csLeft, csRight};
50 std::vector<CPDF_Array*>* pLimits) {
51 if (nLevel > kNameTreeMaxRecursion)
54 if (pNode->GetArrayFor(
"Names") == pFind) {
55 pLimits->push_back(pNode->GetMutableArrayFor(
"Limits").Get());
63 for (size_t i = 0; i < pKids->size(); ++i) {
68 if (GetNodeAncestorsLimitsInternal(pKid, pFind, nLevel + 1, pLimits)) {
69 pLimits->push_back(pNode->GetMutableArrayFor(
"Limits").Get());
78std::vector<CPDF_Array*> GetNodeAncestorsLimits(
81 std::vector<CPDF_Array*> results;
82 GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results);
93 if (nLevel > kNameTreeMaxRecursion)
100 std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
106 if (pNames->IsEmpty() || !pLimits)
108 if (csLeft
!= csName && csRight
!= csName)
115 for (size_t i = 0; i < pNames->size() / 2; ++i) {
116 WideString wsName = pNames->GetUnicodeTextAt(i * 2);
122 pLimits->SetNewAt<CPDF_String>(0, csNewLeft.AsStringView());
123 pLimits->SetNewAt<CPDF_String>(1, csNewRight.AsStringView());
132 for (size_t i = 0; i < pKids->size(); ++i) {
136 if (!UpdateNodesAndLimitsUponDeletion(pKid.Get(), pFind, csName,
141 if ((pKid->KeyExist(
"Names") && pKid->GetArrayFor(
"Names")->IsEmpty()) ||
142 (pKid->KeyExist(
"Kids") && pKid->GetArrayFor(
"Kids")->IsEmpty())) {
145 if (pKids->IsEmpty() || !pLimits)
147 if (csLeft
!= csName && csRight
!= csName)
154 for (size_t j = 0; j < pKids->size(); ++j) {
156 pKids->GetDictAt(j)->GetArrayFor(
"Limits");
158 if (pKidLimits->GetUnicodeTextAt(0).Compare(csNewLeft) < 0)
159 csNewLeft = pKidLimits->GetUnicodeTextAt(0);
160 if (pKidLimits->GetUnicodeTextAt(1).Compare(csNewRight) > 0)
161 csNewRight = pKidLimits->GetUnicodeTextAt(1);
163 pLimits->SetNewAt<CPDF_String>(0, csNewLeft.AsStringView());
164 pLimits->SetNewAt<CPDF_String>(1, csNewRight.AsStringView());
171 std::set<uint32_t>* seen_obj_nums) {
176 bool inserted = seen_obj_nums->insert(obj_num).second;
180bool IsArrayWithTraversedObject(
const CPDF_Array* array,
181 std::set<uint32_t>* seen_obj_nums) {
182 if (IsTraversedObject(array, seen_obj_nums))
186 for (
const auto& item : locker) {
187 if (IsTraversedObject(item.Get(), seen_obj_nums))
206 std::set<uint32_t>* seen_obj_nums) {
207 if (nLevel > kNameTreeMaxRecursion)
212 if (pNames && IsArrayWithTraversedObject(pNames.Get(), seen_obj_nums))
214 if (pLimits && IsArrayWithTraversedObject(pLimits.Get(), seen_obj_nums))
218 auto [csLeft, csRight] = GetNodeLimitsAndSanitize(pLimits.Get());
220 if (csName.Compare(csLeft) < 0)
225 if (csName.Compare(csRight) > 0 && pNames) {
229 *pFindIndex =
fxcrt::CollectionSize<int32_t>(*pNames) / 2 - 1;
236 size_t dwCount = pNames->size() / 2;
237 for (size_t i = 0; i < dwCount; i++) {
238 WideString csValue = pNames->GetUnicodeTextAt(i * 2);
245 *pFindIndex =
pdfium::checked_cast<int32_t>(i);
250 return pNames->GetDirectObjectAt(i * 2 + 1);
258 if (!pKids || IsTraversedObject(pKids.Get(), seen_obj_nums))
261 for (size_t i = 0; i < pKids->size(); i++) {
263 if (!pKid || IsTraversedObject(pKid.Get(), seen_obj_nums))
267 pKid, csName, nLevel + 1, nIndex, ppFind, pFindIndex, seen_obj_nums);
282 std::set<uint32_t> seen_obj_nums;
283 return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind,
284 pFindIndex, &seen_obj_nums);
287struct IndexSearchResult {
300std::optional<IndexSearchResult> SearchNameNodeByIndexInternal(
302 size_t nTargetPairIndex,
304 size_t* nCurPairIndex) {
305 if (nLevel > kNameTreeMaxRecursion)
310 size_t nCount = pNames->size() / 2;
311 if (nTargetPairIndex >= *nCurPairIndex + nCount) {
312 *nCurPairIndex += nCount;
316 size_t index = 2 * (nTargetPairIndex - *nCurPairIndex);
321 IndexSearchResult result;
322 result.key = pNames->GetUnicodeTextAt(index);
323 result.value =
std::move(value);
324 result.container =
std::move(pNames);
325 result.index = index;
333 for (size_t i = 0; i < pKids->size(); i++) {
337 std::optional<IndexSearchResult> result = SearchNameNodeByIndexInternal(
338 pKid.Get(), nTargetPairIndex, nLevel + 1, nCurPairIndex);
339 if (result.has_value())
347std::optional<IndexSearchResult> SearchNameNodeByIndex(
349 size_t nTargetPairIndex) {
350 size_t nCurPairIndex = 0;
351 return SearchNameNodeByIndexInternal(pNode, nTargetPairIndex, 0,
358 std::set<
const CPDF_Dictionary*>& seen) {
359 if (nLevel > kNameTreeMaxRecursion)
362 const bool inserted = seen.insert(pNode).second;
368 return pNames->size() / 2;
375 for (size_t i = 0; i < pKids->size(); i++) {
380 nCount += CountNamesInternal(pKid.Get(), nLevel + 1, seen);
392 return dict->GetArrayFor(
"D");
399 pDoc->GetRoot()->GetDictFor(
"Dests");
402 return GetNamedDestFromObject(pDests->GetDirectObjectFor(name));
408 : m_pRoot(std::move(pRoot)) {
430 return pdfium::WrapUnique(
446 pRoot->SetNewFor<CPDF_Reference>(
"Names", pDoc, pNames->GetObjNum());
454 pNames->SetNewFor<CPDF_Reference>(category, pDoc, pCategory->GetObjNum());
463 return pdfium::WrapUnique(
472 std::unique_ptr<CPDF_NameTree> name_tree = Create(pDoc,
"Dests");
474 dest_array = name_tree->LookupNewStyleNamedDest(name);
476 dest_array = LookupOldStyleNamedDest(pDoc, name);
481 std::set<
const CPDF_Dictionary*> seen;
482 return CountNamesInternal(m_pRoot.Get(), 0, seen);
492 if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor(
"Kids"))
497 if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex))
506 std::optional<IndexSearchResult> result =
507 SearchNameNodeByIndex(m_pRoot.Get(), 0);
508 if (!result.has_value()) {
513 pFind = result.value().container;
519 size_t nNameIndex = (nFindIndex + 1) * 2;
520 size_t nValueIndex = nNameIndex + 1;
521 pFind->InsertNewAt<CPDF_String>(nNameIndex, name.AsStringView());
522 pFind->InsertAt(nValueIndex,
std::move(pObj));
526 std::vector<CPDF_Array*> all_limits =
527 GetNodeAncestorsLimits(m_pRoot, pFind.Get());
528 for (
auto* pLimits : all_limits) {
532 if (name.Compare(pLimits->GetUnicodeTextAt(0)) < 0)
533 pLimits->SetNewAt<CPDF_String>(0, name.AsStringView());
535 if (name.Compare(pLimits->GetUnicodeTextAt(1)) > 0)
536 pLimits->SetNewAt<CPDF_String>(1, name.AsStringView());
542 std::optional<IndexSearchResult> result =
543 SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
551 pFind->RemoveAt(result.value().index + 1);
552 pFind->RemoveAt(result.value().index);
555 UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind.Get(),
556 result.value().key, 0);
563 std::optional<IndexSearchResult> result =
564 SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
570 *csName =
std::move(result.value().key);
571 return result.value().value;
576 return SearchNameNodeByName(m_pRoot, csName,
nullptr,
nullptr);
581 return GetNamedDestFromObject(
582 LookupValue(PDF_DecodeText(sName.unsigned_span())));
fxcrt::ByteString ByteString
CPDF_ArrayLocker(const CPDF_Array *pArray)
std::vector< RetainPtr< CPDF_Object > >::const_iterator const_iterator
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
bool AddValueAndName(RetainPtr< CPDF_Object > pObj, const WideString &name)
static RetainPtr< const CPDF_Array > LookupNamedDest(CPDF_Document *doc, const ByteString &name)
RetainPtr< const CPDF_Object > LookupValue(const WideString &csName) const
RetainPtr< CPDF_Object > LookupValueAndName(size_t nIndex, WideString *csName) const
bool DeleteValueAndName(size_t nIndex)
uint32_t GetObjNum() const
bool operator!=(const WideString &other) const
WideString & operator=(const WideString &that)
int Compare(const WideString &str) const
fxcrt::WideString WideString