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 <cwchar>
      6 #include <memory>
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "core/fxcrt/fx_system.h"
     11 #include "public/fpdf_annot.h"
     12 #include "public/fpdf_edit.h"
     13 #include "public/fpdfview.h"
     14 #include "testing/embedder_test.h"
     15 #include "testing/gmock/include/gmock/gmock-matchers.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 static constexpr char kContentsKey[] = "Contents";
     19 
     20 class FPDFAnnotEmbeddertest : public EmbedderTest {};
     21 
     22 std::wstring BufferToWString(const std::vector<char>& buf) {
     23   return GetPlatformWString(reinterpret_cast<FPDF_WIDESTRING>(buf.data()));
     24 }
     25 
     26 std::string BufferToString(const std::vector<char>& buf) {
     27   return GetPlatformString(reinterpret_cast<FPDF_WIDESTRING>(buf.data()));
     28 }
     29 
     30 TEST_F(FPDFAnnotEmbeddertest, RenderAnnotWithOnlyRolloverAP) {
     31   // Open a file with one annotation and load its first page.
     32   ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
     33   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
     34   ASSERT_TRUE(page);
     35 
     36   // This annotation has a malformed appearance stream, which does not have its
     37   // normal appearance defined, only its rollover appearance. In this case, its
     38   // normal appearance should be generated, allowing the highlight annotation to
     39   // still display.
     40   FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle(), FPDF_ANNOT);
     41   CompareBitmap(bitmap, 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
     42   FPDFBitmap_Destroy(bitmap);
     43 
     44   UnloadPage(page);
     45 }
     46 
     47 TEST_F(FPDFAnnotEmbeddertest, ExtractHighlightLongContent) {
     48   // Open a file with one annotation and load its first page.
     49   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
     50   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
     51   ASSERT_TRUE(page);
     52 
     53   // Check that there is a total of 1 annotation on its first page.
     54   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
     55 
     56   // Check that the annotation is of type "highlight".
     57   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
     58   ASSERT_TRUE(annot);
     59   EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
     60 
     61   // Check that the annotation color is yellow.
     62   unsigned int R;
     63   unsigned int G;
     64   unsigned int B;
     65   unsigned int A;
     66   EXPECT_TRUE(
     67       FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
     68   EXPECT_EQ(255u, R);
     69   EXPECT_EQ(255u, G);
     70   EXPECT_EQ(0u, B);
     71   EXPECT_EQ(255u, A);
     72 
     73   // Check that the author is correct.
     74   static constexpr char kAuthorKey[] = "T";
     75   EXPECT_EQ(FPDF_OBJECT_STRING, FPDFAnnot_GetValueType(annot, kAuthorKey));
     76   unsigned long len = FPDFAnnot_GetStringValue(annot, kAuthorKey, nullptr, 0);
     77   std::vector<char> buf(len);
     78   EXPECT_EQ(28u, FPDFAnnot_GetStringValue(annot, kAuthorKey, buf.data(), len));
     79   EXPECT_STREQ(L"Jae Hyun Park", BufferToWString(buf).c_str());
     80 
     81   // Check that the content is correct.
     82   EXPECT_EQ(FPDF_OBJECT_STRING, FPDFAnnot_GetValueType(annot, kContentsKey));
     83   len = FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0);
     84   buf.clear();
     85   buf.resize(len);
     86   EXPECT_EQ(2690u,
     87             FPDFAnnot_GetStringValue(annot, kContentsKey, buf.data(), len));
     88   const wchar_t contents[] =
     89       L"This is a note for that highlight annotation. Very long highlight "
     90       "annotation. Long long long Long long longLong long longLong long "
     91       "longLong long longLong long longLong long longLong long longLong long "
     92       "longLong long longLong long longLong long longLong long longLong long "
     93       "longLong long longLong long longLong long longLong long longLong long "
     94       "longLong long longLong long longLong long longLong long longLong long "
     95       "longLong long longLong long longLong long longLong long longLong long "
     96       "longLong long longLong long longLong long longLong long longLong long "
     97       "longLong long longLong long longLong long longLong long longLong long "
     98       "longLong long longLong long longLong long longLong long longLong long "
     99       "longLong long longLong long longLong long longLong long longLong long "
    100       "longLong long longLong long longLong long longLong long longLong long "
    101       "longLong long longLong long longLong long longLong long longLong long "
    102       "longLong long longLong long longLong long longLong long longLong long "
    103       "longLong long longLong long longLong long longLong long longLong long "
    104       "longLong long longLong long longLong long longLong long longLong long "
    105       "longLong long longLong long longLong long longLong long longLong long "
    106       "longLong long longLong long longLong long longLong long longLong long "
    107       "longLong long longLong long longLong long longLong long longLong long "
    108       "longLong long long. END";
    109   EXPECT_STREQ(contents, BufferToWString(buf).c_str());
    110 
    111   // Check that the quadpoints are correct.
    112   FS_QUADPOINTSF quadpoints;
    113   ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &quadpoints));
    114   EXPECT_EQ(115.802643f, quadpoints.x1);
    115   EXPECT_EQ(718.913940f, quadpoints.y1);
    116   EXPECT_EQ(157.211182f, quadpoints.x4);
    117   EXPECT_EQ(706.264465f, quadpoints.y4);
    118 
    119   FPDFPage_CloseAnnot(annot);
    120   UnloadPage(page);
    121 }
    122 
    123 TEST_F(FPDFAnnotEmbeddertest, ExtractInkMultiple) {
    124   // Open a file with three annotations and load its first page.
    125   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
    126   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    127   ASSERT_TRUE(page);
    128 
    129   // Check that there is a total of 3 annotation on its first page.
    130   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
    131 
    132   // Check that the third annotation is of type "ink".
    133   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 2);
    134   ASSERT_TRUE(annot);
    135   EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot));
    136 
    137   // Check that the annotation color is blue with opacity.
    138   unsigned int R;
    139   unsigned int G;
    140   unsigned int B;
    141   unsigned int A;
    142   EXPECT_TRUE(
    143       FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
    144   EXPECT_EQ(0u, R);
    145   EXPECT_EQ(0u, G);
    146   EXPECT_EQ(255u, B);
    147   EXPECT_EQ(76u, A);
    148 
    149   // Check that there is no content.
    150   EXPECT_EQ(2u, FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0));
    151 
    152   // Check that the rectange coordinates are correct.
    153   // Note that upon rendering, the rectangle coordinates will be adjusted.
    154   FS_RECTF rect;
    155   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    156   EXPECT_EQ(351.820404f, rect.left);
    157   EXPECT_EQ(583.830688f, rect.bottom);
    158   EXPECT_EQ(475.336090f, rect.right);
    159   EXPECT_EQ(681.535034f, rect.top);
    160 
    161   FPDFPage_CloseAnnot(annot);
    162   UnloadPage(page);
    163 }
    164 
    165 TEST_F(FPDFAnnotEmbeddertest, AddIllegalSubtypeAnnotation) {
    166   // Open a file with one annotation and load its first page.
    167   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
    168   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    169   ASSERT_TRUE(page);
    170 
    171   // Add an annotation with an illegal subtype.
    172   ASSERT_FALSE(FPDFPage_CreateAnnot(page, -1));
    173 
    174   UnloadPage(page);
    175 }
    176 
    177 TEST_F(FPDFAnnotEmbeddertest, AddFirstTextAnnotation) {
    178   // Open a file with no annotation and load its first page.
    179   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
    180   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    181   ASSERT_TRUE(page);
    182   EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
    183 
    184   // Add a text annotation to the page.
    185   FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_TEXT);
    186   ASSERT_TRUE(annot);
    187 
    188   // Check that there is now 1 annotations on this page.
    189   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
    190 
    191   // Check that the subtype of the annotation is correct.
    192   EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot));
    193   FPDFPage_CloseAnnot(annot);
    194 
    195   annot = FPDFPage_GetAnnot(page, 0);
    196   ASSERT_TRUE(annot);
    197   EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot));
    198 
    199   // Set the color of the annotation.
    200   ASSERT_TRUE(
    201       FPDFAnnot_SetColor(annot, FPDFANNOT_COLORTYPE_Color, 51, 102, 153, 204));
    202   // Check that the color has been set correctly.
    203   unsigned int R;
    204   unsigned int G;
    205   unsigned int B;
    206   unsigned int A;
    207   EXPECT_TRUE(
    208       FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
    209   EXPECT_EQ(51u, R);
    210   EXPECT_EQ(102u, G);
    211   EXPECT_EQ(153u, B);
    212   EXPECT_EQ(204u, A);
    213 
    214   // Change the color of the annotation.
    215   ASSERT_TRUE(
    216       FPDFAnnot_SetColor(annot, FPDFANNOT_COLORTYPE_Color, 204, 153, 102, 51));
    217   // Check that the color has been set correctly.
    218   EXPECT_TRUE(
    219       FPDFAnnot_GetColor(annot, FPDFANNOT_COLORTYPE_Color, &R, &G, &B, &A));
    220   EXPECT_EQ(204u, R);
    221   EXPECT_EQ(153u, G);
    222   EXPECT_EQ(102u, B);
    223   EXPECT_EQ(51u, A);
    224 
    225   // Set the annotation rectangle.
    226   FS_RECTF rect;
    227   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    228   EXPECT_EQ(0.f, rect.left);
    229   EXPECT_EQ(0.f, rect.right);
    230   rect.left = 35;
    231   rect.bottom = 150;
    232   rect.right = 53;
    233   rect.top = 165;
    234   ASSERT_TRUE(FPDFAnnot_SetRect(annot, &rect));
    235   // Check that the annotation rectangle has been set correctly.
    236   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    237   EXPECT_EQ(35.f, rect.left);
    238   EXPECT_EQ(150.f, rect.bottom);
    239   EXPECT_EQ(53.f, rect.right);
    240   EXPECT_EQ(165.f, rect.top);
    241 
    242   // Set the content of the annotation.
    243   static constexpr wchar_t contents[] = L"Hello! This is a customized content.";
    244   std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
    245       GetFPDFWideString(contents);
    246   ASSERT_TRUE(FPDFAnnot_SetStringValue(annot, kContentsKey, text.get()));
    247   // Check that the content has been set correctly.
    248   unsigned long len = FPDFAnnot_GetStringValue(annot, kContentsKey, nullptr, 0);
    249   std::vector<char> buf(len);
    250   EXPECT_EQ(74u,
    251             FPDFAnnot_GetStringValue(annot, kContentsKey, buf.data(), len));
    252   EXPECT_STREQ(contents, BufferToWString(buf).c_str());
    253 
    254   FPDFPage_CloseAnnot(annot);
    255   UnloadPage(page);
    256 }
    257 
    258 TEST_F(FPDFAnnotEmbeddertest, AddAndSaveUnderlineAnnotation) {
    259   // Open a file with one annotation and load its first page.
    260   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
    261   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    262   ASSERT_TRUE(page);
    263 
    264   // Check that there is a total of one annotation on its first page, and verify
    265   // its quadpoints.
    266   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
    267   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    268   ASSERT_TRUE(annot);
    269   FS_QUADPOINTSF quadpoints;
    270   ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &quadpoints));
    271   EXPECT_EQ(115.802643f, quadpoints.x1);
    272   EXPECT_EQ(718.913940f, quadpoints.y1);
    273   EXPECT_EQ(157.211182f, quadpoints.x4);
    274   EXPECT_EQ(706.264465f, quadpoints.y4);
    275   FPDFPage_CloseAnnot(annot);
    276 
    277   // Add an underline annotation to the page and set its quadpoints.
    278   annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE);
    279   ASSERT_TRUE(annot);
    280   quadpoints.x1 = 140.802643f;
    281   quadpoints.x3 = 140.802643f;
    282   ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, &quadpoints));
    283   FPDFPage_CloseAnnot(annot);
    284 
    285   // Save the document, closing the page and document.
    286   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
    287   FPDF_ClosePage(page);
    288 
    289   // Open the saved document.
    290   const char md5[] = "dba153419f67b7c0c0e3d22d3e8910d5";
    291 
    292   OpenSavedDocument();
    293   page = LoadSavedPage(0);
    294   VerifySavedRendering(page, 612, 792, md5);
    295 
    296   // Check that the saved document has 2 annotations on the first page
    297   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
    298 
    299   // Check that the second annotation is an underline annotation and verify
    300   // its quadpoints.
    301   FPDF_ANNOTATION new_annot = FPDFPage_GetAnnot(page, 1);
    302   ASSERT_TRUE(new_annot);
    303   EXPECT_EQ(FPDF_ANNOT_UNDERLINE, FPDFAnnot_GetSubtype(new_annot));
    304   FS_QUADPOINTSF new_quadpoints;
    305   ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(new_annot, &new_quadpoints));
    306   EXPECT_NEAR(quadpoints.x1, new_quadpoints.x1, 0.001f);
    307   EXPECT_NEAR(quadpoints.y1, new_quadpoints.y1, 0.001f);
    308   EXPECT_NEAR(quadpoints.x4, new_quadpoints.x4, 0.001f);
    309   EXPECT_NEAR(quadpoints.y4, new_quadpoints.y4, 0.001f);
    310 
    311   FPDFPage_CloseAnnot(new_annot);
    312 
    313   CloseSavedPage(page);
    314   CloseSavedDocument();
    315 }
    316 
    317 TEST_F(FPDFAnnotEmbeddertest, ModifyRectQuadpointsWithAP) {
    318 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
    319   const char md5_original[] = "63af8432fab95a67cdebb7cd0e514941";
    320   const char md5_modified_highlight[] = "aec26075011349dec9bace891856b5f2";
    321   const char md5_modified_square[] = "057f57a32be95975775e5ec513fdcb56";
    322 #elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
    323   const char md5_original[] = "0e27376094f11490f74c65f3dc3a42c5";
    324   const char md5_modified_highlight[] = "66f3caef3a7d488a4fa1ad37fc06310e";
    325   const char md5_modified_square[] = "a456dad0bc6801ee2d6408a4394af563";
    326 #else
    327   const char md5_original[] = "0e27376094f11490f74c65f3dc3a42c5";
    328   const char md5_modified_highlight[] = "66f3caef3a7d488a4fa1ad37fc06310e";
    329   const char md5_modified_square[] = "a456dad0bc6801ee2d6408a4394af563";
    330 #endif
    331 
    332   // Open a file with four annotations and load its first page.
    333   ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
    334   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    335   ASSERT_TRUE(page);
    336   EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
    337 
    338   // Check that the original file renders correctly.
    339   FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    340   CompareBitmap(bitmap, 612, 792, md5_original);
    341   FPDFBitmap_Destroy(bitmap);
    342 
    343   // Retrieve the highlight annotation which has its AP stream already defined.
    344   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    345   ASSERT_TRUE(annot);
    346   EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
    347 
    348   // Check that color cannot be set when an AP stream is defined already.
    349   EXPECT_FALSE(
    350       FPDFAnnot_SetColor(annot, FPDFANNOT_COLORTYPE_Color, 51, 102, 153, 204));
    351 
    352   // Verify its attachment points.
    353   FS_QUADPOINTSF quadpoints;
    354   ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &quadpoints));
    355   EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
    356   EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
    357   EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
    358   EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
    359 
    360   // Check that updating the attachment points would succeed.
    361   quadpoints.x1 -= 50.f;
    362   quadpoints.x2 -= 50.f;
    363   quadpoints.x3 -= 50.f;
    364   quadpoints.x4 -= 50.f;
    365   ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, &quadpoints));
    366   FS_QUADPOINTSF new_quadpoints;
    367   ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, &new_quadpoints));
    368   EXPECT_EQ(quadpoints.x1, new_quadpoints.x1);
    369   EXPECT_EQ(quadpoints.y1, new_quadpoints.y1);
    370   EXPECT_EQ(quadpoints.x4, new_quadpoints.x4);
    371   EXPECT_EQ(quadpoints.y4, new_quadpoints.y4);
    372 
    373   // Check that updating quadpoints does not change the annotation's position.
    374   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    375   CompareBitmap(bitmap, 612, 792, md5_original);
    376   FPDFBitmap_Destroy(bitmap);
    377 
    378   // Verify its annotation rectangle.
    379   FS_RECTF rect;
    380   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    381   EXPECT_NEAR(67.7299f, rect.left, 0.001f);
    382   EXPECT_NEAR(704.296f, rect.bottom, 0.001f);
    383   EXPECT_NEAR(136.325f, rect.right, 0.001f);
    384   EXPECT_NEAR(721.292f, rect.top, 0.001f);
    385 
    386   // Check that updating the rectangle would succeed.
    387   rect.left -= 60.f;
    388   rect.right -= 60.f;
    389   ASSERT_TRUE(FPDFAnnot_SetRect(annot, &rect));
    390   FS_RECTF new_rect;
    391   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
    392   EXPECT_EQ(rect.right, new_rect.right);
    393   FPDFPage_CloseAnnot(annot);
    394 
    395   // Check that updating the rectangle changes the annotation's position.
    396   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    397   CompareBitmap(bitmap, 612, 792, md5_modified_highlight);
    398   FPDFBitmap_Destroy(bitmap);
    399 
    400   // Retrieve the square annotation which has its AP stream already defined.
    401   annot = FPDFPage_GetAnnot(page, 2);
    402   ASSERT_TRUE(annot);
    403   EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot));
    404 
    405   // Check that updating the rectangle would succeed.
    406   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    407   rect.left += 70.f;
    408   rect.right += 70.f;
    409   ASSERT_TRUE(FPDFAnnot_SetRect(annot, &rect));
    410   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
    411   EXPECT_EQ(rect.right, new_rect.right);
    412 
    413   // Check that updating the rectangle changes the square annotation's position.
    414   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    415   CompareBitmap(bitmap, 612, 792, md5_modified_square);
    416   FPDFBitmap_Destroy(bitmap);
    417 
    418   FPDFPage_CloseAnnot(annot);
    419   UnloadPage(page);
    420 }
    421 
    422 TEST_F(FPDFAnnotEmbeddertest, RemoveAnnotation) {
    423   // Open a file with 3 annotations on its first page.
    424   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
    425   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    426   ASSERT_TRUE(page);
    427   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
    428 
    429   // Check that the annotations have the expected rectangle coordinates.
    430   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    431   FS_RECTF rect;
    432   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    433   EXPECT_NEAR(86.1971f, rect.left, 0.001f);
    434   FPDFPage_CloseAnnot(annot);
    435 
    436   annot = FPDFPage_GetAnnot(page, 1);
    437   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    438   EXPECT_NEAR(149.8127f, rect.left, 0.001f);
    439   FPDFPage_CloseAnnot(annot);
    440 
    441   annot = FPDFPage_GetAnnot(page, 2);
    442   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    443   EXPECT_NEAR(351.8204f, rect.left, 0.001f);
    444   FPDFPage_CloseAnnot(annot);
    445 
    446   // Check that nothing happens when attempting to remove an annotation with an
    447   // out-of-bound index.
    448   EXPECT_FALSE(FPDFPage_RemoveAnnot(page, 4));
    449   EXPECT_FALSE(FPDFPage_RemoveAnnot(page, -1));
    450   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
    451 
    452   // Remove the second annotation.
    453   EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
    454   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
    455   EXPECT_FALSE(FPDFPage_GetAnnot(page, 2));
    456 
    457   // Save the document, closing the page and document.
    458   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
    459   FPDF_ClosePage(page);
    460 
    461   // TODO(npm): VerifySavedRendering changes annot rect dimensions by 1??
    462   // Open the saved document.
    463   std::string new_file = GetString();
    464   FPDF_FILEACCESS file_access;
    465   memset(&file_access, 0, sizeof(file_access));
    466   file_access.m_FileLen = new_file.size();
    467   file_access.m_GetBlock = GetBlockFromString;
    468   file_access.m_Param = &new_file;
    469   FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
    470   ASSERT_TRUE(new_doc);
    471   FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
    472   ASSERT_TRUE(new_page);
    473 
    474   // Check that the saved document has 2 annotations on the first page.
    475   EXPECT_EQ(2, FPDFPage_GetAnnotCount(new_page));
    476 
    477   // Check that the remaining 2 annotations are the original 1st and 3rd ones by
    478   // verifying their rectangle coordinates.
    479   annot = FPDFPage_GetAnnot(new_page, 0);
    480   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    481   EXPECT_NEAR(86.1971f, rect.left, 0.001f);
    482   FPDFPage_CloseAnnot(annot);
    483 
    484   annot = FPDFPage_GetAnnot(new_page, 1);
    485   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &rect));
    486   EXPECT_NEAR(351.8204f, rect.left, 0.001f);
    487   FPDFPage_CloseAnnot(annot);
    488   FPDF_ClosePage(new_page);
    489   FPDF_CloseDocument(new_doc);
    490 }
    491 
    492 TEST_F(FPDFAnnotEmbeddertest, AddAndModifyPath) {
    493 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
    494   const char md5_original[] = "c35408717759562d1f8bf33d317483d2";
    495   const char md5_modified_path[] = "cf3cea74bd46497520ff6c4d1ea228c8";
    496   const char md5_two_paths[] = "e8994452fc4385337bae5522354e10ff";
    497   const char md5_new_annot[] = "ee5372b31fede117fc83b9384598aa25";
    498 #else
    499   const char md5_original[] = "964f89bbe8911e540a465cf1a64b7f7e";
    500   const char md5_modified_path[] = "3f77b88ce6048e08e636c9a03921b2e5";
    501   const char md5_two_paths[] = "bffbf5ecd15862b9fe553c795400ff8e";
    502   const char md5_new_annot[] = "e020534c7eeea76be537c70d6e359a40";
    503 #endif
    504 
    505   // Open a file with two annotations and load its first page.
    506   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
    507   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    508   ASSERT_TRUE(page);
    509   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
    510 
    511   // Check that the page renders correctly.
    512   FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    513   CompareBitmap(bitmap, 595, 842, md5_original);
    514   FPDFBitmap_Destroy(bitmap);
    515 
    516   // Retrieve the stamp annotation which has its AP stream already defined.
    517   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    518   ASSERT_TRUE(annot);
    519 
    520   // Check that this annotation has one path object and retrieve it.
    521   EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
    522   FPDF_PAGEOBJECT path = FPDFAnnot_GetObject(annot, 1);
    523   EXPECT_FALSE(path);
    524   path = FPDFAnnot_GetObject(annot, 0);
    525   EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
    526   EXPECT_TRUE(path);
    527 
    528   // Modify the color of the path object.
    529   EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 0, 0, 0, 255));
    530   EXPECT_TRUE(FPDFAnnot_UpdateObject(annot, path));
    531 
    532   // Check that the page with the modified annotation renders correctly.
    533   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    534   CompareBitmap(bitmap, 595, 842, md5_modified_path);
    535   FPDFBitmap_Destroy(bitmap);
    536 
    537   // Add a second path object to the same annotation.
    538   FPDF_PAGEOBJECT dot = FPDFPageObj_CreateNewPath(7, 84);
    539   EXPECT_TRUE(FPDFPath_BezierTo(dot, 9, 86, 10, 87, 11, 88));
    540   EXPECT_TRUE(FPDFPath_SetStrokeColor(dot, 255, 0, 0, 100));
    541   EXPECT_TRUE(FPDFPath_SetStrokeWidth(dot, 14));
    542   EXPECT_TRUE(FPDFPath_SetDrawMode(dot, 0, 1));
    543   EXPECT_TRUE(FPDFAnnot_AppendObject(annot, dot));
    544   EXPECT_EQ(2, FPDFAnnot_GetObjectCount(annot));
    545 
    546   // Check that the page with an annotation with two paths renders correctly.
    547   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    548   CompareBitmap(bitmap, 595, 842, md5_two_paths);
    549   FPDFBitmap_Destroy(bitmap);
    550 
    551   // Delete the newly added path object.
    552   EXPECT_TRUE(FPDFAnnot_RemoveObject(annot, 1));
    553   EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
    554   FPDFPage_CloseAnnot(annot);
    555 
    556   // Check that the page renders the same as before.
    557   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    558   CompareBitmap(bitmap, 595, 842, md5_modified_path);
    559   FPDFBitmap_Destroy(bitmap);
    560 
    561   // Create another stamp annotation and set its annotation rectangle.
    562   annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP);
    563   ASSERT_TRUE(annot);
    564   FS_RECTF rect;
    565   rect.left = 200.f;
    566   rect.bottom = 400.f;
    567   rect.right = 500.f;
    568   rect.top = 600.f;
    569   EXPECT_TRUE(FPDFAnnot_SetRect(annot, &rect));
    570 
    571   // Add a new path to the annotation.
    572   FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(200, 500);
    573   EXPECT_TRUE(FPDFPath_LineTo(check, 300, 400));
    574   EXPECT_TRUE(FPDFPath_LineTo(check, 500, 600));
    575   EXPECT_TRUE(FPDFPath_MoveTo(check, 350, 550));
    576   EXPECT_TRUE(FPDFPath_LineTo(check, 450, 450));
    577   EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 0, 255, 255, 180));
    578   EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
    579   EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
    580   EXPECT_TRUE(FPDFAnnot_AppendObject(annot, check));
    581   EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
    582 
    583   // Check that the annotation's bounding box came from its rectangle.
    584   FS_RECTF new_rect;
    585   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
    586   EXPECT_EQ(rect.left, new_rect.left);
    587   EXPECT_EQ(rect.bottom, new_rect.bottom);
    588   EXPECT_EQ(rect.right, new_rect.right);
    589   EXPECT_EQ(rect.top, new_rect.top);
    590 
    591   // Save the document, closing the page and document.
    592   FPDFPage_CloseAnnot(annot);
    593   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
    594   FPDF_ClosePage(page);
    595 
    596   // Open the saved document.
    597   OpenSavedDocument();
    598   page = LoadSavedPage(0);
    599   VerifySavedRendering(page, 595, 842, md5_new_annot);
    600 
    601   // Check that the document has a correct count of annotations and objects.
    602   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
    603   annot = FPDFPage_GetAnnot(page, 2);
    604   ASSERT_TRUE(annot);
    605   EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
    606 
    607   // Check that the new annotation's rectangle is as defined.
    608   ASSERT_TRUE(FPDFAnnot_GetRect(annot, &new_rect));
    609   EXPECT_EQ(rect.left, new_rect.left);
    610   EXPECT_EQ(rect.bottom, new_rect.bottom);
    611   EXPECT_EQ(rect.right, new_rect.right);
    612   EXPECT_EQ(rect.top, new_rect.top);
    613 
    614   FPDFPage_CloseAnnot(annot);
    615   CloseSavedPage(page);
    616   CloseSavedDocument();
    617 }
    618 
    619 TEST_F(FPDFAnnotEmbeddertest, ModifyAnnotationFlags) {
    620   // Open a file with an annotation and load its first page.
    621   ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
    622   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    623   ASSERT_TRUE(page);
    624 
    625   // Check that the page renders correctly.
    626   FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    627   CompareBitmap(bitmap, 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
    628   FPDFBitmap_Destroy(bitmap);
    629 
    630   // Retrieve the annotation.
    631   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    632   ASSERT_TRUE(annot);
    633 
    634   // Check that the original flag values are as expected.
    635   int flags = FPDFAnnot_GetFlags(annot);
    636   EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
    637   EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
    638 
    639   // Set the HIDDEN flag.
    640   flags |= FPDF_ANNOT_FLAG_HIDDEN;
    641   EXPECT_TRUE(FPDFAnnot_SetFlags(annot, flags));
    642   flags = FPDFAnnot_GetFlags(annot);
    643   EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_HIDDEN);
    644   EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
    645 
    646   // Check that the page renders correctly without rendering the annotation.
    647   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    648   CompareBitmap(bitmap, 612, 792, "1940568c9ba33bac5d0b1ee9558c76b3");
    649   FPDFBitmap_Destroy(bitmap);
    650 
    651   // Unset the HIDDEN flag.
    652   EXPECT_TRUE(FPDFAnnot_SetFlags(annot, FPDF_ANNOT_FLAG_NONE));
    653   EXPECT_FALSE(FPDFAnnot_GetFlags(annot));
    654   flags &= ~FPDF_ANNOT_FLAG_HIDDEN;
    655   EXPECT_TRUE(FPDFAnnot_SetFlags(annot, flags));
    656   flags = FPDFAnnot_GetFlags(annot);
    657   EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
    658   EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
    659 
    660   // Check that the page renders correctly as before.
    661   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    662   CompareBitmap(bitmap, 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
    663   FPDFBitmap_Destroy(bitmap);
    664 
    665   FPDFPage_CloseAnnot(annot);
    666   UnloadPage(page);
    667 }
    668 
    669 TEST_F(FPDFAnnotEmbeddertest, AddAndModifyImage) {
    670 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
    671   const char md5_original[] = "c35408717759562d1f8bf33d317483d2";
    672   const char md5_new_image[] = "ff012f5697436dfcaec25b32d1333596";
    673   const char md5_modified_image[] = "86cf8cb2755a7a2046a543e66d9c1e61";
    674 #else
    675   const char md5_original[] = "964f89bbe8911e540a465cf1a64b7f7e";
    676   const char md5_new_image[] = "9ea8732dc9d579f68853f16892856208";
    677   const char md5_modified_image[] = "74239d2a8c55c9de1dbb9cd8781895aa";
    678 #endif
    679 
    680   // Open a file with two annotations and load its first page.
    681   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
    682   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    683   ASSERT_TRUE(page);
    684   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
    685 
    686   // Check that the page renders correctly.
    687   FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    688   CompareBitmap(bitmap, 595, 842, md5_original);
    689   FPDFBitmap_Destroy(bitmap);
    690 
    691   // Create a stamp annotation and set its annotation rectangle.
    692   FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP);
    693   ASSERT_TRUE(annot);
    694   FS_RECTF rect;
    695   rect.left = 200.f;
    696   rect.bottom = 600.f;
    697   rect.right = 400.f;
    698   rect.top = 800.f;
    699   EXPECT_TRUE(FPDFAnnot_SetRect(annot, &rect));
    700 
    701   // Add a solid-color translucent image object to the new annotation.
    702   constexpr int kBitmapSize = 200;
    703   FPDF_BITMAP image_bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 1);
    704   FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize, 0xeeeecccc);
    705   EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(image_bitmap));
    706   EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(image_bitmap));
    707   FPDF_PAGEOBJECT image_object = FPDFPageObj_NewImageObj(document());
    708   ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
    709   ASSERT_TRUE(FPDFImageObj_SetMatrix(image_object, kBitmapSize, 0, 0,
    710                                      kBitmapSize, 0, 0));
    711   FPDFPageObj_Transform(image_object, 1, 0, 0, 1, 200, 600);
    712   EXPECT_TRUE(FPDFAnnot_AppendObject(annot, image_object));
    713   FPDFPage_CloseAnnot(annot);
    714 
    715   // Check that the page renders correctly with the new image object.
    716   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    717   CompareBitmap(bitmap, 595, 842, md5_new_image);
    718   FPDFBitmap_Destroy(bitmap);
    719 
    720   // Retrieve the newly added stamp annotation and its image object.
    721   annot = FPDFPage_GetAnnot(page, 2);
    722   ASSERT_TRUE(annot);
    723   EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
    724   image_object = FPDFAnnot_GetObject(annot, 0);
    725   EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image_object));
    726 
    727   // Modify the image in the new annotation.
    728   FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize, 0xff000000);
    729   ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
    730   EXPECT_TRUE(FPDFAnnot_UpdateObject(annot, image_object));
    731   FPDFPage_CloseAnnot(annot);
    732 
    733   // Save the document, closing the page and document.
    734   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
    735   FPDF_ClosePage(page);
    736   FPDFBitmap_Destroy(image_bitmap);
    737 
    738   // Test that the saved document renders the modified image object correctly.
    739   VerifySavedDocument(595, 842, md5_modified_image);
    740 }
    741 
    742 TEST_F(FPDFAnnotEmbeddertest, AddAndModifyText) {
    743 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
    744   const char md5_original[] = "c35408717759562d1f8bf33d317483d2";
    745   const char md5_new_text[] = "e5680ed048c2cfd9a1d27212cdf41286";
    746   const char md5_modified_text[] = "79f5cfb0b07caaf936f65f6a7a57ce77";
    747 #else
    748   const char md5_original[] = "964f89bbe8911e540a465cf1a64b7f7e";
    749   const char md5_new_text[] = "00b14fa2dc1c90d1b0d034e1608efef5";
    750   const char md5_modified_text[] = "076c8f24a09ddc0e49f7e758edead6f0";
    751 #endif
    752 
    753   // Open a file with two annotations and load its first page.
    754   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
    755   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    756   ASSERT_TRUE(page);
    757   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
    758 
    759   // Check that the page renders correctly.
    760   FPDF_BITMAP bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    761   CompareBitmap(bitmap, 595, 842, md5_original);
    762   FPDFBitmap_Destroy(bitmap);
    763 
    764   // Create a stamp annotation and set its annotation rectangle.
    765   FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP);
    766   ASSERT_TRUE(annot);
    767   FS_RECTF rect;
    768   rect.left = 200.f;
    769   rect.bottom = 550.f;
    770   rect.right = 450.f;
    771   rect.top = 650.f;
    772   EXPECT_TRUE(FPDFAnnot_SetRect(annot, &rect));
    773 
    774   // Add a translucent text object to the new annotation.
    775   FPDF_PAGEOBJECT text_object =
    776       FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
    777   EXPECT_TRUE(text_object);
    778   std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
    779       GetFPDFWideString(L"I'm a translucent text laying on other text.");
    780   EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
    781   EXPECT_TRUE(FPDFText_SetFillColor(text_object, 0, 0, 255, 150));
    782   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 600);
    783   EXPECT_TRUE(FPDFAnnot_AppendObject(annot, text_object));
    784   FPDFPage_CloseAnnot(annot);
    785 
    786   // Check that the page renders correctly with the new text object.
    787   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    788   CompareBitmap(bitmap, 595, 842, md5_new_text);
    789   FPDFBitmap_Destroy(bitmap);
    790 
    791   // Retrieve the newly added stamp annotation and its text object.
    792   annot = FPDFPage_GetAnnot(page, 2);
    793   ASSERT_TRUE(annot);
    794   EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot));
    795   text_object = FPDFAnnot_GetObject(annot, 0);
    796   EXPECT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
    797 
    798   // Modify the text in the new annotation.
    799   std::unique_ptr<unsigned short, pdfium::FreeDeleter> new_text =
    800       GetFPDFWideString(L"New text!");
    801   EXPECT_TRUE(FPDFText_SetText(text_object, new_text.get()));
    802   EXPECT_TRUE(FPDFAnnot_UpdateObject(annot, text_object));
    803   FPDFPage_CloseAnnot(annot);
    804 
    805   // Check that the page renders correctly with the modified text object.
    806   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    807   CompareBitmap(bitmap, 595, 842, md5_modified_text);
    808   FPDFBitmap_Destroy(bitmap);
    809 
    810   // Remove the new annotation, and check that the page renders as before.
    811   EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 2));
    812   bitmap = RenderPageWithFlags(page, form_handle_, FPDF_ANNOT);
    813   CompareBitmap(bitmap, 595, 842, md5_original);
    814   FPDFBitmap_Destroy(bitmap);
    815 
    816   UnloadPage(page);
    817 }
    818 
    819 TEST_F(FPDFAnnotEmbeddertest, GetSetStringValue) {
    820   // Open a file with four annotations and load its first page.
    821   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
    822   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    823   ASSERT_TRUE(page);
    824 
    825   // Retrieve the first annotation.
    826   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    827   ASSERT_TRUE(annot);
    828 
    829   // Check that a non-existent key does not exist.
    830   EXPECT_FALSE(FPDFAnnot_HasKey(annot, "none"));
    831 
    832   // Check that the string value of a non-string dictionary entry is empty.
    833   static constexpr char kApKey[] = "AP";
    834   EXPECT_TRUE(FPDFAnnot_HasKey(annot, kApKey));
    835   EXPECT_EQ(FPDF_OBJECT_REFERENCE, FPDFAnnot_GetValueType(annot, kApKey));
    836   EXPECT_EQ(2u, FPDFAnnot_GetStringValue(annot, kApKey, nullptr, 0));
    837 
    838   // Check that the string value of the hash is correct.
    839   static constexpr char kHashKey[] = "AAPL:Hash";
    840   EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot, kHashKey));
    841   unsigned long len = FPDFAnnot_GetStringValue(annot, kHashKey, nullptr, 0);
    842   std::vector<char> buf(len);
    843   EXPECT_EQ(66u, FPDFAnnot_GetStringValue(annot, kHashKey, buf.data(), len));
    844   EXPECT_STREQ(L"395fbcb98d558681742f30683a62a2ad",
    845                BufferToWString(buf).c_str());
    846 
    847   // Check that the string value of the modified date is correct.
    848   static constexpr char kDateKey[] = "M";
    849   EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot, kHashKey));
    850   len = FPDFAnnot_GetStringValue(annot, kDateKey, nullptr, 0);
    851   buf.clear();
    852   buf.resize(len);
    853   EXPECT_EQ(44u, FPDFAnnot_GetStringValue(annot, kDateKey, buf.data(), len));
    854   EXPECT_STREQ(L"D:201706071721Z00'00'", BufferToWString(buf).c_str());
    855 
    856   // Update the date entry for the annotation.
    857   const wchar_t new_date[] = L"D:201706282359Z00'00'";
    858   std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
    859       GetFPDFWideString(new_date);
    860   EXPECT_TRUE(FPDFAnnot_SetStringValue(annot, kDateKey, text.get()));
    861 
    862   // Save the document, closing the page and document.
    863   FPDFPage_CloseAnnot(annot);
    864   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
    865   FPDF_ClosePage(page);
    866 
    867   // Open the saved annotation.
    868 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
    869   const char md5[] = "4d64e61c9c0f8c60ab3cc3234bb73b1c";
    870 #else
    871   const char md5[] = "c96ee1f316d7f5a1b154de9f9d467f01";
    872 #endif
    873   OpenSavedDocument();
    874   page = LoadSavedPage(0);
    875   VerifySavedRendering(page, 595, 842, md5);
    876   FPDF_ANNOTATION new_annot = FPDFPage_GetAnnot(page, 0);
    877 
    878   // Check that the string value of the modified date is the newly-set value.
    879   EXPECT_EQ(FPDF_OBJECT_STRING, FPDFAnnot_GetValueType(new_annot, kDateKey));
    880   len = FPDFAnnot_GetStringValue(new_annot, kDateKey, nullptr, 0);
    881   buf.clear();
    882   buf.resize(len);
    883   EXPECT_EQ(44u,
    884             FPDFAnnot_GetStringValue(new_annot, kDateKey, buf.data(), len));
    885   EXPECT_STREQ(new_date, BufferToWString(buf).c_str());
    886 
    887   FPDFPage_CloseAnnot(new_annot);
    888   CloseSavedPage(page);
    889   CloseSavedDocument();
    890 }
    891 
    892 TEST_F(FPDFAnnotEmbeddertest, GetSetAP) {
    893   // Open a file with four annotations and load its first page.
    894   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
    895   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
    896   ASSERT_TRUE(page);
    897 
    898   // Retrieve the first annotation.
    899   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
    900   ASSERT_TRUE(annot);
    901 
    902   // Check that the string value of an AP returns the expected length.
    903   unsigned long normal_len =
    904       FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
    905   EXPECT_EQ(73970u, normal_len);
    906 
    907   // Check that the string value of an AP is not returned if the buffer is too
    908   // small. The result buffer should be overwritten with an empty string.
    909   std::vector<char> buf(normal_len - 1);
    910   // Write L"z" in the buffer to verify it's not overwritten.
    911   wcscpy(reinterpret_cast<wchar_t*>(buf.data()), L"z");
    912   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
    913                                     buf.data(), buf.size()));
    914   std::string ap = BufferToString(buf);
    915   EXPECT_STREQ("z", ap.c_str());
    916 
    917   // Check that the string value of an AP is returned through a buffer that is
    918   // the right size.
    919   buf.clear();
    920   buf.resize(normal_len);
    921   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
    922                                     buf.data(), buf.size()));
    923   ap = BufferToString(buf);
    924   EXPECT_THAT(ap, testing::StartsWith("q Q q 7.442786 w 2 J"));
    925   EXPECT_THAT(ap, testing::EndsWith("c 716.5381 327.7156 l S Q Q"));
    926 
    927   // Check that the string value of an AP is returned through a buffer that is
    928   // larger than necessary.
    929   buf.clear();
    930   buf.resize(normal_len + 1);
    931   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
    932                                     buf.data(), buf.size()));
    933   ap = BufferToString(buf);
    934   EXPECT_THAT(ap, testing::StartsWith("q Q q 7.442786 w 2 J"));
    935   EXPECT_THAT(ap, testing::EndsWith("c 716.5381 327.7156 l S Q Q"));
    936 
    937   // Check that getting an AP for a mode that does not have an AP returns an
    938   // empty string.
    939   unsigned long rollover_len =
    940       FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
    941   EXPECT_EQ(2u, rollover_len);
    942 
    943   buf.clear();
    944   buf.resize(1000);
    945   EXPECT_EQ(2u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
    946                                 buf.data(), buf.size()));
    947   EXPECT_STREQ("", BufferToString(buf).c_str());
    948 
    949   // Check that setting the AP for an invalid appearance mode fails.
    950   std::unique_ptr<unsigned short, pdfium::FreeDeleter> apText =
    951       GetFPDFWideString(L"new test ap");
    952   EXPECT_FALSE(FPDFAnnot_SetAP(annot, -1, apText.get()));
    953   EXPECT_FALSE(
    954       FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_COUNT, apText.get()));
    955   EXPECT_FALSE(FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_COUNT + 1,
    956                                apText.get()));
    957 
    958   // Set the AP correctly now.
    959   EXPECT_TRUE(
    960       FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, apText.get()));
    961 
    962   // Check that the new annotation value is equal to the value we just set.
    963   rollover_len =
    964       FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
    965   EXPECT_EQ(24u, rollover_len);
    966   buf.clear();
    967   buf.resize(rollover_len);
    968   EXPECT_EQ(24u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
    969                                  buf.data(), buf.size()));
    970   EXPECT_STREQ(L"new test ap", BufferToWString(buf).c_str());
    971 
    972   // Check that the Normal AP was not touched when the Rollover AP was set.
    973   buf.clear();
    974   buf.resize(normal_len);
    975   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
    976                                     buf.data(), buf.size()));
    977   ap = BufferToString(buf);
    978   EXPECT_THAT(ap, testing::StartsWith("q Q q 7.442786 w 2 J"));
    979   EXPECT_THAT(ap, testing::EndsWith("c 716.5381 327.7156 l S Q Q"));
    980 
    981   // Save the modified document, then reopen it.
    982   FPDFPage_CloseAnnot(annot);
    983   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
    984   FPDF_ClosePage(page);
    985 
    986   OpenSavedDocument();
    987   page = LoadSavedPage(0);
    988   FPDF_ANNOTATION new_annot = FPDFPage_GetAnnot(page, 0);
    989 
    990   // Check that the new annotation value is equal to the value we set before
    991   // saving.
    992   rollover_len = FPDFAnnot_GetAP(new_annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
    993                                  nullptr, 0);
    994   EXPECT_EQ(24u, rollover_len);
    995   buf.clear();
    996   buf.resize(rollover_len);
    997   EXPECT_EQ(24u, FPDFAnnot_GetAP(new_annot, FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
    998                                  buf.data(), buf.size()));
    999   EXPECT_STREQ(L"new test ap", BufferToWString(buf).c_str());
   1000 
   1001   // Close saved document.
   1002   FPDFPage_CloseAnnot(new_annot);
   1003   CloseSavedPage(page);
   1004   CloseSavedDocument();
   1005 }
   1006 
   1007 TEST_F(FPDFAnnotEmbeddertest, RemoveOptionalAP) {
   1008   // Open a file with four annotations and load its first page.
   1009   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
   1010   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
   1011   ASSERT_TRUE(page);
   1012 
   1013   // Retrieve the first annotation.
   1014   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
   1015   ASSERT_TRUE(annot);
   1016 
   1017   // Set Down AP. Normal AP is already set.
   1018   std::unique_ptr<unsigned short, pdfium::FreeDeleter> apText =
   1019       GetFPDFWideString(L"new test ap");
   1020   EXPECT_TRUE(
   1021       FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, apText.get()));
   1022   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
   1023                                     nullptr, 0));
   1024   EXPECT_EQ(24u,
   1025             FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
   1026 
   1027   // Check that setting the Down AP to null removes the Down entry but keeps
   1028   // Normal intact.
   1029   EXPECT_TRUE(FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr));
   1030   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
   1031                                     nullptr, 0));
   1032   EXPECT_EQ(2u,
   1033             FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
   1034 
   1035   FPDFPage_CloseAnnot(annot);
   1036   FPDF_ClosePage(page);
   1037 }
   1038 
   1039 TEST_F(FPDFAnnotEmbeddertest, RemoveRequiredAP) {
   1040   // Open a file with four annotations and load its first page.
   1041   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
   1042   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
   1043   ASSERT_TRUE(page);
   1044 
   1045   // Retrieve the first annotation.
   1046   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
   1047   ASSERT_TRUE(annot);
   1048 
   1049   // Set Down AP. Normal AP is already set.
   1050   std::unique_ptr<unsigned short, pdfium::FreeDeleter> apText =
   1051       GetFPDFWideString(L"new test ap");
   1052   EXPECT_TRUE(
   1053       FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, apText.get()));
   1054   EXPECT_EQ(73970u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL,
   1055                                     nullptr, 0));
   1056   EXPECT_EQ(24u,
   1057             FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
   1058 
   1059   // Check that setting the Normal AP to null removes the whole AP dictionary.
   1060   EXPECT_TRUE(
   1061       FPDFAnnot_SetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr));
   1062   EXPECT_EQ(
   1063       2u, FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0));
   1064   EXPECT_EQ(2u,
   1065             FPDFAnnot_GetAP(annot, FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr, 0));
   1066 
   1067   FPDFPage_CloseAnnot(annot);
   1068   FPDF_ClosePage(page);
   1069 }
   1070 
   1071 TEST_F(FPDFAnnotEmbeddertest, ExtractLinkedAnnotations) {
   1072   // Open a file with annotations and load its first page.
   1073   ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
   1074   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
   1075   ASSERT_TRUE(page);
   1076   EXPECT_EQ(-1, FPDFPage_GetAnnotIndex(page, nullptr));
   1077 
   1078   // Retrieve the highlight annotation which has its popup defined.
   1079   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
   1080   ASSERT_TRUE(annot);
   1081   EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
   1082   EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot));
   1083   static constexpr char kPopupKey[] = "Popup";
   1084   ASSERT_TRUE(FPDFAnnot_HasKey(annot, kPopupKey));
   1085   ASSERT_EQ(FPDF_OBJECT_REFERENCE, FPDFAnnot_GetValueType(annot, kPopupKey));
   1086 
   1087   // Retrieve and verify the popup of the highlight annotation.
   1088   FPDF_ANNOTATION popup = FPDFAnnot_GetLinkedAnnot(annot, kPopupKey);
   1089   ASSERT_TRUE(popup);
   1090   EXPECT_EQ(FPDF_ANNOT_POPUP, FPDFAnnot_GetSubtype(popup));
   1091   EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, popup));
   1092   FS_RECTF rect;
   1093   ASSERT_TRUE(FPDFAnnot_GetRect(popup, &rect));
   1094   EXPECT_NEAR(612.0f, rect.left, 0.001f);
   1095   EXPECT_NEAR(578.792, rect.bottom, 0.001f);
   1096 
   1097   // Attempting to retrieve |annot|'s "IRT"-linked annotation would fail, since
   1098   // "IRT" is not a key in |annot|'s dictionary.
   1099   static constexpr char kIRTKey[] = "IRT";
   1100   ASSERT_FALSE(FPDFAnnot_HasKey(annot, kIRTKey));
   1101   EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot, kIRTKey));
   1102 
   1103   // Attempting to retrieve |annot|'s parent dictionary as an annotation would
   1104   // fail, since its parent is not an annotation.
   1105   static constexpr char kPKey[] = "P";
   1106   ASSERT_TRUE(FPDFAnnot_HasKey(annot, kPKey));
   1107   EXPECT_EQ(FPDF_OBJECT_REFERENCE, FPDFAnnot_GetValueType(annot, kPKey));
   1108   EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot, kPKey));
   1109 
   1110   FPDFPage_CloseAnnot(popup);
   1111   FPDFPage_CloseAnnot(annot);
   1112   UnloadPage(page);
   1113 }
   1114 
   1115 TEST_F(FPDFAnnotEmbeddertest, GetFormFieldFlagsTextField) {
   1116   // Open file with form text fields.
   1117   ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
   1118   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
   1119   ASSERT_TRUE(page);
   1120 
   1121   // Retrieve the first annotation: user-editable text field.
   1122   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
   1123   ASSERT_TRUE(annot);
   1124 
   1125   // Check that the flag values are as expected.
   1126   int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1127   EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   1128   FPDFPage_CloseAnnot(annot);
   1129 
   1130   // Retrieve the second annotation: read-only text field.
   1131   annot = FPDFPage_GetAnnot(page, 1);
   1132   ASSERT_TRUE(annot);
   1133 
   1134   // Check that the flag values are as expected.
   1135   flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1136   EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
   1137   FPDFPage_CloseAnnot(annot);
   1138 
   1139   UnloadPage(page);
   1140 }
   1141 
   1142 TEST_F(FPDFAnnotEmbeddertest, GetFormFieldFlagsComboBox) {
   1143   // Open file with form text fields.
   1144   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
   1145   FPDF_PAGE page = FPDF_LoadPage(document(), 0);
   1146   ASSERT_TRUE(page);
   1147 
   1148   // Retrieve the first annotation: user-editable combobox.
   1149   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
   1150   ASSERT_TRUE(annot);
   1151 
   1152   // Check that the flag values are as expected.
   1153   int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1154   EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   1155   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
   1156   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
   1157   FPDFPage_CloseAnnot(annot);
   1158 
   1159   // Retrieve the second annotation: regular combobox.
   1160   annot = FPDFPage_GetAnnot(page, 1);
   1161   ASSERT_TRUE(annot);
   1162 
   1163   // Check that the flag values are as expected.
   1164   flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1165   EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   1166   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
   1167   EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
   1168   FPDFPage_CloseAnnot(annot);
   1169 
   1170   // Retrieve the third annotation: read-only combobox.
   1171   annot = FPDFPage_GetAnnot(page, 2);
   1172   ASSERT_TRUE(annot);
   1173 
   1174   // Check that the flag values are as expected.
   1175   flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1176   EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
   1177   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
   1178   EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
   1179   FPDFPage_CloseAnnot(annot);
   1180 
   1181   UnloadPage(page);
   1182 }
   1183 
   1184 TEST_F(FPDFAnnotEmbeddertest, GetFormAnnotNull) {
   1185   // Open file with form text fields.
   1186   EXPECT_TRUE(OpenDocument("text_form.pdf"));
   1187   FPDF_PAGE page = LoadPage(0);
   1188   ASSERT_TRUE(page);
   1189 
   1190   // Attempt to get an annotation where no annotation exists on page.
   1191   FPDF_ANNOTATION annot =
   1192       FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 0, 0);
   1193   EXPECT_FALSE(annot);
   1194 
   1195   UnloadPage(page);
   1196 }
   1197 
   1198 TEST_F(FPDFAnnotEmbeddertest, GetFormAnnotAndCheckFlagsTextField) {
   1199   // Open file with form text fields.
   1200   EXPECT_TRUE(OpenDocument("text_form_multiple.pdf"));
   1201   FPDF_PAGE page = LoadPage(0);
   1202   ASSERT_TRUE(page);
   1203 
   1204   // Retrieve user-editable text field annotation.
   1205   FPDF_ANNOTATION annot =
   1206       FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 105, 118);
   1207   ASSERT_TRUE(annot);
   1208 
   1209   // Check that interactive form annotation flag values are as expected.
   1210   int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1211   EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   1212   FPDFPage_CloseAnnot(annot);
   1213 
   1214   // Retrieve read-only text field annotation.
   1215   annot = FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 105, 202);
   1216   ASSERT_TRUE(annot);
   1217 
   1218   // Check that interactive form annotation flag values are as expected.
   1219   flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1220   EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
   1221   FPDFPage_CloseAnnot(annot);
   1222 
   1223   UnloadPage(page);
   1224 }
   1225 
   1226 TEST_F(FPDFAnnotEmbeddertest, GetFormAnnotAndCheckFlagsComboBox) {
   1227   // Open file with form comboboxes.
   1228   EXPECT_TRUE(OpenDocument("combobox_form.pdf"));
   1229   FPDF_PAGE page = LoadPage(0);
   1230   ASSERT_TRUE(page);
   1231 
   1232   // Retrieve user-editable combobox annotation.
   1233   FPDF_ANNOTATION annot =
   1234       FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 102, 63);
   1235   ASSERT_TRUE(annot);
   1236 
   1237   // Check that interactive form annotation flag values are as expected.
   1238   int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1239   EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   1240   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
   1241   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
   1242   FPDFPage_CloseAnnot(annot);
   1243 
   1244   // Retrieve regular combobox annotation.
   1245   annot = FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 102, 113);
   1246   ASSERT_TRUE(annot);
   1247 
   1248   // Check that interactive form annotation flag values are as expected.
   1249   flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1250   EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
   1251   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
   1252   EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
   1253   FPDFPage_CloseAnnot(annot);
   1254 
   1255   // Retrieve read-only combobox annotation.
   1256   annot = FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, 102, 213);
   1257   ASSERT_TRUE(annot);
   1258 
   1259   // Check that interactive form annotation flag values are as expected.
   1260   flags = FPDFAnnot_GetFormFieldFlags(page, annot);
   1261   EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
   1262   EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
   1263   EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
   1264   FPDFPage_CloseAnnot(annot);
   1265 
   1266   UnloadPage(page);
   1267 }
   1268