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