Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_document.cpp
Go to the documentation of this file.
1// Copyright 2014 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/fpdfapi/parser/cpdf_document.h"
8
9#include <algorithm>
10#include <functional>
11#include <utility>
12
13#include "core/fpdfapi/parser/cpdf_array.h"
14#include "core/fpdfapi/parser/cpdf_dictionary.h"
15#include "core/fpdfapi/parser/cpdf_linearized_header.h"
16#include "core/fpdfapi/parser/cpdf_name.h"
17#include "core/fpdfapi/parser/cpdf_number.h"
18#include "core/fpdfapi/parser/cpdf_parser.h"
19#include "core/fpdfapi/parser/cpdf_read_validator.h"
20#include "core/fpdfapi/parser/cpdf_reference.h"
21#include "core/fpdfapi/parser/cpdf_stream.h"
22#include "core/fpdfapi/parser/cpdf_stream_acc.h"
23#include "core/fpdfapi/parser/fpdf_parser_utility.h"
24#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
25#include "core/fxcrt/fx_codepage.h"
26#include "core/fxcrt/scoped_set_insertion.h"
27#include "core/fxcrt/stl_util.h"
28#include "third_party/abseil-cpp/absl/types/optional.h"
29#include "third_party/base/check.h"
30#include "third_party/base/check_op.h"
31#include "third_party/base/containers/contains.h"
32#include "third_party/base/containers/span.h"
33
34namespace {
35
36const int kMaxPageLevel = 1024;
37
38enum class NodeType : bool {
39 kBranch, // /Type /Pages, AKA page tree node.
40 kLeaf, // /Type /Page, AKA page object.
41};
42
43// Note that this function may modify `kid_dict` to correct PDF spec violations.
44// Same reasoning as CountPages() below.
45NodeType GetNodeType(RetainPtr<CPDF_Dictionary> kid_dict) {
46 const ByteString kid_type_value = kid_dict->GetNameFor("Type");
47 if (kid_type_value == "Pages") {
48 return NodeType::kBranch;
49 }
50 if (kid_type_value == "Page") {
51 return NodeType::kLeaf;
52 }
53
54 // Even though /Type is required for page tree nodes and page objects, PDFs
55 // may not have them or have the wrong type. Tolerate these errors and guess
56 // the type. Then fix the in-memory representation.
57 const bool has_kids = kid_dict->KeyExist("Kids");
58 kid_dict->SetNewFor<CPDF_Name>("Type", has_kids ? "Pages" : "Page");
59 return has_kids ? NodeType::kBranch : NodeType::kLeaf;
60}
61
62// Returns a value in the range [0, `CPDF_Document::kPageMaxNum`), or nullopt on
63// error. Note that this function may modify `pages_dict` to correct PDF spec
64// violations. By normalizing the in-memory representation, other code that
65// reads the object do not have to deal with the same spec violations again.
66// If the PDF gets saved, the saved copy will also be more spec-compliant.
67absl::optional<int> CountPages(
68 RetainPtr<CPDF_Dictionary> pages_dict,
69 std::set<RetainPtr<CPDF_Dictionary>>* visited_pages) {
70 // Required. See ISO 32000-1:2008 spec, table 29, but tolerate page tree nodes
71 // that violate the spec.
72 int count_from_dict = pages_dict->GetIntegerFor("Count");
73 if (count_from_dict > 0 && count_from_dict < CPDF_Document::kPageMaxNum) {
74 return count_from_dict;
75 }
76
77 RetainPtr<CPDF_Array> kids_array = pages_dict->GetMutableArrayFor("Kids");
78 if (!kids_array) {
79 return 0;
80 }
81
82 int count = 0;
83 for (size_t i = 0; i < kids_array->size(); i++) {
84 RetainPtr<CPDF_Dictionary> kid_dict = kids_array->GetMutableDictAt(i);
85 if (!kid_dict || pdfium::Contains(*visited_pages, kid_dict)) {
86 continue;
87 }
88
89 NodeType kid_type = GetNodeType(kid_dict);
90 if (kid_type == NodeType::kBranch) {
91 // Use |visited_pages| to help detect circular references of pages.
92 ScopedSetInsertion<RetainPtr<CPDF_Dictionary>> local_add(visited_pages,
93 kid_dict);
94 absl::optional<int> local_count =
95 CountPages(std::move(kid_dict), visited_pages);
96 if (!local_count.has_value()) {
97 return absl::nullopt; // Propagate error.
98 }
99 count += local_count.value();
100 } else {
101 CHECK_EQ(kid_type, NodeType::kLeaf);
102 count++;
103 }
104
105 if (count >= CPDF_Document::kPageMaxNum) {
106 return absl::nullopt; // Error: too many pages.
107 }
108 }
109 // Fix the in-memory representation for page tree nodes that violate the spec.
110 pages_dict->SetNewFor<CPDF_Number>("Count", count);
111 return count;
112}
113
114int FindPageIndex(const CPDF_Dictionary* pNode,
115 uint32_t* skip_count,
116 uint32_t objnum,
117 int* index,
118 int level) {
119 if (!pNode->KeyExist("Kids")) {
120 if (objnum == pNode->GetObjNum())
121 return *index;
122
123 if (*skip_count != 0)
124 (*skip_count)--;
125
126 (*index)++;
127 return -1;
128 }
129
130 RetainPtr<const CPDF_Array> pKidList = pNode->GetArrayFor("Kids");
131 if (!pKidList)
132 return -1;
133
134 if (level >= kMaxPageLevel)
135 return -1;
136
137 size_t count = pNode->GetIntegerFor("Count");
138 if (count <= *skip_count) {
139 (*skip_count) -= count;
140 (*index) += count;
141 return -1;
142 }
143
144 if (count && count == pKidList->size()) {
145 for (size_t i = 0; i < count; i++) {
146 RetainPtr<const CPDF_Reference> pKid =
147 ToReference(pKidList->GetObjectAt(i));
148 if (pKid && pKid->GetRefObjNum() == objnum)
149 return static_cast<int>(*index + i);
150 }
151 }
152
153 for (size_t i = 0; i < pKidList->size(); i++) {
154 RetainPtr<const CPDF_Dictionary> pKid = pKidList->GetDictAt(i);
155 if (!pKid || pKid == pNode)
156 continue;
157
158 int found_index =
159 FindPageIndex(pKid.Get(), skip_count, objnum, index, level + 1);
160 if (found_index >= 0)
161 return found_index;
162 }
163 return -1;
164}
165
166} // namespace
167
168CPDF_Document::CPDF_Document(std::unique_ptr<RenderDataIface> pRenderData,
169 std::unique_ptr<PageDataIface> pPageData)
173 m_pDocRender->SetDocument(this);
174 m_pDocPage->SetDocument(this);
175}
176
178 // Be absolutely certain that |m_pExtension| is null before destroying
179 // the extension, to avoid re-entering it while being destroyed. clang
180 // seems to already do this for us, but the C++ standards seem to
181 // indicate the opposite.
182 m_pExtension.reset();
183}
184
185// static
187 // See ISO 32000-1:2008 spec, table 30.
188 return ValidateDictType(ToDictionary(obj), "Page");
189}
190
192 return m_pParser ? m_pParser->ParseIndirectObject(objnum) : nullptr;
193}
194
196 SetLastObjNum(m_pParser->GetLastObjNum());
197
198 RetainPtr<CPDF_Object> pRootObj =
199 GetOrParseIndirectObject(m_pParser->GetRootObjNum());
200 if (pRootObj)
201 m_pRootDict = pRootObj->GetMutableDict();
202
204 return GetRoot() && GetPageCount() > 0;
205}
206
209 const ByteString& password) {
210 if (!m_pParser)
211 SetParser(std::make_unique<CPDF_Parser>(this));
212
213 return HandleLoadResult(
214 m_pParser->StartParse(std::move(pFileAccess), password));
215}
216
218 RetainPtr<CPDF_ReadValidator> validator,
219 const ByteString& password) {
220 if (!m_pParser)
221 SetParser(std::make_unique<CPDF_Parser>(this));
222
223 return HandleLoadResult(
224 m_pParser->StartLinearizedParse(std::move(validator), password));
225}
226
228 const CPDF_LinearizedHeader* linearized_header =
229 m_pParser->GetLinearizedHeader();
230 if (!linearized_header) {
231 m_PageList.resize(RetrievePageCount());
232 return;
233 }
234
235 uint32_t objnum = linearized_header->GetFirstPageObjNum();
237 m_PageList.resize(RetrievePageCount());
238 return;
239 }
240
241 uint32_t first_page_num = linearized_header->GetFirstPageNo();
242 uint32_t page_count = linearized_header->GetPageCount();
243 DCHECK(first_page_num < page_count);
244 m_PageList.resize(page_count);
245 m_PageList[first_page_num] = objnum;
246}
247
248RetainPtr<CPDF_Dictionary> CPDF_Document::TraversePDFPages(int iPage,
249 int* nPagesToGo,
250 size_t level) {
251 if (*nPagesToGo < 0 || m_bReachedMaxPageLevel)
252 return nullptr;
253
254 RetainPtr<CPDF_Dictionary> pPages = m_pTreeTraversal[level].first;
255 RetainPtr<CPDF_Array> pKidList = pPages->GetMutableArrayFor("Kids");
256 if (!pKidList) {
257 m_pTreeTraversal.pop_back();
258 if (*nPagesToGo != 1)
259 return nullptr;
260 m_PageList[iPage] = pPages->GetObjNum();
261 return pPages;
262 }
263 if (level >= kMaxPageLevel) {
264 m_pTreeTraversal.pop_back();
265 m_bReachedMaxPageLevel = true;
266 return nullptr;
267 }
268 RetainPtr<CPDF_Dictionary> page;
269 for (size_t i = m_pTreeTraversal[level].second; i < pKidList->size(); i++) {
270 if (*nPagesToGo == 0)
271 break;
272 pKidList->ConvertToIndirectObjectAt(i, this);
273 RetainPtr<CPDF_Dictionary> pKid = pKidList->GetMutableDictAt(i);
274 if (!pKid) {
275 (*nPagesToGo)--;
276 m_pTreeTraversal[level].second++;
277 continue;
278 }
279 if (pKid == pPages) {
280 m_pTreeTraversal[level].second++;
281 continue;
282 }
283 if (!pKid->KeyExist("Kids")) {
284 m_PageList[iPage - (*nPagesToGo) + 1] = pKid->GetObjNum();
285 (*nPagesToGo)--;
286 m_pTreeTraversal[level].second++;
287 if (*nPagesToGo == 0) {
288 page = std::move(pKid);
289 break;
290 }
291 } else {
292 // If the vector has size level+1, the child is not in yet
293 if (m_pTreeTraversal.size() == level + 1)
294 m_pTreeTraversal.emplace_back(std::move(pKid), 0);
295 // Now m_pTreeTraversal[level+1] should exist and be equal to pKid.
296 RetainPtr<CPDF_Dictionary> pPageKid =
297 TraversePDFPages(iPage, nPagesToGo, level + 1);
298 // Check if child was completely processed, i.e. it popped itself out
299 if (m_pTreeTraversal.size() == level + 1)
300 m_pTreeTraversal[level].second++;
301 // If child did not finish, no pages to go, or max level reached, end
302 if (m_pTreeTraversal.size() != level + 1 || *nPagesToGo == 0 ||
303 m_bReachedMaxPageLevel) {
304 page = std::move(pPageKid);
305 break;
306 }
307 }
308 }
309 if (m_pTreeTraversal[level].second == pKidList->size())
310 m_pTreeTraversal.pop_back();
311 return page;
312}
313
314void CPDF_Document::ResetTraversal() {
315 m_iNextPageToTraverse = 0;
316 m_bReachedMaxPageLevel = false;
317 m_pTreeTraversal.clear();
318}
319
320void CPDF_Document::SetParser(std::unique_ptr<CPDF_Parser> pParser) {
321 DCHECK(!m_pParser);
322 m_pParser = std::move(pParser);
323}
324
325CPDF_Parser::Error CPDF_Document::HandleLoadResult(CPDF_Parser::Error error) {
326 if (error == CPDF_Parser::SUCCESS)
327 m_bHasValidCrossReferenceTable = !m_pParser->xref_table_rebuilt();
328 return error;
329}
330
331RetainPtr<const CPDF_Dictionary> CPDF_Document::GetPagesDict() const {
332 const CPDF_Dictionary* pRoot = GetRoot();
333 return pRoot ? pRoot->GetDictFor("Pages") : nullptr;
334}
335
336RetainPtr<CPDF_Dictionary> CPDF_Document::GetMutablePagesDict() {
337 return pdfium::WrapRetain(
338 const_cast<CPDF_Dictionary*>(this->GetPagesDict().Get()));
339}
340
341bool CPDF_Document::IsPageLoaded(int iPage) const {
342 return !!m_PageList[iPage];
343}
344
345RetainPtr<const CPDF_Dictionary> CPDF_Document::GetPageDictionary(int iPage) {
346 if (!fxcrt::IndexInBounds(m_PageList, iPage))
347 return nullptr;
348
349 const uint32_t objnum = m_PageList[iPage];
350 if (objnum) {
351 RetainPtr<CPDF_Dictionary> result =
352 ToDictionary(GetOrParseIndirectObject(objnum));
353 if (result)
354 return result;
355 }
356
357 RetainPtr<CPDF_Dictionary> pPages = GetMutablePagesDict();
358 if (!pPages)
359 return nullptr;
360
361 if (m_pTreeTraversal.empty()) {
362 ResetTraversal();
363 m_pTreeTraversal.emplace_back(std::move(pPages), 0);
364 }
365 int nPagesToGo = iPage - m_iNextPageToTraverse + 1;
366 RetainPtr<CPDF_Dictionary> pPage = TraversePDFPages(iPage, &nPagesToGo, 0);
367 m_iNextPageToTraverse = iPage + 1;
368 return pPage;
369}
370
372 return pdfium::WrapRetain(
373 const_cast<CPDF_Dictionary*>(GetPageDictionary(iPage).Get()));
374}
375
376void CPDF_Document::SetPageObjNum(int iPage, uint32_t objNum) {
377 m_PageList[iPage] = objNum;
378}
379
381 if (!m_pCodecContext)
382 m_pCodecContext = std::make_unique<JBig2_DocumentContext>();
383 return m_pCodecContext.get();
384}
385
387 auto stream = NewIndirect<CPDF_Stream>();
388 m_ModifiedAPStreamIDs.insert(stream->GetObjNum());
389 return stream;
390}
391
392bool CPDF_Document::IsModifiedAPStream(const CPDF_Stream* stream) const {
393 return stream && pdfium::Contains(m_ModifiedAPStreamIDs, stream->GetObjNum());
394}
395
396int CPDF_Document::GetPageIndex(uint32_t objnum) {
397 uint32_t skip_count = 0;
398 bool bSkipped = false;
399 for (uint32_t i = 0; i < m_PageList.size(); ++i) {
400 if (m_PageList[i] == objnum)
401 return i;
402
403 if (!bSkipped && m_PageList[i] == 0) {
404 skip_count = i;
405 bSkipped = true;
406 }
407 }
408 RetainPtr<const CPDF_Dictionary> pPages = GetPagesDict();
409 if (!pPages)
410 return -1;
411
412 int start_index = 0;
413 int found_index = FindPageIndex(pPages, &skip_count, objnum, &start_index, 0);
414
415 // Corrupt page tree may yield out-of-range results.
416 if (!fxcrt::IndexInBounds(m_PageList, found_index))
417 return -1;
418
419 // Only update |m_PageList| when |objnum| points to a /Page object.
420 if (IsValidPageObject(GetOrParseIndirectObject(objnum).Get()))
421 m_PageList[found_index] = objnum;
422 return found_index;
423}
424
426 return fxcrt::CollectionSize<int>(m_PageList);
427}
428
429int CPDF_Document::RetrievePageCount() {
430 RetainPtr<CPDF_Dictionary> pPages = GetMutablePagesDict();
431 if (!pPages)
432 return 0;
433
434 if (!pPages->KeyExist("Kids"))
435 return 1;
436
437 std::set<RetainPtr<CPDF_Dictionary>> visited_pages = {pPages};
438 return CountPages(std::move(pPages), &visited_pages).value_or(0);
439}
440
441uint32_t CPDF_Document::GetUserPermissions(bool get_owner_perms) const {
442 return m_pParser ? m_pParser->GetPermissions(get_owner_perms) : 0;
443}
444
446 RetainPtr<const CPDF_Stream> pFontStream) {
447 return m_pDocPage->GetFontFileStreamAcc(std::move(pFontStream));
448}
449
451 RetainPtr<CPDF_StreamAcc>&& pStreamAcc) {
452 if (m_pDocPage)
453 m_pDocPage->MaybePurgeFontFileStreamAcc(std::move(pStreamAcc));
454}
455
456void CPDF_Document::MaybePurgeImage(uint32_t objnum) {
457 if (m_pDocPage)
458 m_pDocPage->MaybePurgeImage(objnum);
459}
460
462 DCHECK(!m_pRootDict);
463 DCHECK(!m_pInfoDict);
464 m_pRootDict = NewIndirect<CPDF_Dictionary>();
465 m_pRootDict->SetNewFor<CPDF_Name>("Type", "Catalog");
466
467 auto pPages = NewIndirect<CPDF_Dictionary>();
468 pPages->SetNewFor<CPDF_Name>("Type", "Pages");
469 pPages->SetNewFor<CPDF_Number>("Count", 0);
470 pPages->SetNewFor<CPDF_Array>("Kids");
471 m_pRootDict->SetNewFor<CPDF_Reference>("Pages", this, pPages->GetObjNum());
472 m_pInfoDict = NewIndirect<CPDF_Dictionary>();
473}
474
475RetainPtr<CPDF_Dictionary> CPDF_Document::CreateNewPage(int iPage) {
476 auto pDict = NewIndirect<CPDF_Dictionary>();
477 pDict->SetNewFor<CPDF_Name>("Type", "Page");
478 uint32_t dwObjNum = pDict->GetObjNum();
479 if (!InsertNewPage(iPage, pDict)) {
480 DeleteIndirectObject(dwObjNum);
481 return nullptr;
482 }
483 return pDict;
484}
485
486bool CPDF_Document::InsertDeletePDFPage(
487 RetainPtr<CPDF_Dictionary> pages_dict,
488 int pages_to_go,
489 RetainPtr<CPDF_Dictionary> page_dict,
490 bool is_insert,
491 std::set<RetainPtr<CPDF_Dictionary>>* visited) {
492 RetainPtr<CPDF_Array> kids_list = pages_dict->GetMutableArrayFor("Kids");
493 if (!kids_list) {
494 return false;
495 }
496
497 for (size_t i = 0; i < kids_list->size(); i++) {
498 RetainPtr<CPDF_Dictionary> kid_dict = kids_list->GetMutableDictAt(i);
499 NodeType kid_type = GetNodeType(kid_dict);
500 if (kid_type == NodeType::kLeaf) {
501 if (pages_to_go != 0) {
502 pages_to_go--;
503 continue;
504 }
505 if (is_insert) {
506 kids_list->InsertNewAt<CPDF_Reference>(i, this, page_dict->GetObjNum());
507 page_dict->SetNewFor<CPDF_Reference>("Parent", this,
508 pages_dict->GetObjNum());
509 } else {
510 kids_list->RemoveAt(i);
511 }
512 pages_dict->SetNewFor<CPDF_Number>(
513 "Count", pages_dict->GetIntegerFor("Count") + (is_insert ? 1 : -1));
514 ResetTraversal();
515 break;
516 }
517
518 CHECK_EQ(kid_type, NodeType::kBranch);
519 int page_count = kid_dict->GetIntegerFor("Count");
520 if (pages_to_go >= page_count) {
521 pages_to_go -= page_count;
522 continue;
523 }
524 if (pdfium::Contains(*visited, kid_dict)) {
525 return false;
526 }
527
528 ScopedSetInsertion<RetainPtr<CPDF_Dictionary>> insertion(visited, kid_dict);
529 if (!InsertDeletePDFPage(std::move(kid_dict), pages_to_go, page_dict,
530 is_insert, visited)) {
531 return false;
532 }
533 pages_dict->SetNewFor<CPDF_Number>(
534 "Count", pages_dict->GetIntegerFor("Count") + (is_insert ? 1 : -1));
535 break;
536 }
537 return true;
538}
539
540bool CPDF_Document::InsertNewPage(int iPage,
541 RetainPtr<CPDF_Dictionary> pPageDict) {
542 RetainPtr<CPDF_Dictionary> pRoot = GetMutableRoot();
543 if (!pRoot)
544 return false;
545
546 RetainPtr<CPDF_Dictionary> pPages = pRoot->GetMutableDictFor("Pages");
547 if (!pPages)
548 return false;
549
550 int nPages = GetPageCount();
551 if (iPage < 0 || iPage > nPages)
552 return false;
553
554 if (iPage == nPages) {
555 RetainPtr<CPDF_Array> pPagesList = pPages->GetOrCreateArrayFor("Kids");
556 pPagesList->AppendNew<CPDF_Reference>(this, pPageDict->GetObjNum());
557 pPages->SetNewFor<CPDF_Number>("Count", nPages + 1);
558 pPageDict->SetNewFor<CPDF_Reference>("Parent", this, pPages->GetObjNum());
559 ResetTraversal();
560 } else {
561 std::set<RetainPtr<CPDF_Dictionary>> stack = {pPages};
562 if (!InsertDeletePDFPage(std::move(pPages), iPage, pPageDict, true, &stack))
563 return false;
564 }
565 m_PageList.insert(m_PageList.begin() + iPage, pPageDict->GetObjNum());
566 return true;
567}
568
569RetainPtr<CPDF_Dictionary> CPDF_Document::GetInfo() {
570 if (m_pInfoDict)
571 return m_pInfoDict;
572
573 if (!m_pParser)
574 return nullptr;
575
576 uint32_t info_obj_num = m_pParser->GetInfoObjNum();
577 if (info_obj_num == 0)
578 return nullptr;
579
580 auto ref = pdfium::MakeRetain<CPDF_Reference>(this, info_obj_num);
581 m_pInfoDict = ToDictionary(ref->GetMutableDirect());
582 return m_pInfoDict;
583}
584
585RetainPtr<const CPDF_Array> CPDF_Document::GetFileIdentifier() const {
586 return m_pParser ? m_pParser->GetIDArray() : nullptr;
587}
588
589void CPDF_Document::DeletePage(int iPage) {
590 RetainPtr<CPDF_Dictionary> pPages = GetMutablePagesDict();
591 if (!pPages)
592 return;
593
594 int nPages = pPages->GetIntegerFor("Count");
595 if (iPage < 0 || iPage >= nPages)
596 return;
597
598 std::set<RetainPtr<CPDF_Dictionary>> stack = {pPages};
599 if (!InsertDeletePDFPage(std::move(pPages), iPage, nullptr, false, &stack))
600 return;
601
602 m_PageList.erase(m_PageList.begin() + iPage);
603}
604
605void CPDF_Document::SetRootForTesting(RetainPtr<CPDF_Dictionary> root) {
606 m_pRootDict = std::move(root);
607}
608
609bool CPDF_Document::MovePages(pdfium::span<const int> page_indices,
610 int dest_page_index) {
611 const CPDF_Dictionary* pages = GetPagesDict();
612 const int num_pages_signed = pages ? pages->GetIntegerFor("Count") : 0;
613 if (num_pages_signed <= 0) {
614 return false;
615 }
616 const size_t num_pages = num_pages_signed;
617
618 // Check the number of pages is in range.
619 if (page_indices.empty() || page_indices.size() > num_pages) {
620 return false;
621 }
622
623 // Check that destination page index is in range.
624 if (dest_page_index < 0 ||
625 static_cast<size_t>(dest_page_index) > num_pages - page_indices.size()) {
626 return false;
627 }
628
629 // Check for if XFA is enabled.
630 Extension* extension = GetExtension();
631 if (extension && extension->ContainsExtensionForm()) {
632 // Don't manipulate XFA PDFs.
633 return false;
634 }
635
636 // Check for duplicate and out-of-range page indices
637 std::set<int> unique_page_indices;
638 // Store the pages that need to be moved. They'll be deleted then reinserted.
639 std::vector<RetainPtr<CPDF_Dictionary>> pages_to_move;
640 pages_to_move.reserve(page_indices.size());
641 // Store the page indices that will be deleted (and moved).
642 std::vector<int> page_indices_to_delete;
643 page_indices_to_delete.reserve(page_indices.size());
644 for (const int page_index : page_indices) {
645 bool inserted = unique_page_indices.insert(page_index).second;
646 if (!inserted) {
647 // Duplicate page index found
648 return false;
649 }
650 RetainPtr<CPDF_Dictionary> page = GetMutablePageDictionary(page_index);
651 if (!page) {
652 // Page not found, index might be out of range.
653 return false;
654 }
655 pages_to_move.push_back(std::move(page));
656 page_indices_to_delete.push_back(page_index);
657 }
658
659 // Sort the page indices to be deleted in descending order.
660 std::sort(page_indices_to_delete.begin(), page_indices_to_delete.end(),
661 std::greater<int>());
662 // Delete the pages in descending order.
663 if (extension) {
664 for (int page_index : page_indices_to_delete) {
665 extension->DeletePage(page_index);
666 }
667 } else {
668 for (int page_index : page_indices_to_delete) {
669 DeletePage(page_index);
670 }
671 }
672
673 // Insert the deleted pages back into the document at the destination page
674 // index.
675 for (size_t i = 0; i < pages_to_move.size(); ++i) {
676 if (!InsertNewPage(i + dest_page_index, pages_to_move[i])) {
677 // Fail in an indeterminate state.
678 return false;
679 }
680 }
681
682 return true;
683}
684
686 m_PageList.resize(size);
687}
688
689CPDF_Document::StockFontClearer::StockFontClearer(
690 CPDF_Document::PageDataIface* pPageData)
691 : m_pPageData(pPageData) {}
692
693CPDF_Document::StockFontClearer::~StockFontClearer() {
694 m_pPageData->ClearStockFont();
695}
696
698
700
702
bool KeyExist(const ByteString &key) const
int GetIntegerFor(const ByteString &key) const
RetainPtr< const CPDF_Dictionary > GetDictFor(const ByteString &key) const
virtual bool ContainsExtensionForm() const =0
~CPDF_Document() override
JBig2_DocumentContext * GetOrCreateCodecContext()
CPDF_Parser::Error LoadLinearizedDoc(RetainPtr< CPDF_ReadValidator > validator, const ByteString &password)
RetainPtr< CPDF_Object > ParseIndirectObject(uint32_t objnum) override
bool IsPageLoaded(int iPage) const
RetainPtr< const CPDF_Dictionary > GetPageDictionary(int iPage)
int GetPageCount() const
RetainPtr< CPDF_Dictionary > CreateNewPage(int iPage)
void DeletePage(int iPage)
bool IsModifiedAPStream(const CPDF_Stream *stream) const
RetainPtr< const CPDF_Array > GetFileIdentifier() const
void MaybePurgeImage(uint32_t objnum)
void MaybePurgeFontFileStreamAcc(RetainPtr< CPDF_StreamAcc > &&pStreamAcc)
RetainPtr< CPDF_Dictionary > GetInfo()
CPDF_Document(std::unique_ptr< RenderDataIface > pRenderData, std::unique_ptr< PageDataIface > pPageData)
CPDF_Parser::Error LoadDoc(RetainPtr< IFX_SeekableReadStream > pFileAccess, const ByteString &password)
void ResizePageListForTesting(size_t size)
RetainPtr< CPDF_Dictionary > GetMutablePageDictionary(int iPage)
bool MovePages(pdfium::span< const int > page_indices, int dest_page_index)
int GetPageIndex(uint32_t objnum)
Extension * GetExtension() const
uint32_t GetUserPermissions(bool get_owner_perms) const
RetainPtr< CPDF_StreamAcc > GetFontFileStreamAcc(RetainPtr< const CPDF_Stream > pFontStream)
void SetRootForTesting(RetainPtr< CPDF_Dictionary > root)
bool TryInit() override
static bool IsValidPageObject(const CPDF_Object *obj)
const CPDF_Dictionary * GetRoot() const
void SetParser(std::unique_ptr< CPDF_Parser > pParser)
static constexpr int kPageMaxNum
void SetPageObjNum(int iPage, uint32_t objNum)
RetainPtr< CPDF_Stream > CreateModifiedAPStream()
RetainPtr< CPDF_Object > GetOrParseIndirectObject(uint32_t objnum)
uint32_t GetFirstPageObjNum() const
uint32_t GetObjNum() const
Definition cpdf_object.h:65
bool operator==(const char *ptr) const
const CPDF_Dictionary * ToDictionary(const CPDF_Object *obj)