1 // Copyright 2017 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 "public/fpdf_annot.h"
6
7 #include <array>
8 #include <memory>
9 #include <sstream>
10 #include <utility>
11 #include <vector>
12
13 #include "constants/annotation_common.h"
14 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
15 #include "core/fpdfapi/page/cpdf_annotcontext.h"
16 #include "core/fpdfapi/page/cpdf_form.h"
17 #include "core/fpdfapi/page/cpdf_page.h"
18 #include "core/fpdfapi/page/cpdf_pageobject.h"
19 #include "core/fpdfapi/parser/cpdf_array.h"
20 #include "core/fpdfapi/parser/cpdf_boolean.h"
21 #include "core/fpdfapi/parser/cpdf_dictionary.h"
22 #include "core/fpdfapi/parser/cpdf_document.h"
23 #include "core/fpdfapi/parser/cpdf_name.h"
24 #include "core/fpdfapi/parser/cpdf_number.h"
25 #include "core/fpdfapi/parser/cpdf_reference.h"
26 #include "core/fpdfapi/parser/cpdf_stream.h"
27 #include "core/fpdfapi/parser/cpdf_string.h"
28 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
29 #include "core/fpdfdoc/cpdf_annot.h"
30 #include "core/fpdfdoc/cpdf_color_utils.h"
31 #include "core/fpdfdoc/cpdf_formfield.h"
32 #include "core/fpdfdoc/cpdf_generateap.h"
33 #include "core/fpdfdoc/cpdf_interactiveform.h"
34 #include "core/fxcrt/check.h"
35 #include "core/fxcrt/containers/contains.h"
36 #include "core/fxcrt/fx_safe_types.h"
37 #include "core/fxcrt/fx_string_wrappers.h"
38 #include "core/fxcrt/numerics/safe_conversions.h"
39 #include "core/fxcrt/ptr_util.h"
40 #include "core/fxcrt/stl_util.h"
41 #include "core/fxge/cfx_color.h"
42 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
43 #include "fpdfsdk/cpdfsdk_helpers.h"
44 #include "fpdfsdk/cpdfsdk_interactiveform.h"
45
46 namespace {
47
48 // These checks ensure the consistency of annotation subtype values across core/
49 // and public.
50 static_assert(static_cast<int>(CPDF_Annot::Subtype::UNKNOWN) ==
51 FPDF_ANNOT_UNKNOWN,
52 "CPDF_Annot::UNKNOWN value mismatch");
53 static_assert(static_cast<int>(CPDF_Annot::Subtype::TEXT) == FPDF_ANNOT_TEXT,
54 "CPDF_Annot::TEXT value mismatch");
55 static_assert(static_cast<int>(CPDF_Annot::Subtype::LINK) == FPDF_ANNOT_LINK,
56 "CPDF_Annot::LINK value mismatch");
57 static_assert(static_cast<int>(CPDF_Annot::Subtype::FREETEXT) ==
58 FPDF_ANNOT_FREETEXT,
59 "CPDF_Annot::FREETEXT value mismatch");
60 static_assert(static_cast<int>(CPDF_Annot::Subtype::LINE) == FPDF_ANNOT_LINE,
61 "CPDF_Annot::LINE value mismatch");
62 static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUARE) ==
63 FPDF_ANNOT_SQUARE,
64 "CPDF_Annot::SQUARE value mismatch");
65 static_assert(static_cast<int>(CPDF_Annot::Subtype::CIRCLE) ==
66 FPDF_ANNOT_CIRCLE,
67 "CPDF_Annot::CIRCLE value mismatch");
68 static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYGON) ==
69 FPDF_ANNOT_POLYGON,
70 "CPDF_Annot::POLYGON value mismatch");
71 static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYLINE) ==
72 FPDF_ANNOT_POLYLINE,
73 "CPDF_Annot::POLYLINE value mismatch");
74 static_assert(static_cast<int>(CPDF_Annot::Subtype::HIGHLIGHT) ==
75 FPDF_ANNOT_HIGHLIGHT,
76 "CPDF_Annot::HIGHLIGHT value mismatch");
77 static_assert(static_cast<int>(CPDF_Annot::Subtype::UNDERLINE) ==
78 FPDF_ANNOT_UNDERLINE,
79 "CPDF_Annot::UNDERLINE value mismatch");
80 static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUIGGLY) ==
81 FPDF_ANNOT_SQUIGGLY,
82 "CPDF_Annot::SQUIGGLY value mismatch");
83 static_assert(static_cast<int>(CPDF_Annot::Subtype::STRIKEOUT) ==
84 FPDF_ANNOT_STRIKEOUT,
85 "CPDF_Annot::STRIKEOUT value mismatch");
86 static_assert(static_cast<int>(CPDF_Annot::Subtype::STAMP) == FPDF_ANNOT_STAMP,
87 "CPDF_Annot::STAMP value mismatch");
88 static_assert(static_cast<int>(CPDF_Annot::Subtype::CARET) == FPDF_ANNOT_CARET,
89 "CPDF_Annot::CARET value mismatch");
90 static_assert(static_cast<int>(CPDF_Annot::Subtype::INK) == FPDF_ANNOT_INK,
91 "CPDF_Annot::INK value mismatch");
92 static_assert(static_cast<int>(CPDF_Annot::Subtype::POPUP) == FPDF_ANNOT_POPUP,
93 "CPDF_Annot::POPUP value mismatch");
94 static_assert(static_cast<int>(CPDF_Annot::Subtype::FILEATTACHMENT) ==
95 FPDF_ANNOT_FILEATTACHMENT,
96 "CPDF_Annot::FILEATTACHMENT value mismatch");
97 static_assert(static_cast<int>(CPDF_Annot::Subtype::SOUND) == FPDF_ANNOT_SOUND,
98 "CPDF_Annot::SOUND value mismatch");
99 static_assert(static_cast<int>(CPDF_Annot::Subtype::MOVIE) == FPDF_ANNOT_MOVIE,
100 "CPDF_Annot::MOVIE value mismatch");
101 static_assert(static_cast<int>(CPDF_Annot::Subtype::WIDGET) ==
102 FPDF_ANNOT_WIDGET,
103 "CPDF_Annot::WIDGET value mismatch");
104 static_assert(static_cast<int>(CPDF_Annot::Subtype::SCREEN) ==
105 FPDF_ANNOT_SCREEN,
106 "CPDF_Annot::SCREEN value mismatch");
107 static_assert(static_cast<int>(CPDF_Annot::Subtype::PRINTERMARK) ==
108 FPDF_ANNOT_PRINTERMARK,
109 "CPDF_Annot::PRINTERMARK value mismatch");
110 static_assert(static_cast<int>(CPDF_Annot::Subtype::TRAPNET) ==
111 FPDF_ANNOT_TRAPNET,
112 "CPDF_Annot::TRAPNET value mismatch");
113 static_assert(static_cast<int>(CPDF_Annot::Subtype::WATERMARK) ==
114 FPDF_ANNOT_WATERMARK,
115 "CPDF_Annot::WATERMARK value mismatch");
116 static_assert(static_cast<int>(CPDF_Annot::Subtype::THREED) ==
117 FPDF_ANNOT_THREED,
118 "CPDF_Annot::THREED value mismatch");
119 static_assert(static_cast<int>(CPDF_Annot::Subtype::RICHMEDIA) ==
120 FPDF_ANNOT_RICHMEDIA,
121 "CPDF_Annot::RICHMEDIA value mismatch");
122 static_assert(static_cast<int>(CPDF_Annot::Subtype::XFAWIDGET) ==
123 FPDF_ANNOT_XFAWIDGET,
124 "CPDF_Annot::XFAWIDGET value mismatch");
125 static_assert(static_cast<int>(CPDF_Annot::Subtype::REDACT) ==
126 FPDF_ANNOT_REDACT,
127 "CPDF_Annot::REDACT value mismatch");
128
129 // These checks ensure the consistency of annotation appearance mode values
130 // across core/ and public.
131 static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::kNormal) ==
132 FPDF_ANNOT_APPEARANCEMODE_NORMAL,
133 "CPDF_Annot::AppearanceMode::Normal value mismatch");
134 static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::kRollover) ==
135 FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
136 "CPDF_Annot::AppearanceMode::Rollover value mismatch");
137 static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::kDown) ==
138 FPDF_ANNOT_APPEARANCEMODE_DOWN,
139 "CPDF_Annot::AppearanceMode::Down value mismatch");
140
141 // These checks ensure the consistency of dictionary value types across core/
142 // and public/.
143 static_assert(static_cast<int>(CPDF_Object::Type::kBoolean) ==
144 FPDF_OBJECT_BOOLEAN,
145 "CPDF_Object::kBoolean value mismatch");
146 static_assert(static_cast<int>(CPDF_Object::Type::kNumber) ==
147 FPDF_OBJECT_NUMBER,
148 "CPDF_Object::kNumber value mismatch");
149 static_assert(static_cast<int>(CPDF_Object::Type::kString) ==
150 FPDF_OBJECT_STRING,
151 "CPDF_Object::kString value mismatch");
152 static_assert(static_cast<int>(CPDF_Object::Type::kName) == FPDF_OBJECT_NAME,
153 "CPDF_Object::kName value mismatch");
154 static_assert(static_cast<int>(CPDF_Object::Type::kArray) == FPDF_OBJECT_ARRAY,
155 "CPDF_Object::kArray value mismatch");
156 static_assert(static_cast<int>(CPDF_Object::Type::kDictionary) ==
157 FPDF_OBJECT_DICTIONARY,
158 "CPDF_Object::kDictionary value mismatch");
159 static_assert(static_cast<int>(CPDF_Object::Type::kStream) ==
160 FPDF_OBJECT_STREAM,
161 "CPDF_Object::kStream value mismatch");
162 static_assert(static_cast<int>(CPDF_Object::Type::kNullobj) ==
163 FPDF_OBJECT_NULLOBJ,
164 "CPDF_Object::kNullobj value mismatch");
165 static_assert(static_cast<int>(CPDF_Object::Type::kReference) ==
166 FPDF_OBJECT_REFERENCE,
167 "CPDF_Object::kReference value mismatch");
168
169 // These checks ensure the consistency of annotation additional action event
170 // values across core/ and public.
171 static_assert(static_cast<int>(CPDF_AAction::kKeyStroke) ==
172 FPDF_ANNOT_AACTION_KEY_STROKE,
173 "CPDF_AAction::kKeyStroke value mismatch");
174 static_assert(static_cast<int>(CPDF_AAction::kFormat) ==
175 FPDF_ANNOT_AACTION_FORMAT,
176 "CPDF_AAction::kFormat value mismatch");
177 static_assert(static_cast<int>(CPDF_AAction::kValidate) ==
178 FPDF_ANNOT_AACTION_VALIDATE,
179 "CPDF_AAction::kValidate value mismatch");
180 static_assert(static_cast<int>(CPDF_AAction::kCalculate) ==
181 FPDF_ANNOT_AACTION_CALCULATE,
182 "CPDF_AAction::kCalculate value mismatch");
183
HasAPStream(CPDF_Dictionary * pAnnotDict)184 bool HasAPStream(CPDF_Dictionary* pAnnotDict) {
185 return !!GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::kNormal);
186 }
187
UpdateContentStream(CPDF_Form * pForm,CPDF_Stream * pStream)188 void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) {
189 DCHECK(pForm);
190 DCHECK(pStream);
191
192 CPDF_PageContentGenerator generator(pForm);
193 fxcrt::ostringstream buf;
194 generator.ProcessPageObjects(&buf);
195 pStream->SetDataFromStringstreamAndRemoveFilter(&buf);
196 }
197
SetQuadPointsAtIndex(CPDF_Array * array,size_t quad_index,const FS_QUADPOINTSF * quad_points)198 void SetQuadPointsAtIndex(CPDF_Array* array,
199 size_t quad_index,
200 const FS_QUADPOINTSF* quad_points) {
201 DCHECK(array);
202 DCHECK(quad_points);
203 DCHECK(IsValidQuadPointsIndex(array, quad_index));
204
205 size_t nIndex = quad_index * 8;
206 array->SetNewAt<CPDF_Number>(nIndex, quad_points->x1);
207 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y1);
208 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x2);
209 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y2);
210 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x3);
211 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y3);
212 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x4);
213 array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y4);
214 }
215
AppendQuadPoints(CPDF_Array * array,const FS_QUADPOINTSF * quad_points)216 void AppendQuadPoints(CPDF_Array* array, const FS_QUADPOINTSF* quad_points) {
217 DCHECK(quad_points);
218 DCHECK(array);
219
220 array->AppendNew<CPDF_Number>(quad_points->x1);
221 array->AppendNew<CPDF_Number>(quad_points->y1);
222 array->AppendNew<CPDF_Number>(quad_points->x2);
223 array->AppendNew<CPDF_Number>(quad_points->y2);
224 array->AppendNew<CPDF_Number>(quad_points->x3);
225 array->AppendNew<CPDF_Number>(quad_points->y3);
226 array->AppendNew<CPDF_Number>(quad_points->x4);
227 array->AppendNew<CPDF_Number>(quad_points->y4);
228 }
229
UpdateBBox(CPDF_Dictionary * annot_dict)230 void UpdateBBox(CPDF_Dictionary* annot_dict) {
231 DCHECK(annot_dict);
232 // Update BBox entry in appearance stream based on the bounding rectangle
233 // of the annotation's quadpoints.
234 RetainPtr<CPDF_Stream> pStream =
235 GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::kNormal);
236 if (pStream) {
237 CFX_FloatRect boundingRect =
238 CPDF_Annot::BoundingRectFromQuadPoints(annot_dict);
239 if (boundingRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
240 pStream->GetMutableDict()->SetRectFor("BBox", boundingRect);
241 }
242 }
243
GetAnnotDictFromFPDFAnnotation(const FPDF_ANNOTATION annot)244 const CPDF_Dictionary* GetAnnotDictFromFPDFAnnotation(
245 const FPDF_ANNOTATION annot) {
246 CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot);
247 return context ? context->GetAnnotDict() : nullptr;
248 }
249
GetMutableAnnotDictFromFPDFAnnotation(FPDF_ANNOTATION annot)250 RetainPtr<CPDF_Dictionary> GetMutableAnnotDictFromFPDFAnnotation(
251 FPDF_ANNOTATION annot) {
252 CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot);
253 return context ? context->GetMutableAnnotDict() : nullptr;
254 }
255
SetExtGStateInResourceDict(CPDF_Document * pDoc,const CPDF_Dictionary * pAnnotDict,const ByteString & sBlendMode)256 RetainPtr<CPDF_Dictionary> SetExtGStateInResourceDict(
257 CPDF_Document* pDoc,
258 const CPDF_Dictionary* pAnnotDict,
259 const ByteString& sBlendMode) {
260 auto pGSDict =
261 pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict->GetByteStringPool());
262
263 // ExtGState represents a graphics state parameter dictionary.
264 pGSDict->SetNewFor<CPDF_Name>("Type", "ExtGState");
265
266 // CA respresents current stroking alpha specifying constant opacity
267 // value that should be used in transparent imaging model.
268 float fOpacity = pAnnotDict->GetFloatFor("CA");
269
270 pGSDict->SetNewFor<CPDF_Number>("CA", fOpacity);
271
272 // ca represents fill color alpha specifying constant opacity
273 // value that should be used in transparent imaging model.
274 pGSDict->SetNewFor<CPDF_Number>("ca", fOpacity);
275
276 // AIS represents alpha source flag specifying whether current alpha
277 // constant shall be interpreted as shape value (true) or opacity value
278 // (false).
279 pGSDict->SetNewFor<CPDF_Boolean>("AIS", false);
280
281 // BM represents Blend Mode
282 pGSDict->SetNewFor<CPDF_Name>("BM", sBlendMode);
283
284 auto pExtGStateDict =
285 pdfium::MakeRetain<CPDF_Dictionary>(pAnnotDict->GetByteStringPool());
286
287 pExtGStateDict->SetFor("GS", pGSDict);
288
289 auto pResourceDict = pDoc->New<CPDF_Dictionary>();
290 pResourceDict->SetFor("ExtGState", pExtGStateDict);
291 return pResourceDict;
292 }
293
GetFormField(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)294 CPDF_FormField* GetFormField(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
295 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
296 if (!pAnnotDict)
297 return nullptr;
298
299 CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
300 if (!pForm)
301 return nullptr;
302
303 CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
304 return pPDFForm->GetFieldByDict(pAnnotDict);
305 }
306
307 // If `allowed_types` is empty, then match all types.
GetWidgetOfTypes(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,pdfium::span<const CPDF_FormField::Type> allowed_types)308 const CPDFSDK_Widget* GetWidgetOfTypes(
309 FPDF_FORMHANDLE hHandle,
310 FPDF_ANNOTATION annot,
311 pdfium::span<const CPDF_FormField::Type> allowed_types) {
312 const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
313 if (!annot_dict) {
314 return nullptr;
315 }
316
317 CPDFSDK_InteractiveForm* form = FormHandleToInteractiveForm(hHandle);
318 if (!form) {
319 return nullptr;
320 }
321
322 CPDF_InteractiveForm* pdf_form = form->GetInteractiveForm();
323 CPDF_FormField* form_field = pdf_form->GetFieldByDict(annot_dict);
324 if (!form_field) {
325 return nullptr;
326 }
327
328 if (!allowed_types.empty()) {
329 if (!pdfium::Contains(allowed_types, form_field->GetType())) {
330 return nullptr;
331 }
332 }
333
334 CPDF_FormControl* form_control = pdf_form->GetControlByDict(annot_dict);
335 return form_control ? form->GetWidget(form_control) : nullptr;
336 }
337
GetRadioButtonOrCheckBoxWidget(FPDF_FORMHANDLE handle,FPDF_ANNOTATION annot)338 const CPDFSDK_Widget* GetRadioButtonOrCheckBoxWidget(FPDF_FORMHANDLE handle,
339 FPDF_ANNOTATION annot) {
340 constexpr std::array<CPDF_FormField::Type, 2> kAllowedTypes = {
341 CPDF_FormField::kCheckBox, CPDF_FormField::kRadioButton};
342 return GetWidgetOfTypes(handle, annot, kAllowedTypes);
343 }
344
GetInkList(FPDF_ANNOTATION annot)345 RetainPtr<const CPDF_Array> GetInkList(FPDF_ANNOTATION annot) {
346 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
347 if (subtype != FPDF_ANNOT_INK)
348 return nullptr;
349
350 const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
351 return annot_dict ? annot_dict->GetArrayFor(pdfium::annotation::kInkList)
352 : nullptr;
353 }
354
355 } // namespace
356
357 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype)358 FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
359 // The supported subtypes must also be communicated in the user doc.
360 switch (subtype) {
361 case FPDF_ANNOT_CIRCLE:
362 case FPDF_ANNOT_FILEATTACHMENT:
363 case FPDF_ANNOT_FREETEXT:
364 case FPDF_ANNOT_HIGHLIGHT:
365 case FPDF_ANNOT_INK:
366 case FPDF_ANNOT_LINK:
367 case FPDF_ANNOT_POPUP:
368 case FPDF_ANNOT_SQUARE:
369 case FPDF_ANNOT_SQUIGGLY:
370 case FPDF_ANNOT_STAMP:
371 case FPDF_ANNOT_STRIKEOUT:
372 case FPDF_ANNOT_TEXT:
373 case FPDF_ANNOT_UNDERLINE:
374 return true;
375 default:
376 return false;
377 }
378 }
379
380 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
FPDFPage_CreateAnnot(FPDF_PAGE page,FPDF_ANNOTATION_SUBTYPE subtype)381 FPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype) {
382 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
383 if (!pPage || !FPDFAnnot_IsSupportedSubtype(subtype))
384 return nullptr;
385
386 auto pDict = pPage->GetDocument()->New<CPDF_Dictionary>();
387 pDict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "Annot");
388 pDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype,
389 CPDF_Annot::AnnotSubtypeToString(
390 static_cast<CPDF_Annot::Subtype>(subtype)));
391 auto pNewAnnot =
392 std::make_unique<CPDF_AnnotContext>(pDict, IPDFPageFromFPDFPage(page));
393
394 RetainPtr<CPDF_Array> pAnnotList = pPage->GetOrCreateAnnotsArray();
395 pAnnotList->Append(pDict);
396
397 // Caller takes ownership.
398 return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
399 }
400
FPDFPage_GetAnnotCount(FPDF_PAGE page)401 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page) {
402 const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
403 if (!pPage)
404 return 0;
405
406 RetainPtr<const CPDF_Array> pAnnots = pPage->GetAnnotsArray();
407 return pAnnots ? fxcrt::CollectionSize<int>(*pAnnots) : 0;
408 }
409
FPDFPage_GetAnnot(FPDF_PAGE page,int index)410 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page,
411 int index) {
412 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
413 if (!pPage || index < 0)
414 return nullptr;
415
416 RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
417 if (!pAnnots || static_cast<size_t>(index) >= pAnnots->size())
418 return nullptr;
419
420 RetainPtr<CPDF_Dictionary> pDict =
421 ToDictionary(pAnnots->GetMutableDirectObjectAt(index));
422 if (!pDict)
423 return nullptr;
424
425 auto pNewAnnot = std::make_unique<CPDF_AnnotContext>(
426 std::move(pDict), IPDFPageFromFPDFPage(page));
427
428 // Caller takes ownership.
429 return FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
430 }
431
FPDFPage_GetAnnotIndex(FPDF_PAGE page,FPDF_ANNOTATION annot)432 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page,
433 FPDF_ANNOTATION annot) {
434 const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
435 if (!pPage)
436 return -1;
437
438 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
439 if (!pAnnotDict)
440 return -1;
441
442 RetainPtr<const CPDF_Array> pAnnots = pPage->GetAnnotsArray();
443 if (!pAnnots)
444 return -1;
445
446 CPDF_ArrayLocker locker(pAnnots);
447 auto it = std::find_if(locker.begin(), locker.end(),
448 [pAnnotDict](const RetainPtr<CPDF_Object>& candidate) {
449 return candidate->GetDirect() == pAnnotDict;
450 });
451
452 if (it == locker.end())
453 return -1;
454
455 return pdfium::checked_cast<int>(it - locker.begin());
456 }
457
FPDFPage_CloseAnnot(FPDF_ANNOTATION annot)458 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) {
459 delete CPDFAnnotContextFromFPDFAnnotation(annot);
460 }
461
FPDFPage_RemoveAnnot(FPDF_PAGE page,int index)462 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_RemoveAnnot(FPDF_PAGE page,
463 int index) {
464 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
465 if (!pPage || index < 0)
466 return false;
467
468 RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
469 if (!pAnnots || static_cast<size_t>(index) >= pAnnots->size())
470 return false;
471
472 pAnnots->RemoveAt(index);
473 return true;
474 }
475
476 FPDF_EXPORT FPDF_ANNOTATION_SUBTYPE FPDF_CALLCONV
FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot)477 FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) {
478 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
479 if (!pAnnotDict)
480 return FPDF_ANNOT_UNKNOWN;
481
482 return static_cast<FPDF_ANNOTATION_SUBTYPE>(CPDF_Annot::StringToAnnotSubtype(
483 pAnnotDict->GetNameFor(pdfium::annotation::kSubtype)));
484 }
485
486 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_IsObjectSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype)487 FPDFAnnot_IsObjectSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
488 // The supported subtypes must also be communicated in the user doc.
489 return subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_STAMP;
490 }
491
492 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot,FPDF_PAGEOBJECT obj)493 FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
494 CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
495 CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
496 if (!pAnnot || !pAnnot->HasForm() || !pObj)
497 return false;
498
499 // Check that the annotation type is supported by this method.
500 if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
501 return false;
502
503 // Check that the annotation already has an appearance stream, since an
504 // existing object is to be updated.
505 RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
506 RetainPtr<CPDF_Stream> pStream =
507 GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
508 if (!pStream)
509 return false;
510
511 // Check that the object is already in this annotation's object list.
512 CPDF_Form* pForm = pAnnot->GetForm();
513 if (!pdfium::Contains(*pForm, fxcrt::MakeFakeUniquePtr(pObj)))
514 return false;
515
516 // Update the content stream data in the annotation's AP stream.
517 UpdateContentStream(pForm, pStream.Get());
518 return true;
519 }
520
FPDFAnnot_AddInkStroke(FPDF_ANNOTATION annot,const FS_POINTF * points,size_t point_count)521 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_AddInkStroke(FPDF_ANNOTATION annot,
522 const FS_POINTF* points,
523 size_t point_count) {
524 if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_INK || !points ||
525 point_count == 0 ||
526 !pdfium::IsValueInRangeForNumericType<int32_t>(point_count)) {
527 return -1;
528 }
529
530 RetainPtr<CPDF_Dictionary> annot_dict =
531 GetMutableAnnotDictFromFPDFAnnotation(annot);
532 RetainPtr<CPDF_Array> inklist = annot_dict->GetOrCreateArrayFor("InkList");
533 FX_SAFE_SIZE_T safe_ink_size = inklist->size();
534 safe_ink_size += 1;
535 if (!safe_ink_size.IsValid<int32_t>())
536 return -1;
537
538 // SAFETY: required from caller.
539 auto points_span = UNSAFE_BUFFERS(pdfium::make_span(points, point_count));
540 auto ink_coord_list = inklist->AppendNew<CPDF_Array>();
541 for (const auto& point : points_span) {
542 ink_coord_list->AppendNew<CPDF_Number>(point.x);
543 ink_coord_list->AppendNew<CPDF_Number>(point.y);
544 }
545 return static_cast<int>(inklist->size() - 1);
546 }
547
548 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_RemoveInkList(FPDF_ANNOTATION annot)549 FPDFAnnot_RemoveInkList(FPDF_ANNOTATION annot) {
550 if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_INK)
551 return false;
552
553 RetainPtr<CPDF_Dictionary> annot_dict =
554 CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict();
555 annot_dict->RemoveFor("InkList");
556 return true;
557 }
558
559 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_AppendObject(FPDF_ANNOTATION annot,FPDF_PAGEOBJECT obj)560 FPDFAnnot_AppendObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
561 CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
562 CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
563 if (!pAnnot || !pObj)
564 return false;
565
566 // Check that the annotation type is supported by this method.
567 if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
568 return false;
569
570 // If the annotation does not have an AP stream yet, generate and set it.
571 RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
572 RetainPtr<CPDF_Stream> pStream =
573 GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
574 if (!pStream) {
575 CPDF_GenerateAP::GenerateEmptyAP(pAnnot->GetPage()->GetDocument(),
576 pAnnotDict.Get());
577 pStream = GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
578 if (!pStream)
579 return false;
580 }
581
582 // Get the annotation's corresponding form object for parsing its AP stream.
583 if (!pAnnot->HasForm())
584 pAnnot->SetForm(pStream);
585
586 // Check that the object did not come from the same annotation. If this check
587 // succeeds, then it is assumed that the object came from
588 // FPDFPageObj_CreateNew{Path|Rect}() or FPDFPageObj_New{Text|Image}Obj().
589 // Note that an object that came from a different annotation must not be
590 // passed here, since an object cannot belong to more than one annotation.
591 CPDF_Form* pForm = pAnnot->GetForm();
592 if (pdfium::Contains(*pForm, fxcrt::MakeFakeUniquePtr(pObj)))
593 return false;
594
595 // Append the object to the object list.
596 pForm->AppendPageObject(pdfium::WrapUnique(pObj));
597
598 // Set the content stream data in the annotation's AP stream.
599 UpdateContentStream(pForm, pStream.Get());
600 return true;
601 }
602
FPDFAnnot_GetObjectCount(FPDF_ANNOTATION annot)603 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetObjectCount(FPDF_ANNOTATION annot) {
604 CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
605 if (!pAnnot)
606 return 0;
607
608 if (!pAnnot->HasForm()) {
609 RetainPtr<CPDF_Dictionary> pDict = pAnnot->GetMutableAnnotDict();
610 RetainPtr<CPDF_Stream> pStream =
611 GetAnnotAP(pDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
612 if (!pStream)
613 return 0;
614
615 pAnnot->SetForm(std::move(pStream));
616 }
617 return pdfium::checked_cast<int>(pAnnot->GetForm()->GetPageObjectCount());
618 }
619
620 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFAnnot_GetObject(FPDF_ANNOTATION annot,int index)621 FPDFAnnot_GetObject(FPDF_ANNOTATION annot, int index) {
622 CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
623 if (!pAnnot || index < 0)
624 return nullptr;
625
626 if (!pAnnot->HasForm()) {
627 RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
628 RetainPtr<CPDF_Stream> pStream =
629 GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
630 if (!pStream)
631 return nullptr;
632
633 pAnnot->SetForm(std::move(pStream));
634 }
635
636 return FPDFPageObjectFromCPDFPageObject(
637 pAnnot->GetForm()->GetPageObjectByIndex(index));
638 }
639
640 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_RemoveObject(FPDF_ANNOTATION annot,int index)641 FPDFAnnot_RemoveObject(FPDF_ANNOTATION annot, int index) {
642 CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
643 if (!pAnnot || !pAnnot->HasForm() || index < 0)
644 return false;
645
646 // Check that the annotation type is supported by this method.
647 if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
648 return false;
649
650 // Check that the annotation already has an appearance stream, since an
651 // existing object is to be deleted.
652 RetainPtr<CPDF_Dictionary> pAnnotDict = pAnnot->GetMutableAnnotDict();
653 RetainPtr<CPDF_Stream> pStream =
654 GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
655 if (!pStream)
656 return false;
657
658 if (!pAnnot->GetForm()->ErasePageObjectAtIndex(index))
659 return false;
660
661 UpdateContentStream(pAnnot->GetForm(), pStream.Get());
662 return true;
663 }
664
FPDFAnnot_SetColor(FPDF_ANNOTATION annot,FPDFANNOT_COLORTYPE type,unsigned int R,unsigned int G,unsigned int B,unsigned int A)665 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetColor(FPDF_ANNOTATION annot,
666 FPDFANNOT_COLORTYPE type,
667 unsigned int R,
668 unsigned int G,
669 unsigned int B,
670 unsigned int A) {
671 RetainPtr<CPDF_Dictionary> pAnnotDict =
672 GetMutableAnnotDictFromFPDFAnnotation(annot);
673
674 if (!pAnnotDict || R > 255 || G > 255 || B > 255 || A > 255)
675 return false;
676
677 // For annotations with their appearance streams already defined, the path
678 // stream's own color definitions take priority over the annotation color
679 // definitions set by this method, hence this method will simply fail.
680 if (HasAPStream(pAnnotDict.Get()))
681 return false;
682
683 // Set the opacity of the annotation.
684 pAnnotDict->SetNewFor<CPDF_Number>("CA", A / 255.f);
685
686 // Set the color of the annotation.
687 ByteString key = type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C";
688 RetainPtr<CPDF_Array> pColor = pAnnotDict->GetMutableArrayFor(key);
689 if (pColor)
690 pColor->Clear();
691 else
692 pColor = pAnnotDict->SetNewFor<CPDF_Array>(key);
693
694 pColor->AppendNew<CPDF_Number>(R / 255.f);
695 pColor->AppendNew<CPDF_Number>(G / 255.f);
696 pColor->AppendNew<CPDF_Number>(B / 255.f);
697
698 return true;
699 }
700
FPDFAnnot_GetColor(FPDF_ANNOTATION annot,FPDFANNOT_COLORTYPE type,unsigned int * R,unsigned int * G,unsigned int * B,unsigned int * A)701 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot,
702 FPDFANNOT_COLORTYPE type,
703 unsigned int* R,
704 unsigned int* G,
705 unsigned int* B,
706 unsigned int* A) {
707 RetainPtr<CPDF_Dictionary> pAnnotDict =
708 GetMutableAnnotDictFromFPDFAnnotation(annot);
709
710 if (!pAnnotDict || !R || !G || !B || !A)
711 return false;
712
713 // For annotations with their appearance streams already defined, the path
714 // stream's own color definitions take priority over the annotation color
715 // definitions retrieved by this method, hence this method will simply fail.
716 if (HasAPStream(pAnnotDict.Get()))
717 return false;
718
719 RetainPtr<const CPDF_Array> pColor = pAnnotDict->GetArrayFor(
720 type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C");
721 *A = (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetFloatFor("CA") : 1) * 255.f;
722 if (!pColor) {
723 // Use default color. The default colors must be consistent with the ones
724 // used to generate AP. See calls to GetColorStringWithDefault() in
725 // CPDF_GenerateAP::Generate*AP().
726 if (pAnnotDict->GetNameFor(pdfium::annotation::kSubtype) == "Highlight") {
727 *R = 255;
728 *G = 255;
729 *B = 0;
730 } else {
731 *R = 0;
732 *G = 0;
733 *B = 0;
734 }
735 return true;
736 }
737
738 CFX_Color color = fpdfdoc::CFXColorFromArray(*pColor);
739 switch (color.nColorType) {
740 case CFX_Color::Type::kRGB:
741 *R = color.fColor1 * 255.f;
742 *G = color.fColor2 * 255.f;
743 *B = color.fColor3 * 255.f;
744 break;
745 case CFX_Color::Type::kGray:
746 *R = 255.f * color.fColor1;
747 *G = 255.f * color.fColor1;
748 *B = 255.f * color.fColor1;
749 break;
750 case CFX_Color::Type::kCMYK:
751 *R = 255.f * (1 - color.fColor1) * (1 - color.fColor4);
752 *G = 255.f * (1 - color.fColor2) * (1 - color.fColor4);
753 *B = 255.f * (1 - color.fColor3) * (1 - color.fColor4);
754 break;
755 case CFX_Color::Type::kTransparent:
756 *R = 0;
757 *G = 0;
758 *B = 0;
759 break;
760 }
761 return true;
762 }
763
764 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot)765 FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot) {
766 if (!annot)
767 return false;
768
769 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
770 return subtype == FPDF_ANNOT_LINK || subtype == FPDF_ANNOT_HIGHLIGHT ||
771 subtype == FPDF_ANNOT_UNDERLINE || subtype == FPDF_ANNOT_SQUIGGLY ||
772 subtype == FPDF_ANNOT_STRIKEOUT;
773 }
774
775 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,size_t quad_index,const FS_QUADPOINTSF * quad_points)776 FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
777 size_t quad_index,
778 const FS_QUADPOINTSF* quad_points) {
779 if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
780 return false;
781
782 RetainPtr<CPDF_Dictionary> pAnnotDict =
783 CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict();
784 RetainPtr<CPDF_Array> pQuadPointsArray =
785 GetMutableQuadPointsArrayFromDictionary(pAnnotDict.Get());
786 if (!IsValidQuadPointsIndex(pQuadPointsArray.Get(), quad_index))
787 return false;
788
789 SetQuadPointsAtIndex(pQuadPointsArray.Get(), quad_index, quad_points);
790 UpdateBBox(pAnnotDict.Get());
791 return true;
792 }
793
794 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot,const FS_QUADPOINTSF * quad_points)795 FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot,
796 const FS_QUADPOINTSF* quad_points) {
797 if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
798 return false;
799
800 RetainPtr<CPDF_Dictionary> pAnnotDict =
801 CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict();
802 RetainPtr<CPDF_Array> pQuadPointsArray =
803 GetMutableQuadPointsArrayFromDictionary(pAnnotDict.Get());
804 if (!pQuadPointsArray)
805 pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict.Get());
806 AppendQuadPoints(pQuadPointsArray.Get(), quad_points);
807 UpdateBBox(pAnnotDict.Get());
808 return true;
809 }
810
811 FPDF_EXPORT size_t FPDF_CALLCONV
FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot)812 FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot) {
813 if (!FPDFAnnot_HasAttachmentPoints(annot))
814 return 0;
815
816 const CPDF_Dictionary* pAnnotDict =
817 CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
818 RetainPtr<const CPDF_Array> pArray =
819 GetQuadPointsArrayFromDictionary(pAnnotDict);
820 return pArray ? pArray->size() / 8 : 0;
821 }
822
823 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,size_t quad_index,FS_QUADPOINTSF * quad_points)824 FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
825 size_t quad_index,
826 FS_QUADPOINTSF* quad_points) {
827 if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
828 return false;
829
830 const CPDF_Dictionary* pAnnotDict =
831 CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
832 RetainPtr<const CPDF_Array> pArray =
833 GetQuadPointsArrayFromDictionary(pAnnotDict);
834 if (!pArray)
835 return false;
836
837 return GetQuadPointsAtIndex(std::move(pArray), quad_index, quad_points);
838 }
839
FPDFAnnot_SetRect(FPDF_ANNOTATION annot,const FS_RECTF * rect)840 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
841 const FS_RECTF* rect) {
842 RetainPtr<CPDF_Dictionary> pAnnotDict =
843 GetMutableAnnotDictFromFPDFAnnotation(annot);
844 if (!pAnnotDict || !rect)
845 return false;
846
847 CFX_FloatRect newRect = CFXFloatRectFromFSRectF(*rect);
848
849 // Update the "Rect" entry in the annotation dictionary.
850 pAnnotDict->SetRectFor(pdfium::annotation::kRect, newRect);
851
852 // If the annotation's appearance stream is defined, the annotation is of a
853 // type that does not have quadpoints, and the new rectangle is bigger than
854 // the current bounding box, then update the "BBox" entry in the AP
855 // dictionary too, since its "BBox" entry comes from annotation dictionary's
856 // "Rect" entry.
857 if (FPDFAnnot_HasAttachmentPoints(annot))
858 return true;
859
860 RetainPtr<CPDF_Stream> pStream =
861 GetAnnotAP(pAnnotDict.Get(), CPDF_Annot::AppearanceMode::kNormal);
862 if (pStream && newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
863 pStream->GetMutableDict()->SetRectFor("BBox", newRect);
864 return true;
865 }
866
FPDFAnnot_GetRect(FPDF_ANNOTATION annot,FS_RECTF * rect)867 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot,
868 FS_RECTF* rect) {
869 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
870 if (!pAnnotDict || !rect)
871 return false;
872
873 *rect = FSRectFFromCFXFloatRect(
874 pAnnotDict->GetRectFor(pdfium::annotation::kRect));
875 return true;
876 }
877
878 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetVertices(FPDF_ANNOTATION annot,FS_POINTF * buffer,unsigned long length)879 FPDFAnnot_GetVertices(FPDF_ANNOTATION annot,
880 FS_POINTF* buffer,
881 unsigned long length) {
882 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
883 if (subtype != FPDF_ANNOT_POLYGON && subtype != FPDF_ANNOT_POLYLINE)
884 return 0;
885
886 const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
887 if (!annot_dict)
888 return 0;
889
890 RetainPtr<const CPDF_Array> vertices =
891 annot_dict->GetArrayFor(pdfium::annotation::kVertices);
892 if (!vertices)
893 return 0;
894
895 // Truncate to an even number.
896 const unsigned long points_len =
897 fxcrt::CollectionSize<unsigned long>(*vertices) / 2;
898 if (buffer && length >= points_len) {
899 // SAFETY: required from caller.
900 auto buffer_span = UNSAFE_BUFFERS(pdfium::make_span(buffer, length));
901 for (unsigned long i = 0; i < points_len; ++i) {
902 buffer_span[i].x = vertices->GetFloatAt(i * 2);
903 buffer_span[i].y = vertices->GetFloatAt(i * 2 + 1);
904 }
905 }
906 return points_len;
907 }
908
909 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot)910 FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot) {
911 RetainPtr<const CPDF_Array> ink_list = GetInkList(annot);
912 return ink_list ? fxcrt::CollectionSize<unsigned long>(*ink_list) : 0;
913 }
914
915 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot,unsigned long path_index,FS_POINTF * buffer,unsigned long length)916 FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot,
917 unsigned long path_index,
918 FS_POINTF* buffer,
919 unsigned long length) {
920 RetainPtr<const CPDF_Array> ink_list = GetInkList(annot);
921 if (!ink_list)
922 return 0;
923
924 RetainPtr<const CPDF_Array> path = ink_list->GetArrayAt(path_index);
925 if (!path)
926 return 0;
927
928 // Truncate to an even number.
929 const unsigned long points_len =
930 fxcrt::CollectionSize<unsigned long>(*path) / 2;
931 if (buffer && length >= points_len) {
932 // SAFETY: required from caller.
933 auto buffer_span = UNSAFE_BUFFERS(pdfium::make_span(buffer, length));
934 for (unsigned long i = 0; i < points_len; ++i) {
935 buffer_span[i].x = path->GetFloatAt(i * 2);
936 buffer_span[i].y = path->GetFloatAt(i * 2 + 1);
937 }
938 }
939 return points_len;
940 }
941
FPDFAnnot_GetLine(FPDF_ANNOTATION annot,FS_POINTF * start,FS_POINTF * end)942 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetLine(FPDF_ANNOTATION annot,
943 FS_POINTF* start,
944 FS_POINTF* end) {
945 if (!start || !end)
946 return false;
947
948 FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
949 if (subtype != FPDF_ANNOT_LINE)
950 return false;
951
952 const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
953 if (!annot_dict)
954 return false;
955
956 RetainPtr<const CPDF_Array> line =
957 annot_dict->GetArrayFor(pdfium::annotation::kL);
958 if (!line || line->size() < 4)
959 return false;
960
961 start->x = line->GetFloatAt(0);
962 start->y = line->GetFloatAt(1);
963 end->x = line->GetFloatAt(2);
964 end->y = line->GetFloatAt(3);
965 return true;
966 }
967
FPDFAnnot_SetBorder(FPDF_ANNOTATION annot,float horizontal_radius,float vertical_radius,float border_width)968 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetBorder(FPDF_ANNOTATION annot,
969 float horizontal_radius,
970 float vertical_radius,
971 float border_width) {
972 RetainPtr<CPDF_Dictionary> annot_dict =
973 GetMutableAnnotDictFromFPDFAnnotation(annot);
974 if (!annot_dict)
975 return false;
976
977 // Remove the appearance stream. Otherwise PDF viewers will render that and
978 // not use the border values.
979 annot_dict->RemoveFor(pdfium::annotation::kAP);
980
981 auto border = annot_dict->SetNewFor<CPDF_Array>(pdfium::annotation::kBorder);
982 border->AppendNew<CPDF_Number>(horizontal_radius);
983 border->AppendNew<CPDF_Number>(vertical_radius);
984 border->AppendNew<CPDF_Number>(border_width);
985 return true;
986 }
987
988 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetBorder(FPDF_ANNOTATION annot,float * horizontal_radius,float * vertical_radius,float * border_width)989 FPDFAnnot_GetBorder(FPDF_ANNOTATION annot,
990 float* horizontal_radius,
991 float* vertical_radius,
992 float* border_width) {
993 if (!horizontal_radius || !vertical_radius || !border_width)
994 return false;
995
996 const CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
997 if (!annot_dict)
998 return false;
999
1000 RetainPtr<const CPDF_Array> border =
1001 annot_dict->GetArrayFor(pdfium::annotation::kBorder);
1002 if (!border || border->size() < 3)
1003 return false;
1004
1005 *horizontal_radius = border->GetFloatAt(0);
1006 *vertical_radius = border->GetFloatAt(1);
1007 *border_width = border->GetFloatAt(2);
1008 return true;
1009 }
1010
FPDFAnnot_HasKey(FPDF_ANNOTATION annot,FPDF_BYTESTRING key)1011 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
1012 FPDF_BYTESTRING key) {
1013 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
1014 return pAnnotDict && pAnnotDict->KeyExist(key);
1015 }
1016
1017 FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
FPDFAnnot_GetValueType(FPDF_ANNOTATION annot,FPDF_BYTESTRING key)1018 FPDFAnnot_GetValueType(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
1019 if (!FPDFAnnot_HasKey(annot, key))
1020 return FPDF_OBJECT_UNKNOWN;
1021
1022 const CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
1023 RetainPtr<const CPDF_Object> pObj = pAnnot->GetAnnotDict()->GetObjectFor(key);
1024 return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
1025 }
1026
1027 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot,FPDF_BYTESTRING key,FPDF_WIDESTRING value)1028 FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot,
1029 FPDF_BYTESTRING key,
1030 FPDF_WIDESTRING value) {
1031 RetainPtr<CPDF_Dictionary> pAnnotDict =
1032 GetMutableAnnotDictFromFPDFAnnotation(annot);
1033 if (!pAnnotDict)
1034 return false;
1035
1036 // SAFETY: required from caller.
1037 pAnnotDict->SetNewFor<CPDF_String>(
1038 key, UNSAFE_BUFFERS(WideStringFromFPDFWideString(value).AsStringView()));
1039 return true;
1040 }
1041
1042 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot,FPDF_BYTESTRING key,FPDF_WCHAR * buffer,unsigned long buflen)1043 FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot,
1044 FPDF_BYTESTRING key,
1045 FPDF_WCHAR* buffer,
1046 unsigned long buflen) {
1047 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
1048 if (!pAnnotDict) {
1049 return 0;
1050 }
1051 // SAFETY: required from caller.
1052 return Utf16EncodeMaybeCopyAndReturnLength(
1053 pAnnotDict->GetUnicodeTextFor(key),
1054 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1055 }
1056
1057 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetNumberValue(FPDF_ANNOTATION annot,FPDF_BYTESTRING key,float * value)1058 FPDFAnnot_GetNumberValue(FPDF_ANNOTATION annot,
1059 FPDF_BYTESTRING key,
1060 float* value) {
1061 if (!value)
1062 return false;
1063
1064 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
1065 if (!pAnnotDict)
1066 return false;
1067
1068 RetainPtr<const CPDF_Object> p = pAnnotDict->GetObjectFor(key);
1069 if (!p || p->GetType() != FPDF_OBJECT_NUMBER)
1070 return false;
1071
1072 *value = p->GetNumber();
1073 return true;
1074 }
1075
1076 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_SetAP(FPDF_ANNOTATION annot,FPDF_ANNOT_APPEARANCEMODE appearanceMode,FPDF_WIDESTRING value)1077 FPDFAnnot_SetAP(FPDF_ANNOTATION annot,
1078 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
1079 FPDF_WIDESTRING value) {
1080 RetainPtr<CPDF_Dictionary> pAnnotDict =
1081 GetMutableAnnotDictFromFPDFAnnotation(annot);
1082 if (!pAnnotDict)
1083 return false;
1084
1085 if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
1086 return false;
1087
1088 static constexpr auto kModeKeyForMode =
1089 fxcrt::ToArray<const char*>({"N", "R", "D"});
1090 static_assert(kModeKeyForMode.size() == FPDF_ANNOT_APPEARANCEMODE_COUNT,
1091 "length of kModeKeyForMode should be equal to "
1092 "FPDF_ANNOT_APPEARANCEMODE_COUNT");
1093
1094 const char* mode_key = kModeKeyForMode[appearanceMode];
1095
1096 RetainPtr<CPDF_Dictionary> pApDict =
1097 pAnnotDict->GetMutableDictFor(pdfium::annotation::kAP);
1098
1099 // If `value` is null, then the action is to remove.
1100 if (!value) {
1101 if (pApDict) {
1102 if (appearanceMode == FPDF_ANNOT_APPEARANCEMODE_NORMAL) {
1103 pAnnotDict->RemoveFor(pdfium::annotation::kAP);
1104 } else {
1105 pApDict->RemoveFor(mode_key);
1106 }
1107 }
1108 return true;
1109 }
1110
1111 // Otherwise, add/update when `value` is non-null.
1112 //
1113 // Annotation object's non-empty bounding rect will be used as the /BBox
1114 // for the associated /XObject object
1115 CFX_FloatRect rect = pAnnotDict->GetRectFor(pdfium::annotation::kRect);
1116 constexpr float kMinSize = 0.000001f;
1117 if (rect.Width() < kMinSize || rect.Height() < kMinSize) {
1118 return false;
1119 }
1120
1121 CPDF_AnnotContext* pAnnotContext = CPDFAnnotContextFromFPDFAnnotation(annot);
1122
1123 CPDF_Document* pDoc = pAnnotContext->GetPage()->GetDocument();
1124 if (!pDoc) {
1125 return false;
1126 }
1127
1128 auto stream_dict = pdfium::MakeRetain<CPDF_Dictionary>();
1129 stream_dict->SetNewFor<CPDF_Name>(pdfium::annotation::kType, "XObject");
1130 stream_dict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Form");
1131 stream_dict->SetRectFor("BBox", rect);
1132 // Transparency values are specified in range [0.0f, 1.0f]. We are strictly
1133 // checking for value < 1 and not <= 1 so that the output PDF size does not
1134 // unnecessarily bloat up by creating a new dictionary in case of solid
1135 // color.
1136 if (pAnnotDict->KeyExist("CA") && pAnnotDict->GetFloatFor("CA") < 1.0f) {
1137 stream_dict->SetFor("Resources", SetExtGStateInResourceDict(
1138 pDoc, pAnnotDict.Get(), "Normal"));
1139 }
1140 // SAFETY: required from caller.
1141 ByteString new_stream_data = PDF_EncodeText(
1142 UNSAFE_BUFFERS(WideStringFromFPDFWideString(value).AsStringView()));
1143 auto new_stream = pDoc->NewIndirect<CPDF_Stream>(std::move(stream_dict));
1144 new_stream->SetData(new_stream_data.unsigned_span());
1145
1146 // Storing reference to indirect object in annotation's AP
1147 if (!pApDict) {
1148 pApDict = pAnnotDict->SetNewFor<CPDF_Dictionary>(pdfium::annotation::kAP);
1149 }
1150 pApDict->SetNewFor<CPDF_Reference>(mode_key, pDoc, new_stream->GetObjNum());
1151
1152 return true;
1153 }
1154
1155 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetAP(FPDF_ANNOTATION annot,FPDF_ANNOT_APPEARANCEMODE appearanceMode,FPDF_WCHAR * buffer,unsigned long buflen)1156 FPDFAnnot_GetAP(FPDF_ANNOTATION annot,
1157 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
1158 FPDF_WCHAR* buffer,
1159 unsigned long buflen) {
1160 RetainPtr<CPDF_Dictionary> pAnnotDict =
1161 GetMutableAnnotDictFromFPDFAnnotation(annot);
1162 if (!pAnnotDict)
1163 return 0;
1164
1165 if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
1166 return 0;
1167
1168 CPDF_Annot::AppearanceMode mode =
1169 static_cast<CPDF_Annot::AppearanceMode>(appearanceMode);
1170
1171 RetainPtr<CPDF_Stream> pStream = GetAnnotAPNoFallback(pAnnotDict.Get(), mode);
1172 // SAFETY: required from caller.
1173 return Utf16EncodeMaybeCopyAndReturnLength(
1174 pStream ? pStream->GetUnicodeText() : WideString(),
1175 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1176 }
1177
1178 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
FPDFAnnot_GetLinkedAnnot(FPDF_ANNOTATION annot,FPDF_BYTESTRING key)1179 FPDFAnnot_GetLinkedAnnot(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
1180 CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
1181 if (!pAnnot)
1182 return nullptr;
1183
1184 RetainPtr<CPDF_Dictionary> pLinkedDict =
1185 pAnnot->GetMutableAnnotDict()->GetMutableDictFor(key);
1186 if (!pLinkedDict || pLinkedDict->GetNameFor("Type") != "Annot")
1187 return nullptr;
1188
1189 auto pLinkedAnnot = std::make_unique<CPDF_AnnotContext>(
1190 std::move(pLinkedDict), pAnnot->GetPage());
1191
1192 // Caller takes ownership.
1193 return FPDFAnnotationFromCPDFAnnotContext(pLinkedAnnot.release());
1194 }
1195
FPDFAnnot_GetFlags(FPDF_ANNOTATION annot)1196 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetFlags(FPDF_ANNOTATION annot) {
1197 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
1198 return pAnnotDict ? pAnnotDict->GetIntegerFor(pdfium::annotation::kF)
1199 : FPDF_ANNOT_FLAG_NONE;
1200 }
1201
FPDFAnnot_SetFlags(FPDF_ANNOTATION annot,int flags)1202 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetFlags(FPDF_ANNOTATION annot,
1203 int flags) {
1204 RetainPtr<CPDF_Dictionary> pAnnotDict =
1205 GetMutableAnnotDictFromFPDFAnnotation(annot);
1206 if (!pAnnotDict)
1207 return false;
1208
1209 pAnnotDict->SetNewFor<CPDF_Number>(pdfium::annotation::kF, flags);
1210 return true;
1211 }
1212
1213 FPDF_EXPORT int FPDF_CALLCONV
FPDFAnnot_GetFormFieldFlags(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)1214 FPDFAnnot_GetFormFieldFlags(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
1215 CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1216 return pFormField ? pFormField->GetFieldFlags() : FPDF_FORMFLAG_NONE;
1217 }
1218
1219 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle,FPDF_PAGE page,const FS_POINTF * point)1220 FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
1221 FPDF_PAGE page,
1222 const FS_POINTF* point) {
1223 if (!point)
1224 return nullptr;
1225
1226 const CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
1227 if (!pForm)
1228 return nullptr;
1229
1230 const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
1231 if (!pPage)
1232 return nullptr;
1233
1234 const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
1235 int annot_index = -1;
1236 const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
1237 pPage, CFXPointFFromFSPointF(*point), &annot_index);
1238 if (!pFormCtrl || annot_index == -1)
1239 return nullptr;
1240 return FPDFPage_GetAnnot(page, annot_index);
1241 }
1242
1243 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,FPDF_WCHAR * buffer,unsigned long buflen)1244 FPDFAnnot_GetFormFieldName(FPDF_FORMHANDLE hHandle,
1245 FPDF_ANNOTATION annot,
1246 FPDF_WCHAR* buffer,
1247 unsigned long buflen) {
1248 const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1249 if (!pFormField) {
1250 return 0;
1251 }
1252 // SAFETY: required from caller.
1253 return Utf16EncodeMaybeCopyAndReturnLength(
1254 pFormField->GetFullName(),
1255 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1256 }
1257
1258 FPDF_EXPORT int FPDF_CALLCONV
FPDFAnnot_GetFormFieldType(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)1259 FPDFAnnot_GetFormFieldType(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
1260 const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1261 return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
1262 }
1263
1264 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,int event,FPDF_WCHAR * buffer,unsigned long buflen)1265 FPDFAnnot_GetFormAdditionalActionJavaScript(FPDF_FORMHANDLE hHandle,
1266 FPDF_ANNOTATION annot,
1267 int event,
1268 FPDF_WCHAR* buffer,
1269 unsigned long buflen) {
1270 const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1271 if (!pFormField)
1272 return 0;
1273
1274 if (event < FPDF_ANNOT_AACTION_KEY_STROKE ||
1275 event > FPDF_ANNOT_AACTION_CALCULATE) {
1276 return 0;
1277 }
1278
1279 auto type = static_cast<CPDF_AAction::AActionType>(event);
1280 CPDF_AAction additional_action = pFormField->GetAdditionalAction();
1281 CPDF_Action action = additional_action.GetAction(type);
1282 // SAFETY: required from caller.
1283 return Utf16EncodeMaybeCopyAndReturnLength(
1284 action.GetJavaScript(),
1285 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1286 }
1287
1288 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetFormFieldAlternateName(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,FPDF_WCHAR * buffer,unsigned long buflen)1289 FPDFAnnot_GetFormFieldAlternateName(FPDF_FORMHANDLE hHandle,
1290 FPDF_ANNOTATION annot,
1291 FPDF_WCHAR* buffer,
1292 unsigned long buflen) {
1293 const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1294 if (!pFormField) {
1295 return 0;
1296 }
1297 // SAFETY: required from caller.
1298 return Utf16EncodeMaybeCopyAndReturnLength(
1299 pFormField->GetAlternateName(),
1300 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1301 }
1302
1303 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,FPDF_WCHAR * buffer,unsigned long buflen)1304 FPDFAnnot_GetFormFieldValue(FPDF_FORMHANDLE hHandle,
1305 FPDF_ANNOTATION annot,
1306 FPDF_WCHAR* buffer,
1307 unsigned long buflen) {
1308 const CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1309 if (!pFormField) {
1310 return 0;
1311 }
1312 // SAFETY: required from caller.
1313 return Utf16EncodeMaybeCopyAndReturnLength(
1314 pFormField->GetValue(),
1315 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1316 }
1317
FPDFAnnot_GetOptionCount(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)1318 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetOptionCount(FPDF_FORMHANDLE hHandle,
1319 FPDF_ANNOTATION annot) {
1320 const CPDF_FormField* form_field = GetFormField(hHandle, annot);
1321 if (!form_field || !form_field->HasOptField()) {
1322 return -1;
1323 }
1324 return form_field->CountOptions();
1325 }
1326
1327 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,int index,FPDF_WCHAR * buffer,unsigned long buflen)1328 FPDFAnnot_GetOptionLabel(FPDF_FORMHANDLE hHandle,
1329 FPDF_ANNOTATION annot,
1330 int index,
1331 FPDF_WCHAR* buffer,
1332 unsigned long buflen) {
1333 if (index < 0) {
1334 return 0;
1335 }
1336
1337 const CPDF_FormField* form_field = GetFormField(hHandle, annot);
1338 if (!form_field || !form_field->HasOptField() ||
1339 index >= form_field->CountOptions()) {
1340 return 0;
1341 }
1342
1343 // SAFETY: required from caller.
1344 return Utf16EncodeMaybeCopyAndReturnLength(
1345 form_field->GetOptionLabel(index),
1346 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
1347 }
1348
1349 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle,FPDF_ANNOTATION annot,int index)1350 FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle,
1351 FPDF_ANNOTATION annot,
1352 int index) {
1353 if (index < 0) {
1354 return false;
1355 }
1356
1357 const CPDF_FormField* form_field = GetFormField(handle, annot);
1358 if (!form_field) {
1359 return false;
1360 }
1361
1362 if (form_field->GetFieldType() != FormFieldType::kComboBox &&
1363 form_field->GetFieldType() != FormFieldType::kListBox) {
1364 return false;
1365 }
1366
1367 return index < form_field->CountOptions() &&
1368 form_field->IsItemSelected(index);
1369 }
1370
1371 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,float * value)1372 FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
1373 FPDF_ANNOTATION annot,
1374 float* value) {
1375 if (!value) {
1376 return false;
1377 }
1378
1379 const CPDFSDK_Widget* widget = GetWidgetOfTypes(hHandle, annot, {});
1380 if (!widget) {
1381 return false;
1382 }
1383
1384 *value = widget->GetFontSize();
1385 return true;
1386 }
1387
1388 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetFontColor(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,unsigned int * R,unsigned int * G,unsigned int * B)1389 FPDFAnnot_GetFontColor(FPDF_FORMHANDLE hHandle,
1390 FPDF_ANNOTATION annot,
1391 unsigned int* R,
1392 unsigned int* G,
1393 unsigned int* B) {
1394 if (!R || !G || !B) {
1395 return false;
1396 }
1397
1398 const CPDFSDK_Widget* widget = GetWidgetOfTypes(hHandle, annot, {});
1399 if (!widget) {
1400 return false;
1401 }
1402
1403 std::optional<FX_COLORREF> text_color = widget->GetTextColor();
1404 if (!text_color) {
1405 return false;
1406 }
1407
1408 *R = FXSYS_GetRValue(*text_color);
1409 *G = FXSYS_GetGValue(*text_color);
1410 *B = FXSYS_GetBValue(*text_color);
1411 return true;
1412 }
1413
FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)1414 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_IsChecked(FPDF_FORMHANDLE hHandle,
1415 FPDF_ANNOTATION annot) {
1416 const CPDFSDK_Widget* pWidget =
1417 GetRadioButtonOrCheckBoxWidget(hHandle, annot);
1418 return pWidget && pWidget->IsChecked();
1419 }
1420
1421 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_SetFocusableSubtypes(FPDF_FORMHANDLE hHandle,const FPDF_ANNOTATION_SUBTYPE * subtypes,size_t count)1422 FPDFAnnot_SetFocusableSubtypes(FPDF_FORMHANDLE hHandle,
1423 const FPDF_ANNOTATION_SUBTYPE* subtypes,
1424 size_t count) {
1425 CPDFSDK_FormFillEnvironment* pFormFillEnv =
1426 CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
1427 if (!pFormFillEnv)
1428 return false;
1429
1430 if (count > 0 && !subtypes)
1431 return false;
1432
1433 // SAFETY: required from caller.
1434 auto subtypes_span = UNSAFE_BUFFERS(pdfium::make_span(subtypes, count));
1435 std::vector<CPDF_Annot::Subtype> focusable_annot_types;
1436 focusable_annot_types.reserve(count);
1437 for (size_t i = 0; i < count; ++i) {
1438 focusable_annot_types.push_back(
1439 static_cast<CPDF_Annot::Subtype>(subtypes_span[i]));
1440 }
1441
1442 pFormFillEnv->SetFocusableAnnotSubtypes(focusable_annot_types);
1443 return true;
1444 }
1445
1446 FPDF_EXPORT int FPDF_CALLCONV
FPDFAnnot_GetFocusableSubtypesCount(FPDF_FORMHANDLE hHandle)1447 FPDFAnnot_GetFocusableSubtypesCount(FPDF_FORMHANDLE hHandle) {
1448 CPDFSDK_FormFillEnvironment* pFormFillEnv =
1449 CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
1450 if (!pFormFillEnv)
1451 return -1;
1452
1453 return fxcrt::CollectionSize<int>(pFormFillEnv->GetFocusableAnnotSubtypes());
1454 }
1455
1456 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetFocusableSubtypes(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION_SUBTYPE * subtypes,size_t count)1457 FPDFAnnot_GetFocusableSubtypes(FPDF_FORMHANDLE hHandle,
1458 FPDF_ANNOTATION_SUBTYPE* subtypes,
1459 size_t count) {
1460 CPDFSDK_FormFillEnvironment* pFormFillEnv =
1461 CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
1462 if (!pFormFillEnv)
1463 return false;
1464
1465 if (!subtypes)
1466 return false;
1467
1468 const std::vector<CPDF_Annot::Subtype>& focusable_annot_types =
1469 pFormFillEnv->GetFocusableAnnotSubtypes();
1470
1471 // Host should allocate enough memory to get the list of currently supported
1472 // focusable subtypes.
1473 if (count < focusable_annot_types.size())
1474 return false;
1475
1476 // SAFETY: required from caller.
1477 auto subtypes_span = UNSAFE_BUFFERS(pdfium::make_span(subtypes, count));
1478 for (size_t i = 0; i < focusable_annot_types.size(); ++i) {
1479 subtypes_span[i] =
1480 static_cast<FPDF_ANNOTATION_SUBTYPE>(focusable_annot_types[i]);
1481 }
1482 return true;
1483 }
1484
FPDFAnnot_GetLink(FPDF_ANNOTATION annot)1485 FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFAnnot_GetLink(FPDF_ANNOTATION annot) {
1486 if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK)
1487 return nullptr;
1488
1489 // Unretained reference in public API. NOLINTNEXTLINE
1490 return FPDFLinkFromCPDFDictionary(
1491 CPDFAnnotContextFromFPDFAnnotation(annot)->GetMutableAnnotDict());
1492 }
1493
1494 FPDF_EXPORT int FPDF_CALLCONV
FPDFAnnot_GetFormControlCount(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)1495 FPDFAnnot_GetFormControlCount(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
1496 CPDF_FormField* pFormField = GetFormField(hHandle, annot);
1497 return pFormField ? pFormField->CountControls() : -1;
1498 }
1499
1500 FPDF_EXPORT int FPDF_CALLCONV
FPDFAnnot_GetFormControlIndex(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot)1501 FPDFAnnot_GetFormControlIndex(FPDF_FORMHANDLE hHandle, FPDF_ANNOTATION annot) {
1502 const CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
1503 if (!pAnnotDict)
1504 return -1;
1505
1506 CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
1507 if (!pForm)
1508 return -1;
1509
1510 CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
1511 CPDF_FormField* pFormField = pPDFForm->GetFieldByDict(pAnnotDict);
1512 CPDF_FormControl* pFormControl = pPDFForm->GetControlByDict(pAnnotDict);
1513 return pFormField ? pFormField->GetControlIndex(pFormControl) : -1;
1514 }
1515
1516 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAnnot_GetFormFieldExportValue(FPDF_FORMHANDLE hHandle,FPDF_ANNOTATION annot,FPDF_WCHAR * buffer,unsigned long buflen)1517 FPDFAnnot_GetFormFieldExportValue(FPDF_FORMHANDLE hHandle,
1518 FPDF_ANNOTATION annot,
1519 FPDF_WCHAR* buffer,
1520 unsigned long buflen) {
1521 const CPDFSDK_Widget* pWidget =
1522 GetRadioButtonOrCheckBoxWidget(hHandle, annot);
1523 if (!pWidget) {
1524 return 0;
1525 }
1526 // SAFETY: required from caller.
1527 return Utf16EncodeMaybeCopyAndReturnLength(
1528 pWidget->GetExportValue(),
1529 UNSAFE_TODO(SpanFromFPDFApiArgs(buffer, buflen)));
1530 }
1531
FPDFAnnot_SetURI(FPDF_ANNOTATION annot,const char * uri)1532 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetURI(FPDF_ANNOTATION annot,
1533 const char* uri) {
1534 if (!uri || FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_LINK)
1535 return false;
1536
1537 RetainPtr<CPDF_Dictionary> annot_dict =
1538 GetMutableAnnotDictFromFPDFAnnotation(annot);
1539 auto action = annot_dict->SetNewFor<CPDF_Dictionary>("A");
1540 action->SetNewFor<CPDF_Name>("Type", "Action");
1541 action->SetNewFor<CPDF_Name>("S", "URI");
1542 action->SetNewFor<CPDF_String>("URI", uri);
1543 return true;
1544 }
1545
1546 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
FPDFAnnot_GetFileAttachment(FPDF_ANNOTATION annot)1547 FPDFAnnot_GetFileAttachment(FPDF_ANNOTATION annot) {
1548 if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_FILEATTACHMENT) {
1549 return nullptr;
1550 }
1551
1552 RetainPtr<CPDF_Dictionary> annot_dict =
1553 GetMutableAnnotDictFromFPDFAnnotation(annot);
1554 if (!annot_dict) {
1555 return nullptr;
1556 }
1557
1558 return FPDFAttachmentFromCPDFObject(
1559 annot_dict->GetMutableDirectObjectFor("FS"));
1560 }
1561
1562 FPDF_EXPORT FPDF_ATTACHMENT FPDF_CALLCONV
FPDFAnnot_AddFileAttachment(FPDF_ANNOTATION annot,FPDF_WIDESTRING name)1563 FPDFAnnot_AddFileAttachment(FPDF_ANNOTATION annot, FPDF_WIDESTRING name) {
1564 if (FPDFAnnot_GetSubtype(annot) != FPDF_ANNOT_FILEATTACHMENT) {
1565 return nullptr;
1566 }
1567
1568 CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot);
1569 if (!context) {
1570 return nullptr;
1571 }
1572
1573 RetainPtr<CPDF_Dictionary> annot_dict = context->GetMutableAnnotDict();
1574 if (!annot_dict) {
1575 return nullptr;
1576 }
1577
1578 // SAFETY: required from caller.
1579 WideString ws_name = UNSAFE_BUFFERS(WideStringFromFPDFWideString(name));
1580 if (ws_name.IsEmpty()) {
1581 return nullptr;
1582 }
1583
1584 CPDF_Document* doc = context->GetPage()->GetDocument();
1585 auto fs_obj = doc->NewIndirect<CPDF_Dictionary>();
1586
1587 fs_obj->SetNewFor<CPDF_Name>("Type", "Filespec");
1588 fs_obj->SetNewFor<CPDF_String>("UF", ws_name.AsStringView());
1589 fs_obj->SetNewFor<CPDF_String>("F", ws_name.AsStringView());
1590
1591 annot_dict->SetNewFor<CPDF_Reference>("FS", doc, fs_obj->GetObjNum());
1592 return FPDFAttachmentFromCPDFObject(fs_obj);
1593 }
1594