1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "annotation.h"
18
19 #include <utils/pdf_strings.h>
20
21 #include "image_object.h"
22 #include "logging.h"
23 #include "path_object.h"
24
25 #define LOG_TAG "annotation"
26
27 namespace pdfClient {
28
GetObjects() const29 std::vector<PageObject*> StampAnnotation::GetObjects() const {
30 std::vector<PageObject*> page_objects;
31 for (const auto& page_object : pageObjects_) {
32 page_objects.push_back(page_object.get());
33 }
34
35 return page_objects;
36 }
37
updateExistingBounds(FPDF_ANNOTATION fpdf_annot,size_t num_bounds,std::vector<Rectangle_f> bounds)38 bool updateExistingBounds(FPDF_ANNOTATION fpdf_annot, size_t num_bounds,
39 std::vector<Rectangle_f> bounds) {
40 for (auto bound_index = 0; bound_index < num_bounds; bound_index++) {
41 Rectangle_f rect = bounds[bound_index];
42 FS_QUADPOINTSF quad_points = {rect.left, rect.top, rect.right, rect.top,
43 rect.left, rect.bottom, rect.right, rect.bottom};
44 if (!FPDFAnnot_SetAttachmentPoints(fpdf_annot, bound_index, &quad_points)) {
45 LOGD("Failed to update the bounds of highlight annotation");
46 return false;
47 }
48 }
49 return true;
50 }
51
PopulateFromPdfiumInstance(FPDF_ANNOTATION fpdf_annot,FPDF_PAGE page)52 bool StampAnnotation::PopulateFromPdfiumInstance(FPDF_ANNOTATION fpdf_annot, FPDF_PAGE page) {
53 int num_of_objects = FPDFAnnot_GetObjectCount(fpdf_annot);
54
55 for (int object_index = 0; object_index < num_of_objects; object_index++) {
56 FPDF_PAGEOBJECT page_object = FPDFAnnot_GetObject(fpdf_annot, object_index);
57 int objectType = FPDFPageObj_GetType(page_object);
58
59 std::unique_ptr<PageObject> page_object_;
60
61 switch (objectType) {
62 case FPDF_PAGEOBJ_PATH: {
63 page_object_ = std::make_unique<PathObject>();
64 break;
65 }
66 case FPDF_PAGEOBJ_IMAGE: {
67 page_object_ = std::make_unique<ImageObject>();
68 break;
69 }
70 default: {
71 break;
72 }
73 }
74
75 if (page_object_ && !page_object_->PopulateFromFPDFInstance(page_object, page)) {
76 LOGE("Failed to get all the data corresponding to object with index "
77 "%d ",
78 object_index);
79 page_object_ = nullptr;
80 }
81
82 // Add the page_object_ to the stamp annotation even if page_object_ is null
83 // as we are storing empty unique ptr for the unsupported page objects
84 AddObject(std::move(page_object_));
85 }
86 return true;
87 }
88
CreatePdfiumInstance(FPDF_DOCUMENT document,FPDF_PAGE page)89 ScopedFPDFAnnotation StampAnnotation::CreatePdfiumInstance(FPDF_DOCUMENT document, FPDF_PAGE page) {
90 // Create a ScopedFPDFAnnotation, If it will fail to populate this pdfium annot with desired
91 // params, we will return null that will lead to scoped annot getting out of scope and thus
92 // getting destroyed
93 ScopedFPDFAnnotation scoped_annot =
94 ScopedFPDFAnnotation(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
95
96 if (!scoped_annot) {
97 LOGE("Failed to create stamp Annotation.");
98 return nullptr;
99 }
100
101 Rectangle_f annotation_bounds = GetBounds();
102 FS_RECTF rect;
103 rect.left = annotation_bounds.left;
104 rect.bottom = annotation_bounds.bottom;
105 rect.right = annotation_bounds.right;
106 rect.top = annotation_bounds.top;
107
108 if (!FPDFAnnot_SetRect(scoped_annot.get(), &rect)) {
109 LOGE("Stamp Annotation bounds couldn't be set");
110 return nullptr;
111 }
112
113 std::vector<PageObject*> pageObjects = GetObjects();
114 for (auto pageObject : pageObjects) {
115 ScopedFPDFPageObject scoped_page_object = pageObject->CreateFPDFInstance(document, page);
116
117 if (!scoped_page_object) {
118 LOGE("Failed to create page object to add in the stamp annotation");
119 return nullptr;
120 }
121
122 if (!FPDFAnnot_AppendObject(scoped_annot.get(), scoped_page_object.release())) {
123 LOGE("Page object couldn't be inserted in the stamp annotation");
124 return nullptr;
125 }
126 }
127
128 return scoped_annot;
129 }
130
UpdatePdfiumInstance(FPDF_ANNOTATION fpdf_annot,FPDF_DOCUMENT document,FPDF_PAGE page)131 bool StampAnnotation::UpdatePdfiumInstance(FPDF_ANNOTATION fpdf_annot, FPDF_DOCUMENT document,
132 FPDF_PAGE page) {
133 if (FPDFAnnot_GetSubtype(fpdf_annot) != FPDF_ANNOT_STAMP) {
134 LOGE("Unsupported operation - can't update a stamp annotation with some other type of "
135 "annotation");
136 return false;
137 }
138
139 Rectangle_f new_bounds = GetBounds();
140 FS_RECTF rect;
141 rect.left = new_bounds.left;
142 rect.bottom = new_bounds.bottom;
143 rect.right = new_bounds.right;
144 rect.top = new_bounds.top;
145 if (!FPDFAnnot_SetRect(fpdf_annot, &rect)) {
146 LOGE("Failed to update the bounds of the stamp annotation at given index");
147 return false;
148 }
149
150 // First Remove all the known existing objects from the stamp annotation, and then rewrite
151 int num_objects = FPDFAnnot_GetObjectCount(fpdf_annot);
152 for (int object_index = num_objects - 1; object_index >= 0; object_index--) {
153 FPDF_PAGEOBJECT pageObject = FPDFAnnot_GetObject(fpdf_annot, object_index);
154 int object_type = FPDFPageObj_GetType(pageObject);
155 if (pageObject != nullptr &&
156 (object_type == FPDF_PAGEOBJ_IMAGE || object_type == FPDF_PAGEOBJ_PATH)) {
157 if (!FPDFAnnot_RemoveObject(fpdf_annot, object_index)) {
158 LOGE("Failed to remove existing object from stamp annotation");
159 return false;
160 }
161 }
162 }
163
164 // Rewrite
165 std::vector<PageObject*> newPageObjects = GetObjects();
166 for (auto pageObject : newPageObjects) {
167 ScopedFPDFPageObject scoped_page_object = pageObject->CreateFPDFInstance(document, page);
168
169 if (!scoped_page_object) {
170 LOGE("Failed to create new page object to add in the stamp annotation");
171 return false;
172 }
173
174 if (!FPDFAnnot_AppendObject(fpdf_annot, scoped_page_object.release())) {
175 LOGE("Page object couldn't be inserted in the stamp annotation");
176 return false;
177 }
178 }
179 return true;
180 }
181
PopulateFromPdfiumInstance(FPDF_ANNOTATION fpdf_annot,FPDF_PAGE page)182 bool HighlightAnnotation::PopulateFromPdfiumInstance(FPDF_ANNOTATION fpdf_annot, FPDF_PAGE page) {
183 // Get color
184 unsigned int R;
185 unsigned int G;
186 unsigned int B;
187 unsigned int A;
188
189 if (!FPDFAnnot_GetColor(fpdf_annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A)) {
190 LOGE("Couldn't get color of highlight annotation");
191 return false;
192 }
193
194 Color color(R, G, B, A);
195 this->SetColor(color);
196 return true;
197 }
198
CreatePdfiumInstance(FPDF_DOCUMENT document,FPDF_PAGE page)199 ScopedFPDFAnnotation HighlightAnnotation::CreatePdfiumInstance(FPDF_DOCUMENT document,
200 FPDF_PAGE page) {
201 ScopedFPDFAnnotation scoped_annot =
202 ScopedFPDFAnnotation(FPDFPage_CreateAnnot(page, FPDF_ANNOT_HIGHLIGHT));
203
204 if (!scoped_annot) {
205 LOGE("Failed to create highlight Annotation.");
206 return nullptr;
207 }
208
209 if (!this->UpdatePdfiumInstance(scoped_annot.get(), document, page)) {
210 LOGE("Failed to create highlight annotation with given parameters");
211 }
212
213 return scoped_annot;
214 }
215
UpdatePdfiumInstance(FPDF_ANNOTATION fpdf_annot,FPDF_DOCUMENT document,FPDF_PAGE page)216 bool HighlightAnnotation::UpdatePdfiumInstance(FPDF_ANNOTATION fpdf_annot, FPDF_DOCUMENT document,
217 FPDF_PAGE page) {
218 if (FPDFAnnot_GetSubtype(fpdf_annot) != FPDF_ANNOT_HIGHLIGHT) {
219 LOGE("Unsupported operation - can't update a highlight annotation with some other type of "
220 "annotation");
221 return false;
222 }
223
224 auto old_num_bounds = FPDFAnnot_CountAttachmentPoints(fpdf_annot);
225 std::vector<Rectangle_f> bounds = GetBounds();
226 auto new_num_bounds = bounds.size();
227
228 if (old_num_bounds == new_num_bounds) {
229 if (!updateExistingBounds(fpdf_annot, old_num_bounds, bounds)) return false;
230 } else if (old_num_bounds < new_num_bounds) {
231 if (!updateExistingBounds(fpdf_annot, old_num_bounds, bounds)) return false;
232 for (auto bound_index = old_num_bounds; bound_index < new_num_bounds; bound_index++) {
233 Rectangle_f rect = bounds[bound_index];
234 FS_QUADPOINTSF quad_points = {rect.left, rect.top, rect.right, rect.top,
235 rect.left, rect.bottom, rect.right, rect.bottom};
236 if (!FPDFAnnot_AppendAttachmentPoints(fpdf_annot, &quad_points)) {
237 LOGD("Failed to update bounds of the highlight annotation");
238 return false;
239 }
240 }
241 } else {
242 if (!updateExistingBounds(fpdf_annot, new_num_bounds, bounds)) return false;
243 for (auto bound_index = new_num_bounds; bound_index < old_num_bounds; bound_index++) {
244 FS_QUADPOINTSF quad_points = {0, 0, 0, 0, 0, 0, 0, 0};
245 if (!FPDFAnnot_SetAttachmentPoints(fpdf_annot, bound_index, &quad_points)) {
246 LOGD("Failed to update bounds of the highlight annotation");
247 return false;
248 }
249 }
250 }
251
252 Color new_color = this->GetColor();
253 if (!FPDFAnnot_SetColor(fpdf_annot, FPDFANNOT_COLORTYPE_Color, new_color.r, new_color.g,
254 new_color.b, new_color.a)) {
255 LOGE("Highlight Annotation color couldn't be updated");
256 return false;
257 }
258 return true;
259 }
260
GetTextContentFromPdfium(FPDF_ANNOTATION fpdf_annot,unsigned long text_length,std::wstring & text)261 bool FreeTextAnnotation::GetTextContentFromPdfium(FPDF_ANNOTATION fpdf_annot,
262 unsigned long text_length, std::wstring& text) {
263 // Create a buffer of the obtained size to store the text contents.
264 ScopedFPDFWChar text_content_buffer = std::make_unique<FPDF_WCHAR[]>(text_length);
265 if (!FPDFAnnot_GetStringValue(fpdf_annot, kContents, text_content_buffer.get(), text_length)) {
266 return false;
267 }
268
269 text = pdfClient_utils::ToWideString(text_content_buffer.get(), text_length);
270 return true;
271 }
272
PopulateFromPdfiumInstance(FPDF_ANNOTATION fpdf_annot,FPDF_PAGE page)273 bool FreeTextAnnotation::PopulateFromPdfiumInstance(FPDF_ANNOTATION fpdf_annot, FPDF_PAGE page) {
274 // Pass a empty buffer to get the length of the text contents.
275 unsigned long text_length = FPDFAnnot_GetStringValue(fpdf_annot, kContents, nullptr, 0);
276 if (text_length == 0) {
277 LOGE("Failed to get contents of FreeText Annotation");
278 return false;
279 }
280
281 if (!GetTextContentFromPdfium(fpdf_annot, text_length, text_content_)) {
282 LOGE("GetTextContentFromPdfium Failed.");
283 return false;
284 }
285
286 // Get color
287 if (!FPDFAnnot_GetColor(fpdf_annot, FPDFANNOT_COLORTYPE_Color, &text_color_.r, &text_color_.g,
288 &text_color_.b, &text_color_.a)) {
289 LOGE("Couldn't get text color of freetext annotation");
290 return false;
291 }
292
293 if (!FPDFAnnot_GetColor(fpdf_annot, FPDFANNOT_COLORTYPE_InteriorColor, &background_color_.r,
294 &background_color_.g, &background_color_.b, &background_color_.a)) {
295 LOGE("Couldn't get background color of freetext annotation");
296 return false;
297 }
298 return true;
299 }
300
CreatePdfiumInstance(FPDF_DOCUMENT document,FPDF_PAGE page)301 ScopedFPDFAnnotation FreeTextAnnotation::CreatePdfiumInstance(FPDF_DOCUMENT document,
302 FPDF_PAGE page) {
303 ScopedFPDFAnnotation scoped_annot =
304 ScopedFPDFAnnotation(FPDFPage_CreateAnnot(page, FPDF_ANNOT_FREETEXT));
305
306 if (!scoped_annot) {
307 LOGE("Failed to create FreeText Annotation");
308 return nullptr;
309 }
310
311 if (!UpdatePdfiumInstance(scoped_annot.get(), document, page)) {
312 LOGE("Failed to create FreeText Annotation with given parameters");
313 }
314
315 return scoped_annot;
316 }
317
UpdatePdfiumInstance(FPDF_ANNOTATION fpdf_annot,FPDF_DOCUMENT document,FPDF_PAGE page)318 bool FreeTextAnnotation::UpdatePdfiumInstance(FPDF_ANNOTATION fpdf_annot, FPDF_DOCUMENT document,
319 FPDF_PAGE page) {
320 if (FPDFAnnot_GetSubtype(fpdf_annot) != FPDF_ANNOT_FREETEXT) {
321 LOGE("Unsupported operation - can't update a freetext annotation with some other type of "
322 "annotation");
323 return false;
324 }
325
326 Rectangle_f annotation_bounds = GetBounds();
327 if (!FPDFAnnot_SetRect(fpdf_annot, reinterpret_cast<FS_RECTF*>(&annotation_bounds))) {
328 LOGE("FreeText Annotation bounds could not be updated");
329 return false;
330 }
331
332 auto fpdfWideString = pdfClient_utils::ToFPDFWideString(text_content_);
333 if (!FPDFAnnot_SetStringValue(fpdf_annot, kContents, fpdfWideString.get())) {
334 LOGE("FreeText Annotation text content could not be updated");
335 }
336
337 if (!FPDFAnnot_SetColor(fpdf_annot, FPDFANNOT_COLORTYPE_Color, text_color_.r, text_color_.g,
338 text_color_.b, text_color_.a)) {
339 LOGE("FreeText Annotation text color couldn't be updated");
340 return false;
341 }
342
343 if (!FPDFAnnot_SetColor(fpdf_annot, FPDFANNOT_COLORTYPE_InteriorColor, background_color_.r,
344 background_color_.g, background_color_.b, background_color_.a)) {
345 LOGE("FreeText Annotation background color couldn't be updated");
346 return false;
347 }
348
349 return true;
350 }
351
352 } // namespace pdfClient