• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "core/fpdfdoc/cpdf_nametree.h"
8 
9 #include <set>
10 #include <utility>
11 #include <vector>
12 
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/ptr_util.h"
22 
23 namespace {
24 
25 constexpr int kNameTreeMaxRecursion = 32;
26 
GetNodeLimitsAndSanitize(CPDF_Array * pLimits)27 std::pair<WideString, WideString> GetNodeLimitsAndSanitize(
28     CPDF_Array* pLimits) {
29   DCHECK(pLimits);
30   WideString csLeft = pLimits->GetUnicodeTextAt(0);
31   WideString csRight = pLimits->GetUnicodeTextAt(1);
32   // If the lower limit is greater than the upper limit, swap them.
33   if (csLeft.Compare(csRight) > 0) {
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);
38   }
39   while (pLimits->size() > 2)
40     pLimits->RemoveAt(pLimits->size() - 1);
41   return {csLeft, csRight};
42 }
43 
44 // Get the limit arrays that leaf array |pFind| is under in the tree with root
45 // |pNode|. |pLimits| will hold all the limit arrays from the leaf up to before
46 // the root. Return true if successful.
GetNodeAncestorsLimitsInternal(const RetainPtr<CPDF_Dictionary> & pNode,const CPDF_Array * pFind,int nLevel,std::vector<CPDF_Array * > * pLimits)47 bool GetNodeAncestorsLimitsInternal(const RetainPtr<CPDF_Dictionary>& pNode,
48                                     const CPDF_Array* pFind,
49                                     int nLevel,
50                                     std::vector<CPDF_Array*>* pLimits) {
51   if (nLevel > kNameTreeMaxRecursion)
52     return false;
53 
54   if (pNode->GetArrayFor("Names") == pFind) {
55     pLimits->push_back(pNode->GetMutableArrayFor("Limits").Get());
56     return true;
57   }
58 
59   RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
60   if (!pKids)
61     return false;
62 
63   for (size_t i = 0; i < pKids->size(); ++i) {
64     RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
65     if (!pKid)
66       continue;
67 
68     if (GetNodeAncestorsLimitsInternal(pKid, pFind, nLevel + 1, pLimits)) {
69       pLimits->push_back(pNode->GetMutableArrayFor("Limits").Get());
70       return true;
71     }
72   }
73   return false;
74 }
75 
76 // Wrapper for GetNodeAncestorsLimitsInternal() so callers do not need to know
77 // about the details.
GetNodeAncestorsLimits(const RetainPtr<CPDF_Dictionary> & pNode,const CPDF_Array * pFind)78 std::vector<CPDF_Array*> GetNodeAncestorsLimits(
79     const RetainPtr<CPDF_Dictionary>& pNode,
80     const CPDF_Array* pFind) {
81   std::vector<CPDF_Array*> results;
82   GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results);
83   return results;
84 }
85 
86 // Upon the deletion of |csName| from leaf array |pFind|, update the ancestors
87 // of |pFind|. Specifically, the limits of |pFind|'s ancestors will be updated
88 // if needed, and any ancestors that are now empty will be removed.
UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary * pNode,const CPDF_Array * pFind,const WideString & csName,int nLevel)89 bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode,
90                                       const CPDF_Array* pFind,
91                                       const WideString& csName,
92                                       int nLevel) {
93   if (nLevel > kNameTreeMaxRecursion)
94     return false;
95 
96   RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor("Limits");
97   WideString csLeft;
98   WideString csRight;
99   if (pLimits)
100     std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
101 
102   RetainPtr<const CPDF_Array> pNames = pNode->GetArrayFor("Names");
103   if (pNames) {
104     if (pNames != pFind)
105       return false;
106     if (pNames->IsEmpty() || !pLimits)
107       return true;
108     if (csLeft != csName && csRight != csName)
109       return true;
110 
111     // Since |csName| defines |pNode|'s limits, we need to loop through the
112     // names to find the new lower and upper limits.
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);
117       if (wsName.Compare(csNewLeft) < 0)
118         csNewLeft = wsName;
119       if (wsName.Compare(csNewRight) > 0)
120         csNewRight = wsName;
121     }
122     pLimits->SetNewAt<CPDF_String>(0, csNewLeft.AsStringView());
123     pLimits->SetNewAt<CPDF_String>(1, csNewRight.AsStringView());
124     return true;
125   }
126 
127   RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
128   if (!pKids)
129     return false;
130 
131   // Loop through the kids to find the leaf array |pFind|.
132   for (size_t i = 0; i < pKids->size(); ++i) {
133     RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
134     if (!pKid)
135       continue;
136     if (!UpdateNodesAndLimitsUponDeletion(pKid.Get(), pFind, csName,
137                                           nLevel + 1)) {
138       continue;
139     }
140     // Remove this child node if it's empty.
141     if ((pKid->KeyExist("Names") && pKid->GetArrayFor("Names")->IsEmpty()) ||
142         (pKid->KeyExist("Kids") && pKid->GetArrayFor("Kids")->IsEmpty())) {
143       pKids->RemoveAt(i);
144     }
145     if (pKids->IsEmpty() || !pLimits)
146       return true;
147     if (csLeft != csName && csRight != csName)
148       return true;
149 
150     // Since |csName| defines |pNode|'s limits, we need to loop through the
151     // kids to find the new lower and upper limits.
152     WideString csNewLeft = csRight;
153     WideString csNewRight = csLeft;
154     for (size_t j = 0; j < pKids->size(); ++j) {
155       RetainPtr<const CPDF_Array> pKidLimits =
156           pKids->GetDictAt(j)->GetArrayFor("Limits");
157       DCHECK(pKidLimits);
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);
162     }
163     pLimits->SetNewAt<CPDF_String>(0, csNewLeft.AsStringView());
164     pLimits->SetNewAt<CPDF_String>(1, csNewRight.AsStringView());
165     return true;
166   }
167   return false;
168 }
169 
IsTraversedObject(const CPDF_Object * obj,std::set<uint32_t> * seen_obj_nums)170 bool IsTraversedObject(const CPDF_Object* obj,
171                        std::set<uint32_t>* seen_obj_nums) {
172   uint32_t obj_num = obj->GetObjNum();
173   if (!obj_num)
174     return false;
175 
176   bool inserted = seen_obj_nums->insert(obj_num).second;
177   return !inserted;
178 }
179 
IsArrayWithTraversedObject(const CPDF_Array * array,std::set<uint32_t> * seen_obj_nums)180 bool IsArrayWithTraversedObject(const CPDF_Array* array,
181                                 std::set<uint32_t>* seen_obj_nums) {
182   if (IsTraversedObject(array, seen_obj_nums))
183     return true;
184 
185   CPDF_ArrayLocker locker(array);
186   for (const auto& item : locker) {
187     if (IsTraversedObject(item.Get(), seen_obj_nums))
188       return true;
189   }
190   return false;
191 }
192 
193 // Search for |csName| in the tree with root |pNode|. If successful, return the
194 // value that |csName| points to; |nIndex| will be the index of |csName|,
195 // |ppFind| will be the leaf array that |csName| is found in, and |pFindIndex|
196 // will be the index of |csName| in |ppFind|. If |csName| is not found, |ppFind|
197 // will be the leaf array that |csName| should be added to, and |pFindIndex|
198 // will be the index that it should be added at.
SearchNameNodeByNameInternal(const RetainPtr<CPDF_Dictionary> & pNode,const WideString & csName,int nLevel,size_t * nIndex,RetainPtr<CPDF_Array> * ppFind,int * pFindIndex,std::set<uint32_t> * seen_obj_nums)199 RetainPtr<const CPDF_Object> SearchNameNodeByNameInternal(
200     const RetainPtr<CPDF_Dictionary>& pNode,
201     const WideString& csName,
202     int nLevel,
203     size_t* nIndex,
204     RetainPtr<CPDF_Array>* ppFind,
205     int* pFindIndex,
206     std::set<uint32_t>* seen_obj_nums) {
207   if (nLevel > kNameTreeMaxRecursion)
208     return nullptr;
209 
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))
213     pNames.Reset();
214   if (pLimits && IsArrayWithTraversedObject(pLimits.Get(), seen_obj_nums))
215     pLimits.Reset();
216 
217   if (pLimits) {
218     WideString csLeft;
219     WideString csRight;
220     std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
221     // Skip this node if the name to look for is smaller than its lower limit.
222     if (csName.Compare(csLeft) < 0)
223       return nullptr;
224 
225     // Skip this node if the name to look for is greater than its higher limit,
226     // and the node itself is a leaf node.
227     if (csName.Compare(csRight) > 0 && pNames) {
228       if (ppFind)
229         *ppFind = pNames;
230       if (pFindIndex)
231         *pFindIndex = fxcrt::CollectionSize<int32_t>(*pNames) / 2 - 1;
232       return nullptr;
233     }
234   }
235 
236   // If the node is a leaf node, look for the name in its names array.
237   if (pNames) {
238     size_t dwCount = pNames->size() / 2;
239     for (size_t i = 0; i < dwCount; i++) {
240       WideString csValue = pNames->GetUnicodeTextAt(i * 2);
241       int32_t iCompare = csValue.Compare(csName);
242       if (iCompare > 0)
243         break;
244       if (ppFind)
245         *ppFind = pNames;
246       if (pFindIndex)
247         *pFindIndex = pdfium::base::checked_cast<int32_t>(i);
248       if (iCompare < 0)
249         continue;
250 
251       *nIndex += i;
252       return pNames->GetDirectObjectAt(i * 2 + 1);
253     }
254     *nIndex += dwCount;
255     return nullptr;
256   }
257 
258   // Search through the node's children.
259   RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
260   if (!pKids || IsTraversedObject(pKids.Get(), seen_obj_nums))
261     return nullptr;
262 
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))
266       continue;
267 
268     RetainPtr<const CPDF_Object> pFound = SearchNameNodeByNameInternal(
269         pKid, csName, nLevel + 1, nIndex, ppFind, pFindIndex, seen_obj_nums);
270     if (pFound)
271       return pFound;
272   }
273   return nullptr;
274 }
275 
276 // Wrapper for SearchNameNodeByNameInternal() so callers do not need to know
277 // about the details.
SearchNameNodeByName(const RetainPtr<CPDF_Dictionary> & pNode,const WideString & csName,RetainPtr<CPDF_Array> * ppFind,int * pFindIndex)278 RetainPtr<const CPDF_Object> SearchNameNodeByName(
279     const RetainPtr<CPDF_Dictionary>& pNode,
280     const WideString& csName,
281     RetainPtr<CPDF_Array>* ppFind,
282     int* pFindIndex) {
283   size_t nIndex = 0;
284   std::set<uint32_t> seen_obj_nums;
285   return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind,
286                                       pFindIndex, &seen_obj_nums);
287 }
288 
289 struct IndexSearchResult {
290   // For the n-th object in a tree, the key and value.
291   WideString key;
292   RetainPtr<CPDF_Object> value;
293   // The leaf node that holds `key` and `value`.
294   RetainPtr<CPDF_Array> container;
295   // The index for `key` in `container`. Must be even.
296   size_t index;
297 };
298 
299 // Find the `nTargetPairIndex` node in the tree with root `pNode`. `nLevel`
300 // tracks the recursion level and `nCurPairIndex` tracks the progress towards
301 // `nTargetPairIndex`.
SearchNameNodeByIndexInternal(CPDF_Dictionary * pNode,size_t nTargetPairIndex,int nLevel,size_t * nCurPairIndex)302 absl::optional<IndexSearchResult> SearchNameNodeByIndexInternal(
303     CPDF_Dictionary* pNode,
304     size_t nTargetPairIndex,
305     int nLevel,
306     size_t* nCurPairIndex) {
307   if (nLevel > kNameTreeMaxRecursion)
308     return absl::nullopt;
309 
310   RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor("Names");
311   if (pNames) {
312     size_t nCount = pNames->size() / 2;
313     if (nTargetPairIndex >= *nCurPairIndex + nCount) {
314       *nCurPairIndex += nCount;
315       return absl::nullopt;
316     }
317 
318     size_t index = 2 * (nTargetPairIndex - *nCurPairIndex);
319     RetainPtr<CPDF_Object> value = pNames->GetMutableDirectObjectAt(index + 1);
320     if (!value)
321       return absl::nullopt;
322 
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;
328     return result;
329   }
330 
331   RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
332   if (!pKids)
333     return absl::nullopt;
334 
335   for (size_t i = 0; i < pKids->size(); i++) {
336     RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
337     if (!pKid)
338       continue;
339     absl::optional<IndexSearchResult> result = SearchNameNodeByIndexInternal(
340         pKid.Get(), nTargetPairIndex, nLevel + 1, nCurPairIndex);
341     if (result.has_value())
342       return result;
343   }
344   return absl::nullopt;
345 }
346 
347 // Wrapper for SearchNameNodeByIndexInternal() so callers do not need to know
348 // about the details.
SearchNameNodeByIndex(CPDF_Dictionary * pNode,size_t nTargetPairIndex)349 absl::optional<IndexSearchResult> SearchNameNodeByIndex(
350     CPDF_Dictionary* pNode,
351     size_t nTargetPairIndex) {
352   size_t nCurPairIndex = 0;
353   return SearchNameNodeByIndexInternal(pNode, nTargetPairIndex, 0,
354                                        &nCurPairIndex);
355 }
356 
357 // Get the total number of key-value pairs in the tree with root |pNode|.
CountNamesInternal(const CPDF_Dictionary * pNode,int nLevel,std::set<const CPDF_Dictionary * > & seen)358 size_t CountNamesInternal(const CPDF_Dictionary* pNode,
359                           int nLevel,
360                           std::set<const CPDF_Dictionary*>& seen) {
361   if (nLevel > kNameTreeMaxRecursion)
362     return 0;
363 
364   const bool inserted = seen.insert(pNode).second;
365   if (!inserted)
366     return 0;
367 
368   RetainPtr<const CPDF_Array> pNames = pNode->GetArrayFor("Names");
369   if (pNames)
370     return pNames->size() / 2;
371 
372   RetainPtr<const CPDF_Array> pKids = pNode->GetArrayFor("Kids");
373   if (!pKids)
374     return 0;
375 
376   size_t nCount = 0;
377   for (size_t i = 0; i < pKids->size(); i++) {
378     RetainPtr<const CPDF_Dictionary> pKid = pKids->GetDictAt(i);
379     if (!pKid)
380       continue;
381 
382     nCount += CountNamesInternal(pKid.Get(), nLevel + 1, seen);
383   }
384   return nCount;
385 }
386 
GetNamedDestFromObject(RetainPtr<const CPDF_Object> obj)387 RetainPtr<const CPDF_Array> GetNamedDestFromObject(
388     RetainPtr<const CPDF_Object> obj) {
389   RetainPtr<const CPDF_Array> array = ToArray(obj);
390   if (array)
391     return array;
392   RetainPtr<const CPDF_Dictionary> dict = ToDictionary(obj);
393   if (dict)
394     return dict->GetArrayFor("D");
395   return nullptr;
396 }
397 
LookupOldStyleNamedDest(CPDF_Document * pDoc,const ByteString & name)398 RetainPtr<const CPDF_Array> LookupOldStyleNamedDest(CPDF_Document* pDoc,
399                                                     const ByteString& name) {
400   RetainPtr<const CPDF_Dictionary> pDests =
401       pDoc->GetRoot()->GetDictFor("Dests");
402   if (!pDests)
403     return nullptr;
404   return GetNamedDestFromObject(pDests->GetDirectObjectFor(name));
405 }
406 
407 }  // namespace
408 
CPDF_NameTree(RetainPtr<CPDF_Dictionary> pRoot)409 CPDF_NameTree::CPDF_NameTree(RetainPtr<CPDF_Dictionary> pRoot)
410     : m_pRoot(std::move(pRoot)) {
411   DCHECK(m_pRoot);
412 }
413 
414 CPDF_NameTree::~CPDF_NameTree() = default;
415 
416 // static
Create(CPDF_Document * pDoc,const ByteString & category)417 std::unique_ptr<CPDF_NameTree> CPDF_NameTree::Create(
418     CPDF_Document* pDoc,
419     const ByteString& category) {
420   RetainPtr<CPDF_Dictionary> pRoot = pDoc->GetMutableRoot();
421   if (!pRoot)
422     return nullptr;
423 
424   RetainPtr<CPDF_Dictionary> pNames = pRoot->GetMutableDictFor("Names");
425   if (!pNames)
426     return nullptr;
427 
428   RetainPtr<CPDF_Dictionary> pCategory = pNames->GetMutableDictFor(category);
429   if (!pCategory)
430     return nullptr;
431 
432   return pdfium::WrapUnique(
433       new CPDF_NameTree(std::move(pCategory)));  // Private ctor.
434 }
435 
436 // static
CreateWithRootNameArray(CPDF_Document * pDoc,const ByteString & category)437 std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateWithRootNameArray(
438     CPDF_Document* pDoc,
439     const ByteString& category) {
440   RetainPtr<CPDF_Dictionary> pRoot = pDoc->GetMutableRoot();
441   if (!pRoot)
442     return nullptr;
443 
444   // Retrieve the document's Names dictionary; create it if missing.
445   RetainPtr<CPDF_Dictionary> pNames = pRoot->GetMutableDictFor("Names");
446   if (!pNames) {
447     pNames = pDoc->NewIndirect<CPDF_Dictionary>();
448     pRoot->SetNewFor<CPDF_Reference>("Names", pDoc, pNames->GetObjNum());
449   }
450 
451   // Create the |category| dictionary if missing.
452   RetainPtr<CPDF_Dictionary> pCategory = pNames->GetMutableDictFor(category);
453   if (!pCategory) {
454     pCategory = pDoc->NewIndirect<CPDF_Dictionary>();
455     pCategory->SetNewFor<CPDF_Array>("Names");
456     pNames->SetNewFor<CPDF_Reference>(category, pDoc, pCategory->GetObjNum());
457   }
458 
459   return pdfium::WrapUnique(new CPDF_NameTree(pCategory));  // Private ctor.
460 }
461 
462 // static
CreateForTesting(CPDF_Dictionary * pRoot)463 std::unique_ptr<CPDF_NameTree> CPDF_NameTree::CreateForTesting(
464     CPDF_Dictionary* pRoot) {
465   return pdfium::WrapUnique(
466       new CPDF_NameTree(pdfium::WrapRetain(pRoot)));  // Private ctor.
467 }
468 
469 // static
LookupNamedDest(CPDF_Document * pDoc,const ByteString & name)470 RetainPtr<const CPDF_Array> CPDF_NameTree::LookupNamedDest(
471     CPDF_Document* pDoc,
472     const ByteString& name) {
473   RetainPtr<const CPDF_Array> dest_array;
474   std::unique_ptr<CPDF_NameTree> name_tree = Create(pDoc, "Dests");
475   if (name_tree)
476     dest_array = name_tree->LookupNewStyleNamedDest(name);
477   if (!dest_array)
478     dest_array = LookupOldStyleNamedDest(pDoc, name);
479   return dest_array;
480 }
481 
GetCount() const482 size_t CPDF_NameTree::GetCount() const {
483   std::set<const CPDF_Dictionary*> seen;
484   return CountNamesInternal(m_pRoot.Get(), 0, seen);
485 }
486 
AddValueAndName(RetainPtr<CPDF_Object> pObj,const WideString & name)487 bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
488                                     const WideString& name) {
489   RetainPtr<CPDF_Array> pFind;
490   int nFindIndex = -1;
491   // Handle the corner case where the root node is empty. i.e. No kids and no
492   // names. In which case, just insert into it and skip all the searches.
493   RetainPtr<CPDF_Array> pNames = m_pRoot->GetMutableArrayFor("Names");
494   if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor("Kids"))
495     pFind = pNames;
496 
497   if (!pFind) {
498     // Fail if the tree already contains this name or if the tree is too deep.
499     if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex))
500       return false;
501   }
502 
503   // If the returned |pFind| is a nullptr, then |name| is smaller than all
504   // existing entries in the tree, and we did not find a leaf array to place
505   // |name| into. We instead will find the leftmost leaf array in which to place
506   // |name| and |pObj|.
507   if (!pFind) {
508     absl::optional<IndexSearchResult> result =
509         SearchNameNodeByIndex(m_pRoot.Get(), 0);
510     if (!result.has_value()) {
511       // Give up if that fails too.
512       return false;
513     }
514 
515     pFind = result.value().container;
516     DCHECK(pFind);
517   }
518 
519   // Insert the name and the object into the leaf array found. Note that the
520   // insertion position is right after the key-value pair returned by |index|.
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));
525 
526   // Expand the limits that the newly added name is under, if the name falls
527   // outside of the limits of its leaf array or any arrays above it.
528   std::vector<CPDF_Array*> all_limits =
529       GetNodeAncestorsLimits(m_pRoot, pFind.Get());
530   for (auto* pLimits : all_limits) {
531     if (!pLimits)
532       continue;
533 
534     if (name.Compare(pLimits->GetUnicodeTextAt(0)) < 0)
535       pLimits->SetNewAt<CPDF_String>(0, name.AsStringView());
536 
537     if (name.Compare(pLimits->GetUnicodeTextAt(1)) > 0)
538       pLimits->SetNewAt<CPDF_String>(1, name.AsStringView());
539   }
540   return true;
541 }
542 
DeleteValueAndName(size_t nIndex)543 bool CPDF_NameTree::DeleteValueAndName(size_t nIndex) {
544   absl::optional<IndexSearchResult> result =
545       SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
546   if (!result) {
547     // Fail if the tree does not contain |nIndex|.
548     return false;
549   }
550 
551   // Remove the name and the object from the leaf array |pFind|.
552   RetainPtr<CPDF_Array> pFind = result.value().container;
553   pFind->RemoveAt(result.value().index + 1);
554   pFind->RemoveAt(result.value().index);
555 
556   // Delete empty nodes and update the limits of |pFind|'s ancestors as needed.
557   UpdateNodesAndLimitsUponDeletion(m_pRoot.Get(), pFind.Get(),
558                                    result.value().key, 0);
559   return true;
560 }
561 
LookupValueAndName(size_t nIndex,WideString * csName) const562 RetainPtr<CPDF_Object> CPDF_NameTree::LookupValueAndName(
563     size_t nIndex,
564     WideString* csName) const {
565   absl::optional<IndexSearchResult> result =
566       SearchNameNodeByIndex(m_pRoot.Get(), nIndex);
567   if (!result) {
568     csName->clear();
569     return nullptr;
570   }
571 
572   *csName = std::move(result.value().key);
573   return result.value().value;
574 }
575 
LookupValue(const WideString & csName) const576 RetainPtr<const CPDF_Object> CPDF_NameTree::LookupValue(
577     const WideString& csName) const {
578   return SearchNameNodeByName(m_pRoot, csName, nullptr, nullptr);
579 }
580 
LookupNewStyleNamedDest(const ByteString & sName)581 RetainPtr<const CPDF_Array> CPDF_NameTree::LookupNewStyleNamedDest(
582     const ByteString& sName) {
583   return GetNamedDestFromObject(LookupValue(PDF_DecodeText(sName.raw_span())));
584 }
585