1 // Copyright 2018 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 #include "core/fpdfapi/parser/cpdf_cross_ref_table.h"
6
7 #include <utility>
8
9 #include "core/fpdfapi/parser/cpdf_dictionary.h"
10 #include "core/fpdfapi/parser/cpdf_parser.h"
11 #include "third_party/base/containers/contains.h"
12 #include "third_party/base/notreached.h"
13
14 // static
MergeUp(std::unique_ptr<CPDF_CrossRefTable> current,std::unique_ptr<CPDF_CrossRefTable> top)15 std::unique_ptr<CPDF_CrossRefTable> CPDF_CrossRefTable::MergeUp(
16 std::unique_ptr<CPDF_CrossRefTable> current,
17 std::unique_ptr<CPDF_CrossRefTable> top) {
18 if (!current)
19 return top;
20
21 if (!top)
22 return current;
23
24 current->Update(std::move(top));
25 return current;
26 }
27
28 CPDF_CrossRefTable::CPDF_CrossRefTable() = default;
29
CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer,uint32_t trailer_object_number)30 CPDF_CrossRefTable::CPDF_CrossRefTable(RetainPtr<CPDF_Dictionary> trailer,
31 uint32_t trailer_object_number)
32 : trailer_(std::move(trailer)),
33 trailer_object_number_(trailer_object_number) {}
34
35 CPDF_CrossRefTable::~CPDF_CrossRefTable() = default;
36
AddCompressed(uint32_t obj_num,uint32_t archive_obj_num,uint32_t archive_obj_index)37 void CPDF_CrossRefTable::AddCompressed(uint32_t obj_num,
38 uint32_t archive_obj_num,
39 uint32_t archive_obj_index) {
40 if (obj_num >= CPDF_Parser::kMaxObjectNumber ||
41 archive_obj_num >= CPDF_Parser::kMaxObjectNumber) {
42 NOTREACHED();
43 return;
44 }
45
46 auto& info = objects_info_[obj_num];
47 if (info.gennum > 0)
48 return;
49
50 if (info.type == ObjectType::kObjStream)
51 return;
52
53 info.type = ObjectType::kCompressed;
54 info.archive.obj_num = archive_obj_num;
55 info.archive.obj_index = archive_obj_index;
56 info.gennum = 0;
57
58 objects_info_[archive_obj_num].type = ObjectType::kObjStream;
59 }
60
AddNormal(uint32_t obj_num,uint16_t gen_num,FX_FILESIZE pos)61 void CPDF_CrossRefTable::AddNormal(uint32_t obj_num,
62 uint16_t gen_num,
63 FX_FILESIZE pos) {
64 if (obj_num >= CPDF_Parser::kMaxObjectNumber) {
65 NOTREACHED();
66 return;
67 }
68
69 auto& info = objects_info_[obj_num];
70 if (info.gennum > gen_num)
71 return;
72
73 if (info.type == ObjectType::kCompressed && gen_num == 0)
74 return;
75
76 if (info.type != ObjectType::kObjStream)
77 info.type = ObjectType::kNormal;
78
79 info.gennum = gen_num;
80 info.pos = pos;
81 }
82
SetFree(uint32_t obj_num)83 void CPDF_CrossRefTable::SetFree(uint32_t obj_num) {
84 if (obj_num >= CPDF_Parser::kMaxObjectNumber) {
85 NOTREACHED();
86 return;
87 }
88
89 auto& info = objects_info_[obj_num];
90 info.type = ObjectType::kFree;
91 info.gennum = 0xFFFF;
92 info.pos = 0;
93 }
94
SetTrailer(RetainPtr<CPDF_Dictionary> trailer,uint32_t trailer_object_number)95 void CPDF_CrossRefTable::SetTrailer(RetainPtr<CPDF_Dictionary> trailer,
96 uint32_t trailer_object_number) {
97 trailer_ = std::move(trailer);
98 trailer_object_number_ = trailer_object_number;
99 }
100
GetObjectInfo(uint32_t obj_num) const101 const CPDF_CrossRefTable::ObjectInfo* CPDF_CrossRefTable::GetObjectInfo(
102 uint32_t obj_num) const {
103 const auto it = objects_info_.find(obj_num);
104 return it != objects_info_.end() ? &it->second : nullptr;
105 }
106
Update(std::unique_ptr<CPDF_CrossRefTable> new_cross_ref)107 void CPDF_CrossRefTable::Update(
108 std::unique_ptr<CPDF_CrossRefTable> new_cross_ref) {
109 UpdateInfo(std::move(new_cross_ref->objects_info_));
110 UpdateTrailer(std::move(new_cross_ref->trailer_));
111 }
112
SetObjectMapSize(uint32_t size)113 void CPDF_CrossRefTable::SetObjectMapSize(uint32_t size) {
114 if (size == 0) {
115 objects_info_.clear();
116 return;
117 }
118
119 objects_info_.erase(objects_info_.lower_bound(size), objects_info_.end());
120
121 if (!pdfium::Contains(objects_info_, size - 1)) {
122 objects_info_[size - 1].pos = 0;
123 }
124 }
125
UpdateInfo(std::map<uint32_t,ObjectInfo> new_objects_info)126 void CPDF_CrossRefTable::UpdateInfo(
127 std::map<uint32_t, ObjectInfo> new_objects_info) {
128 if (new_objects_info.empty()) {
129 return;
130 }
131
132 if (objects_info_.empty()) {
133 objects_info_ = std::move(new_objects_info);
134 return;
135 }
136
137 auto cur_it = objects_info_.begin();
138 auto new_it = new_objects_info.begin();
139 while (cur_it != objects_info_.end() && new_it != new_objects_info.end()) {
140 if (cur_it->first == new_it->first) {
141 if (cur_it->second.type == ObjectType::kObjStream &&
142 new_it->second.type == ObjectType::kNormal) {
143 new_it->second.type = ObjectType::kObjStream;
144 }
145 ++cur_it;
146 ++new_it;
147 } else if (cur_it->first < new_it->first) {
148 new_objects_info.insert(new_it, *cur_it);
149 ++cur_it;
150 } else {
151 new_it = new_objects_info.lower_bound(cur_it->first);
152 }
153 }
154 for (; cur_it != objects_info_.end(); ++cur_it) {
155 new_objects_info.insert(new_objects_info.end(), *cur_it);
156 }
157 objects_info_ = std::move(new_objects_info);
158 }
159
UpdateTrailer(RetainPtr<CPDF_Dictionary> new_trailer)160 void CPDF_CrossRefTable::UpdateTrailer(RetainPtr<CPDF_Dictionary> new_trailer) {
161 if (!new_trailer)
162 return;
163
164 if (!trailer_) {
165 trailer_ = std::move(new_trailer);
166 return;
167 }
168
169 new_trailer->SetFor("XRefStm", trailer_->RemoveFor("XRefStm"));
170 new_trailer->SetFor("Prev", trailer_->RemoveFor("Prev"));
171
172 for (const auto& key : new_trailer->GetKeys())
173 trailer_->SetFor(key, new_trailer->RemoveFor(key.AsStringView()));
174 }
175