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_pagecontentmanager.cpp
Go to the documentation of this file.
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/edit/cpdf_pagecontentmanager.h"
6
7#include <stdint.h>
8
9#include <map>
10#include <numeric>
11#include <sstream>
12#include <utility>
13#include <vector>
14
15#include "core/fpdfapi/page/cpdf_pageobject.h"
16#include "core/fpdfapi/page/cpdf_pageobjectholder.h"
17#include "core/fpdfapi/parser/cpdf_array.h"
18#include "core/fpdfapi/parser/cpdf_dictionary.h"
19#include "core/fpdfapi/parser/cpdf_document.h"
20#include "core/fpdfapi/parser/cpdf_reference.h"
21#include "core/fpdfapi/parser/cpdf_stream.h"
22#include "core/fpdfapi/parser/object_tree_traversal_util.h"
23#include "core/fxcrt/check.h"
24#include "core/fxcrt/containers/adapters.h"
25#include "core/fxcrt/containers/contains.h"
26#include "core/fxcrt/numerics/safe_conversions.h"
27#include "third_party/abseil-cpp/absl/types/variant.h"
28
30 CPDF_PageObjectHolder* page_obj_holder,
31 CPDF_Document* document)
35 RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
36 RetainPtr<CPDF_Object> contents_obj =
37 page_dict->GetMutableObjectFor("Contents");
38 RetainPtr<CPDF_Array> contents_array = ToArray(contents_obj);
39 if (contents_array) {
40 CHECK(contents_array->IsInline());
41 contents_ = std::move(contents_array);
42 return;
43 }
44
45 RetainPtr<CPDF_Reference> contents_reference = ToReference(contents_obj);
46 if (contents_reference) {
47 RetainPtr<CPDF_Object> indirect_obj =
48 contents_reference->GetMutableDirect();
49 if (!indirect_obj)
50 return;
51
52 contents_array.Reset(indirect_obj->AsMutableArray());
53 if (contents_array) {
54 if (pdfium::Contains(objects_with_multi_refs_,
55 contents_array->GetObjNum())) {
56 RetainPtr<CPDF_Array> cloned_contents_array =
57 pdfium::WrapRetain(contents_array->Clone()->AsMutableArray());
58 page_dict->SetFor("Contents", cloned_contents_array);
59 contents_ = std::move(cloned_contents_array);
60 } else {
61 contents_ = std::move(contents_array);
62 }
63 } else if (indirect_obj->IsStream()) {
64 contents_ = pdfium::WrapRetain(indirect_obj->AsMutableStream());
65 }
66 }
67}
68
70 ExecuteScheduledRemovals();
71}
72
73RetainPtr<CPDF_Stream> CPDF_PageContentManager::GetStreamByIndex(
74 size_t stream_index) {
75 RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
76 if (contents_stream) {
77 return stream_index == 0 ? contents_stream : nullptr;
78 }
79
80 RetainPtr<CPDF_Array> contents_array = GetContentsArray();
81 if (!contents_array) {
82 return nullptr;
83 }
84
85 RetainPtr<CPDF_Reference> stream_reference =
86 ToReference(contents_array->GetMutableObjectAt(stream_index));
87 if (!stream_reference)
88 return nullptr;
89
90 return ToStream(stream_reference->GetMutableDirect());
91}
92
93size_t CPDF_PageContentManager::AddStream(fxcrt::ostringstream* buf) {
94 auto new_stream = document_->NewIndirect<CPDF_Stream>(buf);
95
96 // If there is one Content stream (not in an array), now there will be two, so
97 // create an array with the old and the new one. The new one's index is 1.
98 RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
99 if (contents_stream) {
100 auto new_contents_array = document_->NewIndirect<CPDF_Array>();
101 new_contents_array->AppendNew<CPDF_Reference>(document_,
102 contents_stream->GetObjNum());
103 new_contents_array->AppendNew<CPDF_Reference>(document_,
104 new_stream->GetObjNum());
105
106 RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
107 page_dict->SetNewFor<CPDF_Reference>("Contents", document_,
108 new_contents_array->GetObjNum());
109 contents_ = std::move(new_contents_array);
110 return 1;
111 }
112
113 // If there is an array, just add the new stream to it, at the last position.
114 RetainPtr<CPDF_Array> contents_array = GetContentsArray();
115 if (contents_array) {
116 contents_array->AppendNew<CPDF_Reference>(document_,
117 new_stream->GetObjNum());
118 return contents_array->size() - 1;
119 }
120
121 // There were no Contents, so add the new stream as the single Content stream.
122 // Its index is 0.
123 RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
124 page_dict->SetNewFor<CPDF_Reference>("Contents", document_,
125 new_stream->GetObjNum());
126 contents_ = std::move(new_stream);
127 return 0;
128}
129
130void CPDF_PageContentManager::UpdateStream(size_t stream_index,
131 fxcrt::ostringstream* buf) {
132 // If `buf` is now empty, remove the stream instead of setting the data.
133 if (buf->tellp() <= 0) {
134 ScheduleRemoveStreamByIndex(stream_index);
135 return;
136 }
137
138 RetainPtr<CPDF_Stream> existing_stream = GetStreamByIndex(stream_index);
139 CHECK(existing_stream);
140 if (!pdfium::Contains(objects_with_multi_refs_,
141 existing_stream->GetObjNum())) {
142 existing_stream->SetDataFromStringstreamAndRemoveFilter(buf);
143 return;
144 }
145
146 if (GetContentsStream()) {
147 auto new_stream = document_->NewIndirect<CPDF_Stream>(buf);
148 RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
149 page_dict->SetNewFor<CPDF_Reference>("Contents", document_,
150 new_stream->GetObjNum());
151 }
152
153 RetainPtr<CPDF_Array> contents_array = GetContentsArray();
154 if (!contents_array) {
155 return;
156 }
157
158 RetainPtr<CPDF_Reference> stream_reference =
159 ToReference(contents_array->GetMutableObjectAt(stream_index));
160 if (!stream_reference) {
161 return;
162 }
163
164 auto new_stream = document_->NewIndirect<CPDF_Stream>(buf);
165 stream_reference->SetRef(document_, new_stream->GetObjNum());
166}
167
168void CPDF_PageContentManager::ScheduleRemoveStreamByIndex(size_t stream_index) {
169 streams_to_remove_.insert(stream_index);
170}
171
172void CPDF_PageContentManager::ExecuteScheduledRemovals() {
173 // This method assumes there are no dirty streams in the
174 // CPDF_PageObjectHolder. If there were any, their indexes would need to be
175 // updated.
176 // Since CPDF_PageContentManager is only instantiated in
177 // CPDF_PageContentGenerator::GenerateContent(), which cleans up the dirty
178 // streams first, this should always be true.
179 DCHECK(!page_obj_holder_->HasDirtyStreams());
180
181 if (streams_to_remove_.empty()) {
182 return;
183 }
184
185 RetainPtr<CPDF_Stream> contents_stream = GetContentsStream();
186 if (contents_stream) {
187 // Only stream that can be removed is 0.
188 if (streams_to_remove_.find(0) != streams_to_remove_.end()) {
189 RetainPtr<CPDF_Dictionary> page_dict = page_obj_holder_->GetMutableDict();
190 page_dict->RemoveFor("Contents");
191 }
192 return;
193 }
194
195 RetainPtr<CPDF_Array> contents_array = GetContentsArray();
196 if (!contents_array) {
197 return;
198 }
199
200 // Initialize a vector with the old stream indexes. This will be used to build
201 // a map from the old to the new indexes.
202 std::vector<size_t> streams_left(contents_array->size());
203 std::iota(streams_left.begin(), streams_left.end(), 0);
204
205 // In reverse order so as to not change the indexes in the middle of the loop,
206 // remove the streams.
207 for (size_t stream_index : pdfium::Reversed(streams_to_remove_)) {
208 contents_array->RemoveAt(stream_index);
209 streams_left.erase(streams_left.begin() + stream_index);
210 }
211
212 // Create a mapping from the old to the new stream indexes, shifted due to the
213 // deletion of the |streams_to_remove_|.
214 std::map<size_t, size_t> stream_index_mapping;
215 for (size_t i = 0; i < streams_left.size(); ++i) {
216 stream_index_mapping[streams_left[i]] = i;
217 }
218
219 // Update the page objects' content stream indexes.
220 for (const auto& obj : *page_obj_holder_) {
221 int32_t old_stream_index = obj->GetContentStream();
222 int32_t new_stream_index =
223 pdfium::checked_cast<int32_t>(stream_index_mapping[old_stream_index]);
224 obj->SetContentStream(new_stream_index);
225 }
226
227 // Even if there is a single content stream now, keep the array with a single
228 // element. It's valid, a second stream might be added in the near future, and
229 // the complexity of removing it is not worth it.
230}
231
232RetainPtr<CPDF_Stream> CPDF_PageContentManager::GetContentsStream() {
233 if (absl::holds_alternative<RetainPtr<CPDF_Stream>>(contents_)) {
234 return absl::get<RetainPtr<CPDF_Stream>>(contents_);
235 }
236 return nullptr;
237}
238
239RetainPtr<CPDF_Array> CPDF_PageContentManager::GetContentsArray() {
240 if (absl::holds_alternative<RetainPtr<CPDF_Array>>(contents_)) {
241 return absl::get<RetainPtr<CPDF_Array>>(contents_);
242 }
243 return nullptr;
244}
#define DCHECK
Definition check.h:33
std::vector< RetainPtr< CPDF_Object > >::const_iterator const_iterator
Definition cpdf_array.h:29
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
void UpdateStream(size_t stream_index, fxcrt::ostringstream *buf)
size_t AddStream(fxcrt::ostringstream *buf)
CPDF_PageContentManager(CPDF_PageObjectHolder *page_obj_holder, CPDF_Document *document)
#define CHECK(cvref)