Home | History | Annotate | Download | only in fpdfsdk
      1 // Copyright 2017 PDFium Authors. All rights reserved.
      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 <utility>
      9 
     10 #include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
     11 #include "core/fpdfapi/page/cpdf_form.h"
     12 #include "core/fpdfapi/page/cpdf_page.h"
     13 #include "core/fpdfapi/page/cpdf_pageobject.h"
     14 #include "core/fpdfapi/parser/cpdf_array.h"
     15 #include "core/fpdfapi/parser/cpdf_dictionary.h"
     16 #include "core/fpdfapi/parser/cpdf_document.h"
     17 #include "core/fpdfapi/parser/cpdf_name.h"
     18 #include "core/fpdfapi/parser/cpdf_number.h"
     19 #include "core/fpdfapi/parser/cpdf_string.h"
     20 #include "core/fpdfdoc/cpdf_annot.h"
     21 #include "core/fpdfdoc/cpdf_formfield.h"
     22 #include "core/fpdfdoc/cpdf_interform.h"
     23 #include "core/fpdfdoc/cpvt_generateap.h"
     24 #include "core/fxge/cfx_color.h"
     25 #include "fpdfsdk/fsdk_define.h"
     26 
     27 namespace {
     28 
     29 // These checks ensure the consistency of annotation subtype values across core/
     30 // and public.
     31 static_assert(static_cast<int>(CPDF_Annot::Subtype::UNKNOWN) ==
     32                   FPDF_ANNOT_UNKNOWN,
     33               "CPDF_Annot::UNKNOWN value mismatch");
     34 static_assert(static_cast<int>(CPDF_Annot::Subtype::TEXT) == FPDF_ANNOT_TEXT,
     35               "CPDF_Annot::TEXT value mismatch");
     36 static_assert(static_cast<int>(CPDF_Annot::Subtype::LINK) == FPDF_ANNOT_LINK,
     37               "CPDF_Annot::LINK value mismatch");
     38 static_assert(static_cast<int>(CPDF_Annot::Subtype::FREETEXT) ==
     39                   FPDF_ANNOT_FREETEXT,
     40               "CPDF_Annot::FREETEXT value mismatch");
     41 static_assert(static_cast<int>(CPDF_Annot::Subtype::LINE) == FPDF_ANNOT_LINE,
     42               "CPDF_Annot::LINE value mismatch");
     43 static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUARE) ==
     44                   FPDF_ANNOT_SQUARE,
     45               "CPDF_Annot::SQUARE value mismatch");
     46 static_assert(static_cast<int>(CPDF_Annot::Subtype::CIRCLE) ==
     47                   FPDF_ANNOT_CIRCLE,
     48               "CPDF_Annot::CIRCLE value mismatch");
     49 static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYGON) ==
     50                   FPDF_ANNOT_POLYGON,
     51               "CPDF_Annot::POLYGON value mismatch");
     52 static_assert(static_cast<int>(CPDF_Annot::Subtype::POLYLINE) ==
     53                   FPDF_ANNOT_POLYLINE,
     54               "CPDF_Annot::POLYLINE value mismatch");
     55 static_assert(static_cast<int>(CPDF_Annot::Subtype::HIGHLIGHT) ==
     56                   FPDF_ANNOT_HIGHLIGHT,
     57               "CPDF_Annot::HIGHLIGHT value mismatch");
     58 static_assert(static_cast<int>(CPDF_Annot::Subtype::UNDERLINE) ==
     59                   FPDF_ANNOT_UNDERLINE,
     60               "CPDF_Annot::UNDERLINE value mismatch");
     61 static_assert(static_cast<int>(CPDF_Annot::Subtype::SQUIGGLY) ==
     62                   FPDF_ANNOT_SQUIGGLY,
     63               "CPDF_Annot::SQUIGGLY value mismatch");
     64 static_assert(static_cast<int>(CPDF_Annot::Subtype::STRIKEOUT) ==
     65                   FPDF_ANNOT_STRIKEOUT,
     66               "CPDF_Annot::STRIKEOUT value mismatch");
     67 static_assert(static_cast<int>(CPDF_Annot::Subtype::STAMP) == FPDF_ANNOT_STAMP,
     68               "CPDF_Annot::STAMP value mismatch");
     69 static_assert(static_cast<int>(CPDF_Annot::Subtype::CARET) == FPDF_ANNOT_CARET,
     70               "CPDF_Annot::CARET value mismatch");
     71 static_assert(static_cast<int>(CPDF_Annot::Subtype::INK) == FPDF_ANNOT_INK,
     72               "CPDF_Annot::INK value mismatch");
     73 static_assert(static_cast<int>(CPDF_Annot::Subtype::POPUP) == FPDF_ANNOT_POPUP,
     74               "CPDF_Annot::POPUP value mismatch");
     75 static_assert(static_cast<int>(CPDF_Annot::Subtype::FILEATTACHMENT) ==
     76                   FPDF_ANNOT_FILEATTACHMENT,
     77               "CPDF_Annot::FILEATTACHMENT value mismatch");
     78 static_assert(static_cast<int>(CPDF_Annot::Subtype::SOUND) == FPDF_ANNOT_SOUND,
     79               "CPDF_Annot::SOUND value mismatch");
     80 static_assert(static_cast<int>(CPDF_Annot::Subtype::MOVIE) == FPDF_ANNOT_MOVIE,
     81               "CPDF_Annot::MOVIE value mismatch");
     82 static_assert(static_cast<int>(CPDF_Annot::Subtype::WIDGET) ==
     83                   FPDF_ANNOT_WIDGET,
     84               "CPDF_Annot::WIDGET value mismatch");
     85 static_assert(static_cast<int>(CPDF_Annot::Subtype::SCREEN) ==
     86                   FPDF_ANNOT_SCREEN,
     87               "CPDF_Annot::SCREEN value mismatch");
     88 static_assert(static_cast<int>(CPDF_Annot::Subtype::PRINTERMARK) ==
     89                   FPDF_ANNOT_PRINTERMARK,
     90               "CPDF_Annot::PRINTERMARK value mismatch");
     91 static_assert(static_cast<int>(CPDF_Annot::Subtype::TRAPNET) ==
     92                   FPDF_ANNOT_TRAPNET,
     93               "CPDF_Annot::TRAPNET value mismatch");
     94 static_assert(static_cast<int>(CPDF_Annot::Subtype::WATERMARK) ==
     95                   FPDF_ANNOT_WATERMARK,
     96               "CPDF_Annot::WATERMARK value mismatch");
     97 static_assert(static_cast<int>(CPDF_Annot::Subtype::THREED) ==
     98                   FPDF_ANNOT_THREED,
     99               "CPDF_Annot::THREED value mismatch");
    100 static_assert(static_cast<int>(CPDF_Annot::Subtype::RICHMEDIA) ==
    101                   FPDF_ANNOT_RICHMEDIA,
    102               "CPDF_Annot::RICHMEDIA value mismatch");
    103 static_assert(static_cast<int>(CPDF_Annot::Subtype::XFAWIDGET) ==
    104                   FPDF_ANNOT_XFAWIDGET,
    105               "CPDF_Annot::XFAWIDGET value mismatch");
    106 
    107 // These checks ensure the consistency of annotation appearance mode values
    108 // across core/ and public.
    109 static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Normal) ==
    110                   FPDF_ANNOT_APPEARANCEMODE_NORMAL,
    111               "CPDF_Annot::AppearanceMode::Normal value mismatch");
    112 static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Rollover) ==
    113                   FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
    114               "CPDF_Annot::AppearanceMode::Rollover value mismatch");
    115 static_assert(static_cast<int>(CPDF_Annot::AppearanceMode::Down) ==
    116                   FPDF_ANNOT_APPEARANCEMODE_DOWN,
    117               "CPDF_Annot::AppearanceMode::Down value mismatch");
    118 
    119 // These checks ensure the consistency of dictionary value types across core/
    120 // and public/.
    121 static_assert(static_cast<int>(CPDF_Object::Type::BOOLEAN) ==
    122                   FPDF_OBJECT_BOOLEAN,
    123               "CPDF_Object::BOOLEAN value mismatch");
    124 static_assert(static_cast<int>(CPDF_Object::Type::NUMBER) == FPDF_OBJECT_NUMBER,
    125               "CPDF_Object::NUMBER value mismatch");
    126 static_assert(static_cast<int>(CPDF_Object::Type::STRING) == FPDF_OBJECT_STRING,
    127               "CPDF_Object::STRING value mismatch");
    128 static_assert(static_cast<int>(CPDF_Object::Type::NAME) == FPDF_OBJECT_NAME,
    129               "CPDF_Object::NAME value mismatch");
    130 static_assert(static_cast<int>(CPDF_Object::Type::ARRAY) == FPDF_OBJECT_ARRAY,
    131               "CPDF_Object::ARRAY value mismatch");
    132 static_assert(static_cast<int>(CPDF_Object::Type::DICTIONARY) ==
    133                   FPDF_OBJECT_DICTIONARY,
    134               "CPDF_Object::DICTIONARY value mismatch");
    135 static_assert(static_cast<int>(CPDF_Object::Type::STREAM) == FPDF_OBJECT_STREAM,
    136               "CPDF_Object::STREAM value mismatch");
    137 static_assert(static_cast<int>(CPDF_Object::Type::NULLOBJ) ==
    138                   FPDF_OBJECT_NULLOBJ,
    139               "CPDF_Object::NULLOBJ value mismatch");
    140 static_assert(static_cast<int>(CPDF_Object::Type::REFERENCE) ==
    141                   FPDF_OBJECT_REFERENCE,
    142               "CPDF_Object::REFERENCE value mismatch");
    143 
    144 class CPDF_AnnotContext {
    145  public:
    146   CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict,
    147                     CPDF_Page* pPage,
    148                     CPDF_Stream* pStream)
    149       : m_pAnnotDict(pAnnotDict), m_pPage(pPage) {
    150     SetForm(pStream);
    151   }
    152   ~CPDF_AnnotContext() {}
    153 
    154   bool HasForm() const { return !!m_pAnnotForm; }
    155 
    156   void SetForm(CPDF_Stream* pStream) {
    157     if (!pStream)
    158       return;
    159 
    160     // Reset the annotation matrix to be the identity matrix, since the
    161     // appearance stream already takes matrix into account.
    162     pStream->GetDict()->SetMatrixFor("Matrix", CFX_Matrix());
    163 
    164     m_pAnnotForm = pdfium::MakeUnique<CPDF_Form>(
    165         m_pPage->m_pDocument.Get(), m_pPage->m_pResources.Get(), pStream);
    166     m_pAnnotForm->ParseContent();
    167   }
    168 
    169   CPDF_Form* GetForm() const { return m_pAnnotForm.get(); }
    170   CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
    171   CPDF_Page* GetPage() const { return m_pPage.Get(); }
    172 
    173  private:
    174   std::unique_ptr<CPDF_Form> m_pAnnotForm;
    175   UnownedPtr<CPDF_Dictionary> m_pAnnotDict;
    176   UnownedPtr<CPDF_Page> m_pPage;
    177 };
    178 
    179 CPDF_AnnotContext* CPDFAnnotContextFromFPDFAnnotation(FPDF_ANNOTATION annot) {
    180   return static_cast<CPDF_AnnotContext*>(annot);
    181 }
    182 
    183 bool HasAPStream(const CPDF_Dictionary* pAnnotDict) {
    184   return !!FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
    185 }
    186 
    187 void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) {
    188   ASSERT(pForm);
    189   ASSERT(pStream);
    190 
    191   CPDF_PageContentGenerator generator(pForm);
    192   std::ostringstream buf;
    193   generator.ProcessPageObjects(&buf);
    194   pStream->SetDataAndRemoveFilter(&buf);
    195 }
    196 
    197 }  // namespace
    198 
    199 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    200 FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
    201   // The supported subtypes must also be communicated in the user doc.
    202   return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_HIGHLIGHT ||
    203          subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_POPUP ||
    204          subtype == FPDF_ANNOT_SQUARE || subtype == FPDF_ANNOT_SQUIGGLY ||
    205          subtype == FPDF_ANNOT_STAMP || subtype == FPDF_ANNOT_STRIKEOUT ||
    206          subtype == FPDF_ANNOT_TEXT || subtype == FPDF_ANNOT_UNDERLINE;
    207 }
    208 
    209 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
    210 FPDFPage_CreateAnnot(FPDF_PAGE page, FPDF_ANNOTATION_SUBTYPE subtype) {
    211   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    212   if (!pPage || !FPDFAnnot_IsSupportedSubtype(subtype))
    213     return nullptr;
    214 
    215   auto pDict = pdfium::MakeUnique<CPDF_Dictionary>(
    216       pPage->m_pDocument->GetByteStringPool());
    217   pDict->SetNewFor<CPDF_Name>("Type", "Annot");
    218   pDict->SetNewFor<CPDF_Name>("Subtype",
    219                               CPDF_Annot::AnnotSubtypeToString(
    220                                   static_cast<CPDF_Annot::Subtype>(subtype)));
    221   auto pNewAnnot =
    222       pdfium::MakeUnique<CPDF_AnnotContext>(pDict.get(), pPage, nullptr);
    223 
    224   CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayFor("Annots");
    225   if (!pAnnotList)
    226     pAnnotList = pPage->m_pFormDict->SetNewFor<CPDF_Array>("Annots");
    227 
    228   pAnnotList->Add(std::move(pDict));
    229   return pNewAnnot.release();
    230 }
    231 
    232 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotCount(FPDF_PAGE page) {
    233   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    234   if (!pPage || !pPage->m_pFormDict)
    235     return 0;
    236 
    237   CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
    238   return pAnnots ? pAnnots->GetCount() : 0;
    239 }
    240 
    241 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV FPDFPage_GetAnnot(FPDF_PAGE page,
    242                                                             int index) {
    243   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    244   if (!pPage || !pPage->m_pFormDict || index < 0)
    245     return nullptr;
    246 
    247   CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
    248   if (!pAnnots || static_cast<size_t>(index) >= pAnnots->GetCount())
    249     return nullptr;
    250 
    251   CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(index));
    252   auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict, pPage, nullptr);
    253   return pNewAnnot.release();
    254 }
    255 
    256 FPDF_EXPORT int FPDF_CALLCONV FPDFPage_GetAnnotIndex(FPDF_PAGE page,
    257                                                      FPDF_ANNOTATION annot) {
    258   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    259   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    260   if (!pPage || !pPage->m_pFormDict || !pAnnot || !pAnnot->GetAnnotDict())
    261     return -1;
    262 
    263   CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
    264   if (!pAnnots)
    265     return -1;
    266 
    267   CPDF_Dictionary* pDict = pAnnot->GetAnnotDict();
    268   auto it =
    269       std::find_if(pAnnots->begin(), pAnnots->end(),
    270                    [pDict](const std::unique_ptr<CPDF_Object>& candidate) {
    271                      return candidate->GetDirect() == pDict;
    272                    });
    273 
    274   if (it == pAnnots->end())
    275     return -1;
    276 
    277   return it - pAnnots->begin();
    278 }
    279 
    280 FPDF_EXPORT void FPDF_CALLCONV FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) {
    281   delete CPDFAnnotContextFromFPDFAnnotation(annot);
    282 }
    283 
    284 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFPage_RemoveAnnot(FPDF_PAGE page,
    285                                                          int index) {
    286   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    287   if (!pPage || !pPage->m_pFormDict || index < 0)
    288     return false;
    289 
    290   CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
    291   if (!pAnnots || static_cast<size_t>(index) >= pAnnots->GetCount())
    292     return false;
    293 
    294   pAnnots->RemoveAt(index);
    295   return true;
    296 }
    297 
    298 FPDF_EXPORT FPDF_ANNOTATION_SUBTYPE FPDF_CALLCONV
    299 FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) {
    300   if (!annot)
    301     return FPDF_ANNOT_UNKNOWN;
    302 
    303   CPDF_Dictionary* pAnnotDict =
    304       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    305   if (!pAnnotDict)
    306     return FPDF_ANNOT_UNKNOWN;
    307 
    308   return static_cast<FPDF_ANNOTATION_SUBTYPE>(
    309       CPDF_Annot::StringToAnnotSubtype(pAnnotDict->GetStringFor("Subtype")));
    310 }
    311 
    312 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    313 FPDFAnnot_IsObjectSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
    314   // The supported subtypes must also be communicated in the user doc.
    315   return subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_STAMP;
    316 }
    317 
    318 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    319 FPDFAnnot_UpdateObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
    320   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    321   CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
    322   if (!pAnnot || !pAnnot->GetAnnotDict() || !pAnnot->HasForm() || !pObj)
    323     return false;
    324 
    325   // Check that the annotation type is supported by this method.
    326   if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
    327     return false;
    328 
    329   // Check that the annotation already has an appearance stream, since an
    330   // existing object is to be updated.
    331   CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(),
    332                                             CPDF_Annot::AppearanceMode::Normal);
    333   if (!pStream)
    334     return false;
    335 
    336   // Check that the object is already in this annotation's object list.
    337   CPDF_Form* pForm = pAnnot->GetForm();
    338   CPDF_PageObjectList* pObjList = pForm->GetPageObjectList();
    339   auto it =
    340       std::find_if(pObjList->begin(), pObjList->end(),
    341                    [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
    342                      return candidate.get() == pObj;
    343                    });
    344   if (it == pObjList->end())
    345     return false;
    346 
    347   // Update the content stream data in the annotation's AP stream.
    348   UpdateContentStream(pForm, pStream);
    349   return true;
    350 }
    351 
    352 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    353 FPDFAnnot_AppendObject(FPDF_ANNOTATION annot, FPDF_PAGEOBJECT obj) {
    354   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    355   CPDF_PageObject* pObj = CPDFPageObjectFromFPDFPageObject(obj);
    356   if (!pAnnot || !pObj)
    357     return false;
    358 
    359   CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict();
    360   CPDF_Page* pPage = pAnnot->GetPage();
    361   if (!pAnnotDict || !pPage)
    362     return false;
    363 
    364   // Check that the annotation type is supported by this method.
    365   if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
    366     return false;
    367 
    368   // If the annotation does not have an AP stream yet, generate and set it.
    369   CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(),
    370                                             CPDF_Annot::AppearanceMode::Normal);
    371   if (!pStream) {
    372     CPVT_GenerateAP::GenerateEmptyAP(pPage->m_pDocument.Get(), pAnnotDict);
    373     pStream =
    374         FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
    375     if (!pStream)
    376       return false;
    377   }
    378 
    379   // Get the annotation's corresponding form object for parsing its AP stream.
    380   if (!pAnnot->HasForm())
    381     pAnnot->SetForm(pStream);
    382 
    383   // Check that the object did not come from the same annotation. If this check
    384   // succeeds, then it is assumed that the object came from
    385   // FPDFPageObj_CreateNew{Path|Rect}() or FPDFPageObj_New{Text|Image}Obj().
    386   // Note that an object that came from a different annotation must not be
    387   // passed here, since an object cannot belong to more than one annotation.
    388   CPDF_Form* pForm = pAnnot->GetForm();
    389   CPDF_PageObjectList* pObjList = pForm->GetPageObjectList();
    390   auto it =
    391       std::find_if(pObjList->begin(), pObjList->end(),
    392                    [pObj](const std::unique_ptr<CPDF_PageObject>& candidate) {
    393                      return candidate.get() == pObj;
    394                    });
    395   if (it != pObjList->end())
    396     return false;
    397 
    398   // Append the object to the object list.
    399   std::unique_ptr<CPDF_PageObject> pPageObjHolder(pObj);
    400   pObjList->push_back(std::move(pPageObjHolder));
    401 
    402   // Set the content stream data in the annotation's AP stream.
    403   UpdateContentStream(pForm, pStream);
    404   return true;
    405 }
    406 
    407 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetObjectCount(FPDF_ANNOTATION annot) {
    408   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    409   if (!pAnnot || !pAnnot->GetAnnotDict())
    410     return 0;
    411 
    412   if (!pAnnot->HasForm()) {
    413     CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(
    414         pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
    415     if (!pStream)
    416       return 0;
    417 
    418     pAnnot->SetForm(pStream);
    419   }
    420   return pdfium::CollectionSize<int>(*pAnnot->GetForm()->GetPageObjectList());
    421 }
    422 
    423 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
    424 FPDFAnnot_GetObject(FPDF_ANNOTATION annot, int index) {
    425   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    426   if (!pAnnot || !pAnnot->GetAnnotDict() || index < 0)
    427     return nullptr;
    428 
    429   if (!pAnnot->HasForm()) {
    430     CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(
    431         pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal);
    432     if (!pStream)
    433       return nullptr;
    434 
    435     pAnnot->SetForm(pStream);
    436   }
    437 
    438   return pAnnot->GetForm()->GetPageObjectList()->GetPageObjectByIndex(index);
    439 }
    440 
    441 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    442 FPDFAnnot_RemoveObject(FPDF_ANNOTATION annot, int index) {
    443   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    444   if (!pAnnot || !pAnnot->GetAnnotDict() || !pAnnot->HasForm() || index < 0)
    445     return false;
    446 
    447   // Check that the annotation type is supported by this method.
    448   if (!FPDFAnnot_IsObjectSupportedSubtype(FPDFAnnot_GetSubtype(annot)))
    449     return false;
    450 
    451   // Check that the annotation already has an appearance stream, since an
    452   // existing object is to be deleted.
    453   CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(),
    454                                             CPDF_Annot::AppearanceMode::Normal);
    455   if (!pStream)
    456     return false;
    457 
    458   CPDF_PageObjectList* pObjList = pAnnot->GetForm()->GetPageObjectList();
    459   if (static_cast<size_t>(index) >= pObjList->size())
    460     return false;
    461 
    462   pObjList->erase(pObjList->begin() + index);
    463   UpdateContentStream(pAnnot->GetForm(), pStream);
    464   return true;
    465 }
    466 
    467 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetColor(FPDF_ANNOTATION annot,
    468                                                        FPDFANNOT_COLORTYPE type,
    469                                                        unsigned int R,
    470                                                        unsigned int G,
    471                                                        unsigned int B,
    472                                                        unsigned int A) {
    473   if (!annot || R > 255 || G > 255 || B > 255 || A > 255)
    474     return false;
    475 
    476   CPDF_Dictionary* pAnnotDict =
    477       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    478   if (!pAnnotDict)
    479     return false;
    480 
    481   // For annotations with their appearance streams already defined, the path
    482   // stream's own color definitions take priority over the annotation color
    483   // definitions set by this method, hence this method will simply fail.
    484   if (HasAPStream(pAnnotDict))
    485     return false;
    486 
    487   // Set the opacity of the annotation.
    488   pAnnotDict->SetNewFor<CPDF_Number>("CA", A / 255.f);
    489 
    490   // Set the color of the annotation.
    491   ByteString key = type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C";
    492   CPDF_Array* pColor = pAnnotDict->GetArrayFor(key);
    493   if (pColor)
    494     pColor->Clear();
    495   else
    496     pColor = pAnnotDict->SetNewFor<CPDF_Array>(key);
    497 
    498   pColor->AddNew<CPDF_Number>(R / 255.f);
    499   pColor->AddNew<CPDF_Number>(G / 255.f);
    500   pColor->AddNew<CPDF_Number>(B / 255.f);
    501 
    502   return true;
    503 }
    504 
    505 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot,
    506                                                        FPDFANNOT_COLORTYPE type,
    507                                                        unsigned int* R,
    508                                                        unsigned int* G,
    509                                                        unsigned int* B,
    510                                                        unsigned int* A) {
    511   if (!annot || !R || !G || !B || !A)
    512     return false;
    513 
    514   CPDF_Dictionary* pAnnotDict =
    515       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    516   if (!pAnnotDict)
    517     return false;
    518 
    519   // For annotations with their appearance streams already defined, the path
    520   // stream's own color definitions take priority over the annotation color
    521   // definitions retrieved by this method, hence this method will simply fail.
    522   if (HasAPStream(pAnnotDict))
    523     return false;
    524 
    525   CPDF_Array* pColor = pAnnotDict->GetArrayFor(
    526       type == FPDFANNOT_COLORTYPE_InteriorColor ? "IC" : "C");
    527   *A =
    528       (pAnnotDict->KeyExist("CA") ? pAnnotDict->GetNumberFor("CA") : 1) * 255.f;
    529   if (!pColor) {
    530     // Use default color. The default colors must be consistent with the ones
    531     // used to generate AP. See calls to GetColorStringWithDefault() in
    532     // CPVT_GenerateAP::Generate*AP().
    533     if (pAnnotDict->GetStringFor("Subtype") == "Highlight") {
    534       *R = 255;
    535       *G = 255;
    536       *B = 0;
    537     } else {
    538       *R = 0;
    539       *G = 0;
    540       *B = 0;
    541     }
    542     return true;
    543   }
    544 
    545   CFX_Color color = CFX_Color::ParseColor(*pColor);
    546   switch (color.nColorType) {
    547     case CFX_Color::kRGB:
    548       *R = color.fColor1 * 255.f;
    549       *G = color.fColor2 * 255.f;
    550       *B = color.fColor3 * 255.f;
    551       break;
    552     case CFX_Color::kGray:
    553       *R = 255.f * color.fColor1;
    554       *G = 255.f * color.fColor1;
    555       *B = 255.f * color.fColor1;
    556       break;
    557     case CFX_Color::kCMYK:
    558       *R = 255.f * (1 - color.fColor1) * (1 - color.fColor4);
    559       *G = 255.f * (1 - color.fColor2) * (1 - color.fColor4);
    560       *B = 255.f * (1 - color.fColor3) * (1 - color.fColor4);
    561       break;
    562     case CFX_Color::kTransparent:
    563       *R = 0;
    564       *G = 0;
    565       *B = 0;
    566       break;
    567   }
    568   return true;
    569 }
    570 
    571 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    572 FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot) {
    573   if (!annot)
    574     return false;
    575 
    576   FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
    577   return subtype == FPDF_ANNOT_LINK || subtype == FPDF_ANNOT_HIGHLIGHT ||
    578          subtype == FPDF_ANNOT_UNDERLINE || subtype == FPDF_ANNOT_SQUIGGLY ||
    579          subtype == FPDF_ANNOT_STRIKEOUT;
    580 }
    581 
    582 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    583 FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
    584                               const FS_QUADPOINTSF* quadPoints) {
    585   if (!annot || !quadPoints || !FPDFAnnot_HasAttachmentPoints(annot))
    586     return false;
    587 
    588   CPDF_Dictionary* pAnnotDict =
    589       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    590   if (!pAnnotDict)
    591     return false;
    592 
    593   // Update the "QuadPoints" entry in the annotation dictionary.
    594   CPDF_Array* pQuadPoints = pAnnotDict->GetArrayFor("QuadPoints");
    595   if (pQuadPoints)
    596     pQuadPoints->Clear();
    597   else
    598     pQuadPoints = pAnnotDict->SetNewFor<CPDF_Array>("QuadPoints");
    599 
    600   pQuadPoints->AddNew<CPDF_Number>(quadPoints->x1);
    601   pQuadPoints->AddNew<CPDF_Number>(quadPoints->y1);
    602   pQuadPoints->AddNew<CPDF_Number>(quadPoints->x2);
    603   pQuadPoints->AddNew<CPDF_Number>(quadPoints->y2);
    604   pQuadPoints->AddNew<CPDF_Number>(quadPoints->x3);
    605   pQuadPoints->AddNew<CPDF_Number>(quadPoints->y3);
    606   pQuadPoints->AddNew<CPDF_Number>(quadPoints->x4);
    607   pQuadPoints->AddNew<CPDF_Number>(quadPoints->y4);
    608 
    609   // If the annotation's appearance stream is defined, and the new quadpoints
    610   // defines a bigger bounding box than the appearance stream currently
    611   // specifies, then update the "BBox" entry in the AP dictionary too, since it
    612   // comes from annotation dictionary's "QuadPoints" entry.
    613   CPDF_Stream* pStream =
    614       FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
    615   if (pStream) {
    616     CFX_FloatRect newRect = CPDF_Annot::RectFromQuadPoints(pAnnotDict);
    617     if (newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
    618       pStream->GetDict()->SetRectFor("BBox", newRect);
    619   }
    620   return true;
    621 }
    622 
    623 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    624 FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
    625                               FS_QUADPOINTSF* quadPoints) {
    626   if (!annot || !FPDFAnnot_HasAttachmentPoints(annot) || !quadPoints)
    627     return false;
    628 
    629   CPDF_Dictionary* pAnnotDict =
    630       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    631   if (!pAnnotDict)
    632     return false;
    633 
    634   CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
    635   if (!pArray)
    636     return false;
    637 
    638   quadPoints->x1 = pArray->GetNumberAt(0);
    639   quadPoints->y1 = pArray->GetNumberAt(1);
    640   quadPoints->x2 = pArray->GetNumberAt(2);
    641   quadPoints->y2 = pArray->GetNumberAt(3);
    642   quadPoints->x3 = pArray->GetNumberAt(4);
    643   quadPoints->y3 = pArray->GetNumberAt(5);
    644   quadPoints->x4 = pArray->GetNumberAt(6);
    645   quadPoints->y4 = pArray->GetNumberAt(7);
    646   return true;
    647 }
    648 
    649 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
    650                                                       const FS_RECTF* rect) {
    651   if (!annot || !rect)
    652     return false;
    653 
    654   CPDF_Dictionary* pAnnotDict =
    655       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    656   if (!pAnnotDict)
    657     return false;
    658 
    659   CFX_FloatRect newRect = CFXFloatRectFromFSRECTF(*rect);
    660 
    661   // Update the "Rect" entry in the annotation dictionary.
    662   pAnnotDict->SetRectFor("Rect", newRect);
    663 
    664   // If the annotation's appearance stream is defined, the annotation is of a
    665   // type that does not have quadpoints, and the new rectangle is bigger than
    666   // the current bounding box, then update the "BBox" entry in the AP
    667   // dictionary too, since its "BBox" entry comes from annotation dictionary's
    668   // "Rect" entry.
    669   if (FPDFAnnot_HasAttachmentPoints(annot))
    670     return true;
    671 
    672   CPDF_Stream* pStream =
    673       FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
    674   if (pStream && newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
    675     pStream->GetDict()->SetRectFor("BBox", newRect);
    676   return true;
    677 }
    678 
    679 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetRect(FPDF_ANNOTATION annot,
    680                                                       FS_RECTF* rect) {
    681   if (!annot || !rect)
    682     return false;
    683 
    684   CPDF_Dictionary* pAnnotDict =
    685       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    686   if (!pAnnotDict)
    687     return false;
    688 
    689   FSRECTFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"), rect);
    690   return true;
    691 }
    692 
    693 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
    694                                                      FPDF_BYTESTRING key) {
    695   if (!annot)
    696     return false;
    697 
    698   CPDF_Dictionary* pAnnotDict =
    699       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    700   if (!pAnnotDict)
    701     return false;
    702 
    703   return pAnnotDict->KeyExist(key);
    704 }
    705 
    706 FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
    707 FPDFAnnot_GetValueType(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
    708   if (!FPDFAnnot_HasKey(annot, key))
    709     return FPDF_OBJECT_UNKNOWN;
    710 
    711   auto* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    712   CPDF_Object* pObj = pAnnot->GetAnnotDict()->GetObjectFor(key);
    713   return pObj ? pObj->GetType() : FPDF_OBJECT_UNKNOWN;
    714 }
    715 
    716 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    717 FPDFAnnot_SetStringValue(FPDF_ANNOTATION annot,
    718                          FPDF_BYTESTRING key,
    719                          FPDF_WIDESTRING value) {
    720   if (!annot)
    721     return false;
    722 
    723   CPDF_Dictionary* pAnnotDict =
    724       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    725   if (!pAnnotDict)
    726     return false;
    727 
    728   pAnnotDict->SetNewFor<CPDF_String>(
    729       key, CFXByteStringFromFPDFWideString(value), false);
    730   return true;
    731 }
    732 
    733 FPDF_EXPORT unsigned long FPDF_CALLCONV
    734 FPDFAnnot_GetStringValue(FPDF_ANNOTATION annot,
    735                          FPDF_BYTESTRING key,
    736                          void* buffer,
    737                          unsigned long buflen) {
    738   if (!annot)
    739     return 0;
    740 
    741   CPDF_Dictionary* pAnnotDict =
    742       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    743   if (!pAnnotDict)
    744     return 0;
    745 
    746   return Utf16EncodeMaybeCopyAndReturnLength(pAnnotDict->GetUnicodeTextFor(key),
    747                                              buffer, buflen);
    748 }
    749 
    750 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
    751 FPDFAnnot_SetAP(FPDF_ANNOTATION annot,
    752                 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
    753                 FPDF_WIDESTRING value) {
    754   if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
    755     return false;
    756 
    757   if (!annot)
    758     return false;
    759 
    760   CPDF_Dictionary* pAnnotDict =
    761       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    762   if (!pAnnotDict)
    763     return false;
    764 
    765   constexpr const char* modeKeyForMode[] = {"N", "R", "D"};
    766   static_assert(FX_ArraySize(modeKeyForMode) == FPDF_ANNOT_APPEARANCEMODE_COUNT,
    767                 "length of modeKeyForMode should be equal to "
    768                 "FPDF_ANNOT_APPEARANCEMODE_COUNT");
    769   const char* modeKey = modeKeyForMode[appearanceMode];
    770 
    771   CPDF_Dictionary* pApDict = pAnnotDict->GetDictFor("AP");
    772 
    773   // If value is null, we're in remove mode. Otherwise, we're in add/update
    774   // mode.
    775   if (value) {
    776     if (!pApDict)
    777       pApDict = pAnnotDict->SetNewFor<CPDF_Dictionary>("AP");
    778 
    779     ByteString newValue = CFXByteStringFromFPDFWideString(value);
    780     auto pNewApStream = pdfium::MakeUnique<CPDF_Stream>();
    781     pNewApStream->SetData(newValue.raw_str(), newValue.GetLength());
    782     pApDict->SetFor(modeKey, std::move(pNewApStream));
    783   } else {
    784     if (pApDict) {
    785       if (appearanceMode == FPDF_ANNOT_APPEARANCEMODE_NORMAL)
    786         pAnnotDict->RemoveFor("AP");
    787       else
    788         pApDict->RemoveFor(modeKey);
    789     }
    790   }
    791 
    792   return true;
    793 }
    794 
    795 FPDF_EXPORT unsigned long FPDF_CALLCONV
    796 FPDFAnnot_GetAP(FPDF_ANNOTATION annot,
    797                 FPDF_ANNOT_APPEARANCEMODE appearanceMode,
    798                 void* buffer,
    799                 unsigned long buflen) {
    800   if (appearanceMode < 0 || appearanceMode >= FPDF_ANNOT_APPEARANCEMODE_COUNT)
    801     return 0;
    802 
    803   if (!annot)
    804     return 0;
    805 
    806   CPDF_Dictionary* pAnnotDict =
    807       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    808   if (!pAnnotDict)
    809     return 0;
    810 
    811   CPDF_Annot::AppearanceMode mode =
    812       static_cast<CPDF_Annot::AppearanceMode>(appearanceMode);
    813 
    814   CPDF_Stream* pStream = FPDFDOC_GetAnnotAPNoFallback(pAnnotDict, mode);
    815   return Utf16EncodeMaybeCopyAndReturnLength(
    816       pStream ? pStream->GetUnicodeText() : L"", buffer, buflen);
    817 }
    818 
    819 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
    820 FPDFAnnot_GetLinkedAnnot(FPDF_ANNOTATION annot, FPDF_BYTESTRING key) {
    821   CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot);
    822   if (!pAnnot || !pAnnot->GetAnnotDict())
    823     return nullptr;
    824 
    825   CPDF_Dictionary* pLinkedDict = pAnnot->GetAnnotDict()->GetDictFor(key);
    826   if (!pLinkedDict || pLinkedDict->GetStringFor("Type") != "Annot")
    827     return nullptr;
    828 
    829   auto pLinkedAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(
    830       pLinkedDict, pAnnot->GetPage(), nullptr);
    831   return pLinkedAnnot.release();
    832 }
    833 
    834 FPDF_EXPORT int FPDF_CALLCONV FPDFAnnot_GetFlags(FPDF_ANNOTATION annot) {
    835   if (!annot)
    836     return FPDF_ANNOT_FLAG_NONE;
    837 
    838   CPDF_Dictionary* pAnnotDict =
    839       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    840   return pAnnotDict ? pAnnotDict->GetIntegerFor("F") : FPDF_ANNOT_FLAG_NONE;
    841 }
    842 
    843 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetFlags(FPDF_ANNOTATION annot,
    844                                                        int flags) {
    845   if (!annot)
    846     return false;
    847 
    848   CPDF_Dictionary* pAnnotDict =
    849       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    850   if (!pAnnotDict)
    851     return false;
    852 
    853   pAnnotDict->SetNewFor<CPDF_Number>("F", flags);
    854   return true;
    855 }
    856 
    857 FPDF_EXPORT int FPDF_CALLCONV
    858 FPDFAnnot_GetFormFieldFlags(FPDF_PAGE page, FPDF_ANNOTATION annot) {
    859   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    860   if (!pPage || !annot)
    861     return FPDF_FORMFLAG_NONE;
    862 
    863   CPDF_Dictionary* pAnnotDict =
    864       CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
    865   if (!pAnnotDict)
    866     return FPDF_FORMFLAG_NONE;
    867 
    868   CPDF_InterForm interform(pPage->m_pDocument.Get());
    869   CPDF_FormField* pFormField = interform.GetFieldByDict(pAnnotDict);
    870   return pFormField ? pFormField->GetFieldFlags() : FPDF_FORMFLAG_NONE;
    871 }
    872 
    873 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
    874 FPDFAnnot_GetFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
    875                               FPDF_PAGE page,
    876                               double page_x,
    877                               double page_y) {
    878   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
    879   if (!hHandle || !pPage)
    880     return nullptr;
    881 
    882   CPDF_InterForm interform(pPage->m_pDocument.Get());
    883   int annot_index = -1;
    884   CPDF_FormControl* pFormCtrl = interform.GetControlAtPoint(
    885       pPage, CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
    886       &annot_index);
    887   if (!pFormCtrl || annot_index == -1)
    888     return nullptr;
    889   return FPDFPage_GetAnnot(page, annot_index);
    890 }
    891