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/stl_util.h"
20#include "third_party/base/check.h"
21#include "third_party/base/memory/ptr_util.h"
25constexpr int kNameTreeMaxRecursion = 32;
27std::pair<WideString, WideString> GetNodeLimitsAndSanitize(
28 CPDF_Array* pLimits) {
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};
47bool GetNodeAncestorsLimitsInternal(
const RetainPtr<CPDF_Dictionary>& pNode,
48 const CPDF_Array* pFind,
50 std::vector<CPDF_Array*>* pLimits) {
51 if (nLevel > kNameTreeMaxRecursion)
54 if (pNode->GetArrayFor(
"Names") == pFind) {
55 pLimits->push_back(pNode->GetMutableArrayFor(
"Limits").Get());
59 RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor(
"Kids");
63 for (size_t i = 0; i < pKids->size(); ++i) {
64 RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
68 if (GetNodeAncestorsLimitsInternal(pKid, pFind, nLevel + 1, pLimits)) {
69 pLimits->push_back(pNode->GetMutableArrayFor(
"Limits").Get());
78std::vector<CPDF_Array*> GetNodeAncestorsLimits(
80 const CPDF_Array* pFind) {
81 std::vector<CPDF_Array*> results;
82 GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results);
89bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode,
90 const CPDF_Array* pFind,
91 const WideString& csName,
93 if (nLevel > kNameTreeMaxRecursion)
96 RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor(
"Limits");
100 std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
102 RetainPtr<
const CPDF_Array> pNames = pNode->GetArrayFor(
"Names");
106 if (pNames->IsEmpty() || !pLimits)
108 if (csLeft
!= csName && csRight
!= csName)
113 WideString csNewLeft = csRight;
114 WideString csNewRight = csLeft;
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());
127 RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor(
"Kids");
132 for (size_t i = 0; i < pKids->size(); ++i) {
133 RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(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)
152 WideString csNewLeft = csRight;
153 WideString csNewRight = csLeft;
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))
201 const WideString& csName,
206 std::set<uint32_t>* seen_obj_nums) {
207 if (nLevel > kNameTreeMaxRecursion)
210 RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor(
"Limits");
211 RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor(
"Names");
212 if (pNames && IsArrayWithTraversedObject(pNames.Get(), seen_obj_nums))
214 if (pLimits && IsArrayWithTraversedObject(pLimits.Get(), seen_obj_nums))
220 std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
231 *pFindIndex =
fxcrt::CollectionSize<int32_t>(*pNames) / 2 - 1;
238 size_t dwCount = pNames->size() / 2;
239 for (size_t i = 0; i < dwCount; i++) {
240 WideString csValue = pNames->GetUnicodeTextAt(i * 2);
247 *pFindIndex = pdfium::base::checked_cast<int32_t>(i);
252 return pNames->GetDirectObjectAt(i * 2 + 1);
259 RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor(
"Kids");
260 if (!pKids || IsTraversedObject(pKids.Get(), seen_obj_nums))
263 for (size_t i = 0; i < pKids->size(); i++) {
264 RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
265 if (!pKid || IsTraversedObject(pKid.Get(), seen_obj_nums))
269 pKid, csName, nLevel + 1, nIndex, ppFind, pFindIndex, seen_obj_nums);
280 const WideString& csName,
284 std::set<uint32_t> seen_obj_nums;
285 return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind,
286 pFindIndex, &seen_obj_nums);
289struct IndexSearchResult {
302absl::optional<IndexSearchResult> SearchNameNodeByIndexInternal(
303 CPDF_Dictionary* pNode,
304 size_t nTargetPairIndex,
306 size_t* nCurPairIndex) {
307 if (nLevel > kNameTreeMaxRecursion)
308 return absl::nullopt;
310 RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor(
"Names");
312 size_t nCount = pNames->size() / 2;
313 if (nTargetPairIndex >= *nCurPairIndex + nCount) {
314 *nCurPairIndex += nCount;
315 return absl::nullopt;
318 size_t index = 2 * (nTargetPairIndex - *nCurPairIndex);
321 return absl::nullopt;
323 IndexSearchResult result;
324 result.key = pNames->GetUnicodeTextAt(index);
325 result.value =
std::move(value);
326 result.container =
std::move(pNames);
327 result.index = index;
331 RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor(
"Kids");
333 return absl::nullopt;
335 for (size_t i = 0; i < pKids->size(); i++) {
336 RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
339 absl::optional<IndexSearchResult> result = SearchNameNodeByIndexInternal(
340 pKid.Get(), nTargetPairIndex, nLevel + 1, nCurPairIndex);
341 if (result.has_value())
344 return absl::nullopt;
349absl::optional<IndexSearchResult> SearchNameNodeByIndex(
350 CPDF_Dictionary* pNode,
351 size_t nTargetPairIndex) {
352 size_t nCurPairIndex = 0;
353 return SearchNameNodeByIndexInternal(pNode, nTargetPairIndex, 0,
358size_t CountNamesInternal(
const CPDF_Dictionary* pNode,
360 std::set<
const CPDF_Dictionary*>& seen) {
361 if (nLevel > kNameTreeMaxRecursion)
364 const bool inserted = seen.insert(pNode).second;
368 RetainPtr<
const CPDF_Array> pNames = pNode->GetArrayFor(
"Names");
370 return pNames->size() / 2;
372 RetainPtr<
const CPDF_Array> pKids = pNode->GetArrayFor(
"Kids");
377 for (size_t i = 0; i < pKids->size(); i++) {
378 RetainPtr<
const CPDF_Dictionary> pKid = pKids->GetDictAt(i);
382 nCount += CountNamesInternal(pKid.Get(), nLevel + 1, seen);
387RetainPtr<
const CPDF_Array> GetNamedDestFromObject(
389 RetainPtr<
const CPDF_Array> array = ToArray(obj);
392 RetainPtr<
const CPDF_Dictionary> dict = ToDictionary(obj);
394 return dict->GetArrayFor(
"D");
399 const ByteString& name) {
400 RetainPtr<
const CPDF_Dictionary> pDests =
401 pDoc->GetRoot()->GetDictFor(
"Dests");
404 return GetNamedDestFromObject(pDests->GetDirectObjectFor(name));
410 : m_pRoot(std::move(pRoot)) {
419 const ByteString& category) {
420 RetainPtr<CPDF_Dictionary> pRoot = pDoc->GetMutableRoot();
424 RetainPtr<CPDF_Dictionary> pNames = pRoot->GetMutableDictFor(
"Names");
428 RetainPtr<CPDF_Dictionary> pCategory = pNames->GetMutableDictFor(category);
432 return pdfium::WrapUnique(
439 const ByteString& category) {
440 RetainPtr<CPDF_Dictionary> pRoot = pDoc->GetMutableRoot();
445 RetainPtr<CPDF_Dictionary> pNames = pRoot->GetMutableDictFor(
"Names");
447 pNames = pDoc->NewIndirect<CPDF_Dictionary>();
448 pRoot->SetNewFor<CPDF_Reference>(
"Names", pDoc, pNames->GetObjNum());
452 RetainPtr<CPDF_Dictionary> pCategory = pNames->GetMutableDictFor(category);
454 pCategory = pDoc->NewIndirect<CPDF_Dictionary>();
455 pCategory->SetNewFor<CPDF_Array>(
"Names");
456 pNames->SetNewFor<CPDF_Reference>(category, pDoc, pCategory->GetObjNum());
464 CPDF_Dictionary* pRoot) {
465 return pdfium::WrapUnique(
472 const ByteString& name) {
474 std::unique_ptr<CPDF_NameTree> name_tree = Create(pDoc,
"Dests");
476 dest_array = name_tree->LookupNewStyleNamedDest(name);
478 dest_array = LookupOldStyleNamedDest(pDoc, name);
483 std::set<
const CPDF_Dictionary*> seen;
484 return CountNamesInternal(m_pRoot.Get(), 0, seen);
488 const WideString& name) {
493 RetainPtr<CPDF_Array> pNames = m_pRoot->GetMutableArrayFor(
"Names");
494 if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor(
"Kids"))
499 if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex))
508 absl::optional<IndexSearchResult> result =
509 SearchNameNodeByIndex(m_pRoot.Get(), 0);
510 if (!result.has_value()) {
515 pFind = result.value().container;
521 size_t nNameIndex = (nFindIndex + 1) * 2;
522 size_t nValueIndex = nNameIndex + 1;
523 pFind->InsertNewAt<CPDF_String>(nNameIndex, name.AsStringView());
524 pFind->InsertAt(nValueIndex,
std::move(pObj));
528 std::vector<CPDF_Array*> all_limits =
529 GetNodeAncestorsLimits(m_pRoot, pFind.Get());
530 for (
auto* pLimits : all_limits) {
534 if (name.Compare(pLimits->GetUnicodeTextAt(0)) < 0)
535 pLimits->SetNewAt<CPDF_String>(0, name.AsStringView());
537 if (name.Compare(pLimits->GetUnicodeTextAt(1)) > 0)
538 pLimits->SetNewAt<CPDF_String>(1, name.AsStringView());
544 absl::optional<IndexSearchResult> result =
545 SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
552 RetainPtr<CPDF_Array> pFind = result.value().container;
553 pFind->RemoveAt(result.value().index + 1);
554 pFind->RemoveAt(result.value().index);
557 UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind.Get(),
558 result.value().key, 0);
564 WideString* csName)
const {
565 absl::optional<IndexSearchResult> result =
566 SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
572 *csName = std::move(result.value().key);
573 return result.value().value;
577 const WideString& csName)
const {
578 return SearchNameNodeByName(m_pRoot, csName,
nullptr,
nullptr);
582 const ByteString& sName) {
583 return GetNamedDestFromObject(LookupValue(PDF_DecodeText(sName.raw_span())));
CPDF_ArrayLocker(const CPDF_Array *pArray)
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