• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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