xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdf_annot_embeddertest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2017 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "public/fpdf_annot.h"
6 
7 #include <limits.h>
8 
9 #include <algorithm>
10 #include <string>
11 #include <vector>
12 
13 #include "build/build_config.h"
14 #include "constants/annotation_common.h"
15 #include "core/fpdfapi/page/cpdf_annotcontext.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_dictionary.h"
18 #include "core/fxcrt/fx_system.h"
19 #include "core/fxge/cfx_defaultrenderdevice.h"
20 #include "fpdfsdk/cpdfsdk_helpers.h"
21 #include "public/cpp/fpdf_scopers.h"
22 #include "public/fpdf_edit.h"
23 #include "public/fpdf_formfill.h"
24 #include "public/fpdfview.h"
25 #include "testing/embedder_test.h"
26 #include "testing/embedder_test_constants.h"
27 #include "testing/fx_string_testhelpers.h"
28 #include "testing/gmock/include/gmock/gmock-matchers.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "testing/utils/hash.h"
31 #include "third_party/base/containers/contains.h"
32 #include "third_party/base/containers/span.h"
33 
34 using pdfium::AnnotationStampWithApChecksum;
35 
36 namespace {
37 
38 const wchar_t kStreamData[] =
39     L"/GS gs 0.0 0.0 0.0 RG 4 w 211.8 747.6 m 211.8 744.8 "
40     L"212.6 743.0 214.2 740.8 "
41     L"c 215.4 739.0 216.8 737.1 218.9 736.1 c 220.8 735.1 221.4 733.0 "
42     L"223.7 732.4 c 232.6 729.9 242.0 730.8 251.2 730.8 c 257.5 730.8 "
43     L"263.0 732.9 269.0 734.4 c S";
44 
VerifyFocusableAnnotSubtypes(FPDF_FORMHANDLE form_handle,pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes)45 void VerifyFocusableAnnotSubtypes(
46     FPDF_FORMHANDLE form_handle,
47     pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes) {
48   ASSERT_EQ(static_cast<int>(expected_subtypes.size()),
49             FPDFAnnot_GetFocusableSubtypesCount(form_handle));
50 
51   std::vector<FPDF_ANNOTATION_SUBTYPE> actual_subtypes(
52       expected_subtypes.size());
53   ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes(
54       form_handle, actual_subtypes.data(), actual_subtypes.size()));
55   for (size_t i = 0; i < expected_subtypes.size(); ++i)
56     ASSERT_EQ(expected_subtypes[i], actual_subtypes[i]);
57 }
58 
SetAndVerifyFocusableAnnotSubtypes(FPDF_FORMHANDLE form_handle,pdfium::span<const FPDF_ANNOTATION_SUBTYPE> subtypes)59 void SetAndVerifyFocusableAnnotSubtypes(
60     FPDF_FORMHANDLE form_handle,
61     pdfium::span<const FPDF_ANNOTATION_SUBTYPE> subtypes) {
62   ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle, subtypes.data(),
63                                              subtypes.size()));
64   VerifyFocusableAnnotSubtypes(form_handle, subtypes);
65 }
66 
VerifyAnnotationSubtypesAndFocusability(FPDF_FORMHANDLE form_handle,FPDF_PAGE page,pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes,pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_focusable_subtypes)67 void VerifyAnnotationSubtypesAndFocusability(
68     FPDF_FORMHANDLE form_handle,
69     FPDF_PAGE page,
70     pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_subtypes,
71     pdfium::span<const FPDF_ANNOTATION_SUBTYPE> expected_focusable_subtypes) {
72   ASSERT_EQ(static_cast<int>(expected_subtypes.size()),
73             FPDFPage_GetAnnotCount(page));
74   for (size_t i = 0; i < expected_subtypes.size(); ++i) {
75     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
76     ASSERT_TRUE(annot);
77     EXPECT_EQ(expected_subtypes[i], FPDFAnnot_GetSubtype(annot.get()));
78 
79     bool expected_focusable =
80         pdfium::Contains(expected_focusable_subtypes, expected_subtypes[i]);
81     EXPECT_EQ(expected_focusable,
82               FORM_SetFocusedAnnot(form_handle, annot.get()));
83 
84     // Kill the focus so the next test starts in an unfocused state.
85     FORM_ForceToKillFocus(form_handle);
86   }
87 }
88 
VerifyUriActionInLink(FPDF_DOCUMENT doc,FPDF_LINK link,const std::string & expected_uri)89 void VerifyUriActionInLink(FPDF_DOCUMENT doc,
90                            FPDF_LINK link,
91                            const std::string& expected_uri) {
92   ASSERT_TRUE(link);
93 
94   FPDF_ACTION action = FPDFLink_GetAction(link);
95   ASSERT_TRUE(action);
96   EXPECT_EQ(static_cast<unsigned long>(PDFACTION_URI),
97             FPDFAction_GetType(action));
98 
99   unsigned long bufsize = FPDFAction_GetURIPath(doc, action, nullptr, 0);
100   ASSERT_EQ(expected_uri.size() + 1, bufsize);
101 
102   std::vector<char> buffer(bufsize);
103   EXPECT_EQ(bufsize,
104             FPDFAction_GetURIPath(doc, action, buffer.data(), bufsize));
105   EXPECT_STREQ(expected_uri.c_str(), buffer.data());
106 }
107 
108 }  // namespace
109 
110 class FPDFAnnotEmbedderTest : public EmbedderTest {};
111 
TEST_F(FPDFAnnotEmbedderTest,SetAP)112 TEST_F(FPDFAnnotEmbedderTest, SetAP) {
113   ScopedFPDFDocument doc(FPDF_CreateNewDocument());
114   ASSERT_TRUE(doc);
115   ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
116   ASSERT_TRUE(page);
117   ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
118   ASSERT_TRUE(ap_stream);
119 
120   ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
121   ASSERT_TRUE(annot);
122 
123   // Negative case: FPDFAnnot_SetAP() should fail if bounding rect is not yet
124   // set on the annotation.
125   EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
126                                ap_stream.get()));
127 
128   const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
129   EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
130 
131   ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
132                                  /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/255));
133 
134   EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
135                               ap_stream.get()));
136 
137   // Verify that appearance stream is created as form XObject
138   CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
139   ASSERT_TRUE(context);
140   const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
141   ASSERT_TRUE(annot_dict);
142   RetainPtr<const CPDF_Dictionary> ap_dict =
143       annot_dict->GetDictFor(pdfium::annotation::kAP);
144   ASSERT_TRUE(ap_dict);
145   RetainPtr<const CPDF_Dictionary> stream_dict = ap_dict->GetDictFor("N");
146   ASSERT_TRUE(stream_dict);
147   // Check for non-existence of resources dictionary in case of opaque color
148   RetainPtr<const CPDF_Dictionary> resources_dict =
149       stream_dict->GetDictFor("Resources");
150   ASSERT_FALSE(resources_dict);
151   ByteString type = stream_dict->GetByteStringFor(pdfium::annotation::kType);
152   EXPECT_EQ("XObject", type);
153   ByteString sub_type =
154       stream_dict->GetByteStringFor(pdfium::annotation::kSubtype);
155   EXPECT_EQ("Form", sub_type);
156 
157   // Check that the appearance stream is same as we just set.
158   const uint32_t kStreamDataSize = std::size(kStreamData) * sizeof(FPDF_WCHAR);
159   unsigned long normal_length_bytes = FPDFAnnot_GetAP(
160       annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
161   ASSERT_EQ(kStreamDataSize, normal_length_bytes);
162   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(normal_length_bytes);
163   EXPECT_EQ(kStreamDataSize,
164             FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
165                             buf.data(), normal_length_bytes));
166   EXPECT_EQ(kStreamData, GetPlatformWString(buf.data()));
167 }
168 
TEST_F(FPDFAnnotEmbedderTest,SetAPWithOpacity)169 TEST_F(FPDFAnnotEmbedderTest, SetAPWithOpacity) {
170   ScopedFPDFDocument doc(FPDF_CreateNewDocument());
171   ASSERT_TRUE(doc);
172   ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
173   ASSERT_TRUE(page);
174   ScopedFPDFWideString ap_stream = GetFPDFWideString(kStreamData);
175   ASSERT_TRUE(ap_stream);
176 
177   ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
178   ASSERT_TRUE(annot);
179 
180   ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color,
181                                  /*R=*/255, /*G=*/0, /*B=*/0, /*A=*/102));
182 
183   const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
184   EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
185 
186   EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
187                               ap_stream.get()));
188 
189   CPDF_AnnotContext* context = CPDFAnnotContextFromFPDFAnnotation(annot.get());
190   ASSERT_TRUE(context);
191   const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
192   ASSERT_TRUE(annot_dict);
193   RetainPtr<const CPDF_Dictionary> ap_dict =
194       annot_dict->GetDictFor(pdfium::annotation::kAP);
195   ASSERT_TRUE(ap_dict);
196   RetainPtr<const CPDF_Dictionary> stream_dict = ap_dict->GetDictFor("N");
197   ASSERT_TRUE(stream_dict);
198   RetainPtr<const CPDF_Dictionary> resources_dict =
199       stream_dict->GetDictFor("Resources");
200   ASSERT_TRUE(stream_dict);
201   RetainPtr<const CPDF_Dictionary> extGState_dict =
202       resources_dict->GetDictFor("ExtGState");
203   ASSERT_TRUE(extGState_dict);
204   RetainPtr<const CPDF_Dictionary> gs_dict = extGState_dict->GetDictFor("GS");
205   ASSERT_TRUE(gs_dict);
206   ByteString type = gs_dict->GetByteStringFor(pdfium::annotation::kType);
207   EXPECT_EQ("ExtGState", type);
208   float opacity = gs_dict->GetFloatFor("CA");
209   // Opacity value of 102 is represented as 0.4f (=104/255) in /CA entry.
210   EXPECT_FLOAT_EQ(0.4f, opacity);
211   ByteString blend_mode = gs_dict->GetByteStringFor("BM");
212   EXPECT_EQ("Normal", blend_mode);
213   bool alpha_source_flag = gs_dict->GetBooleanFor("AIS", true);
214   EXPECT_FALSE(alpha_source_flag);
215 }
216 
TEST_F(FPDFAnnotEmbedderTest,InkListAPIValidations)217 TEST_F(FPDFAnnotEmbedderTest, InkListAPIValidations) {
218   ScopedFPDFDocument doc(FPDF_CreateNewDocument());
219   ASSERT_TRUE(doc);
220   ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
221   ASSERT_TRUE(page);
222 
223   // Create a new ink annotation.
224   ScopedFPDFAnnotation ink_annot(
225       FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
226   ASSERT_TRUE(ink_annot);
227   CPDF_AnnotContext* context =
228       CPDFAnnotContextFromFPDFAnnotation(ink_annot.get());
229   ASSERT_TRUE(context);
230   const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
231   ASSERT_TRUE(annot_dict);
232 
233   static constexpr FS_POINTF kFirstInkStroke[] = {
234       {80.0f, 90.0f}, {81.0f, 91.0f}, {82.0f, 92.0f},
235       {83.0f, 93.0f}, {84.0f, 94.0f}, {85.0f, 95.0f}};
236   static constexpr size_t kFirstStrokePointCount = std::size(kFirstInkStroke);
237 
238   static constexpr FS_POINTF kSecondInkStroke[] = {
239       {70.0f, 90.0f}, {71.0f, 91.0f}, {72.0f, 92.0f}};
240   static constexpr size_t kSecondStrokePointCount = std::size(kSecondInkStroke);
241 
242   static constexpr FS_POINTF kThirdInkStroke[] = {{60.0f, 90.0f},
243                                                   {61.0f, 91.0f},
244                                                   {62.0f, 92.0f},
245                                                   {63.0f, 93.0f},
246                                                   {64.0f, 94.0f}};
247   static constexpr size_t kThirdStrokePointCount = std::size(kThirdInkStroke);
248 
249   // Negative test: |annot| is passed as nullptr.
250   EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(nullptr, kFirstInkStroke,
251                                        kFirstStrokePointCount));
252 
253   // Negative test: |annot| is not ink annotation.
254   // Create a new highlight annotation.
255   ScopedFPDFAnnotation highlight_annot(
256       FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_HIGHLIGHT));
257   ASSERT_TRUE(highlight_annot);
258   EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(highlight_annot.get(), kFirstInkStroke,
259                                        kFirstStrokePointCount));
260 
261   // Negative test: passing |point_count| as  0.
262   EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), kFirstInkStroke, 0));
263 
264   // Negative test: passing |points| array as nullptr.
265   EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), nullptr,
266                                        kFirstStrokePointCount));
267 
268   // Negative test: passing |point_count| more than ULONG_MAX/2.
269   EXPECT_EQ(-1, FPDFAnnot_AddInkStroke(ink_annot.get(), kSecondInkStroke,
270                                        ULONG_MAX / 2 + 1));
271 
272   // InkStroke should get added to ink annotation. Also inklist should get
273   // created.
274   EXPECT_EQ(0, FPDFAnnot_AddInkStroke(ink_annot.get(), kFirstInkStroke,
275                                       kFirstStrokePointCount));
276 
277   RetainPtr<const CPDF_Array> inklist = annot_dict->GetArrayFor("InkList");
278   ASSERT_TRUE(inklist);
279   EXPECT_EQ(1u, inklist->size());
280   EXPECT_EQ(kFirstStrokePointCount * 2, inklist->GetArrayAt(0)->size());
281 
282   // Adding another inkStroke to ink annotation with all valid paremeters.
283   // InkList already exists in ink_annot.
284   EXPECT_EQ(1, FPDFAnnot_AddInkStroke(ink_annot.get(), kSecondInkStroke,
285                                       kSecondStrokePointCount));
286   EXPECT_EQ(2u, inklist->size());
287   EXPECT_EQ(kSecondStrokePointCount * 2, inklist->GetArrayAt(1)->size());
288 
289   // Adding one more InkStroke to the ink annotation. |point_count| passed is
290   // less than the data available in |buffer|.
291   EXPECT_EQ(2, FPDFAnnot_AddInkStroke(ink_annot.get(), kThirdInkStroke,
292                                       kThirdStrokePointCount - 1));
293   EXPECT_EQ(3u, inklist->size());
294   EXPECT_EQ((kThirdStrokePointCount - 1) * 2, inklist->GetArrayAt(2)->size());
295 }
296 
TEST_F(FPDFAnnotEmbedderTest,RemoveInkList)297 TEST_F(FPDFAnnotEmbedderTest, RemoveInkList) {
298   ScopedFPDFDocument doc(FPDF_CreateNewDocument());
299   ASSERT_TRUE(doc);
300   ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
301   ASSERT_TRUE(page);
302 
303   // Negative test: |annot| is passed as nullptr.
304   EXPECT_FALSE(FPDFAnnot_RemoveInkList(nullptr));
305 
306   // Negative test: |annot| is not ink annotation.
307   // Create a new highlight annotation.
308   ScopedFPDFAnnotation highlight_annot(
309       FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_HIGHLIGHT));
310   ASSERT_TRUE(highlight_annot);
311   EXPECT_FALSE(FPDFAnnot_RemoveInkList(highlight_annot.get()));
312 
313   // Create a new ink annotation.
314   ScopedFPDFAnnotation ink_annot(
315       FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_INK));
316   ASSERT_TRUE(ink_annot);
317   CPDF_AnnotContext* context =
318       CPDFAnnotContextFromFPDFAnnotation(ink_annot.get());
319   ASSERT_TRUE(context);
320   const CPDF_Dictionary* annot_dict = context->GetAnnotDict();
321   ASSERT_TRUE(annot_dict);
322 
323   static constexpr FS_POINTF kInkStroke[] = {{80.0f, 90.0f}, {81.0f, 91.0f},
324                                              {82.0f, 92.0f}, {83.0f, 93.0f},
325                                              {84.0f, 94.0f}, {85.0f, 95.0f}};
326   static constexpr size_t kPointCount = std::size(kInkStroke);
327 
328   // InkStroke should get added to ink annotation. Also inklist should get
329   // created.
330   EXPECT_EQ(0,
331             FPDFAnnot_AddInkStroke(ink_annot.get(), kInkStroke, kPointCount));
332 
333   RetainPtr<const CPDF_Array> inklist = annot_dict->GetArrayFor("InkList");
334   ASSERT_TRUE(inklist);
335   ASSERT_EQ(1u, inklist->size());
336   EXPECT_EQ(kPointCount * 2, inklist->GetArrayAt(0)->size());
337 
338   // Remove inklist.
339   EXPECT_TRUE(FPDFAnnot_RemoveInkList(ink_annot.get()));
340   EXPECT_FALSE(annot_dict->KeyExist("InkList"));
341 }
342 
TEST_F(FPDFAnnotEmbedderTest,BadParams)343 TEST_F(FPDFAnnotEmbedderTest, BadParams) {
344   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
345   FPDF_PAGE page = LoadPage(0);
346   ASSERT_TRUE(page);
347 
348   EXPECT_EQ(0, FPDFPage_GetAnnotCount(nullptr));
349 
350   EXPECT_FALSE(FPDFPage_GetAnnot(nullptr, 0));
351   EXPECT_FALSE(FPDFPage_GetAnnot(nullptr, -1));
352   EXPECT_FALSE(FPDFPage_GetAnnot(nullptr, 1));
353   EXPECT_FALSE(FPDFPage_GetAnnot(page, -1));
354   EXPECT_FALSE(FPDFPage_GetAnnot(page, 1));
355 
356   EXPECT_EQ(FPDF_ANNOT_UNKNOWN, FPDFAnnot_GetSubtype(nullptr));
357 
358   EXPECT_EQ(0, FPDFAnnot_GetObjectCount(nullptr));
359 
360   EXPECT_FALSE(FPDFAnnot_GetObject(nullptr, 0));
361   EXPECT_FALSE(FPDFAnnot_GetObject(nullptr, -1));
362   EXPECT_FALSE(FPDFAnnot_GetObject(nullptr, 1));
363 
364   EXPECT_FALSE(FPDFAnnot_HasKey(nullptr, "foo"));
365 
366   static const wchar_t kContents[] = L"Bar";
367   ScopedFPDFWideString text = GetFPDFWideString(kContents);
368   EXPECT_FALSE(FPDFAnnot_SetStringValue(nullptr, "foo", text.get()));
369 
370   FPDF_WCHAR buffer[64];
371   EXPECT_EQ(0u, FPDFAnnot_GetStringValue(nullptr, "foo", nullptr, 0));
372   EXPECT_EQ(0u, FPDFAnnot_GetStringValue(nullptr, "foo", buffer, 0));
373   EXPECT_EQ(0u,
374             FPDFAnnot_GetStringValue(nullptr, "foo", buffer, sizeof(buffer)));
375 
376   UnloadPage(page);
377 }
378 
TEST_F(FPDFAnnotEmbedderTest,BadAnnotsEntry)379 TEST_F(FPDFAnnotEmbedderTest, BadAnnotsEntry) {
380   ASSERT_TRUE(OpenDocument("bad_annots_entry.pdf"));
381   FPDF_PAGE page = LoadPage(0);
382   ASSERT_TRUE(page);
383 
384   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
385   EXPECT_FALSE(FPDFPage_GetAnnot(page, 0));
386 
387   UnloadPage(page);
388 }
389 
TEST_F(FPDFAnnotEmbedderTest,RenderAnnotWithOnlyRolloverAP)390 TEST_F(FPDFAnnotEmbedderTest, RenderAnnotWithOnlyRolloverAP) {
391   // Open a file with one annotation and load its first page.
392   ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
393   FPDF_PAGE page = LoadPage(0);
394   ASSERT_TRUE(page);
395 
396   // This annotation has a malformed appearance stream, which does not have its
397   // normal appearance defined, only its rollover appearance. In this case, its
398   // normal appearance should be generated, allowing the highlight annotation to
399   // still display.
400   ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
401   CompareBitmap(bitmap.get(), 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
402 
403   UnloadPage(page);
404 }
405 
TEST_F(FPDFAnnotEmbedderTest,RenderMultilineMarkupAnnotWithoutAP)406 TEST_F(FPDFAnnotEmbedderTest, RenderMultilineMarkupAnnotWithoutAP) {
407   const char* checksum = []() {
408     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
409       return "ec1f4ccbd0aecfdea6d53893387a0101";
410     return "76512832d88017668d9acc7aacd13dae";
411   }();
412 
413   // Open a file with multiline markup annotations.
414   ASSERT_TRUE(OpenDocument("annotation_markup_multiline_no_ap.pdf"));
415   FPDF_PAGE page = LoadPage(0);
416   ASSERT_TRUE(page);
417 
418   ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
419   CompareBitmap(bitmap.get(), 595, 842, checksum);
420 
421   UnloadPage(page);
422 }
423 
TEST_F(FPDFAnnotEmbedderTest,ExtractHighlightLongContent)424 TEST_F(FPDFAnnotEmbedderTest, ExtractHighlightLongContent) {
425   // Open a file with one annotation and load its first page.
426   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
427   FPDF_PAGE page = LoadPageNoEvents(0);
428   ASSERT_TRUE(page);
429 
430   // Check that there is a total of 1 annotation on its first page.
431   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
432 
433   // Check that the annotation is of type "highlight".
434   {
435     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
436     ASSERT_TRUE(annot);
437     EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
438 
439     // Check that the annotation color is yellow.
440     unsigned int R;
441     unsigned int G;
442     unsigned int B;
443     unsigned int A;
444     ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
445                                    &G, &B, &A));
446     EXPECT_EQ(255u, R);
447     EXPECT_EQ(255u, G);
448     EXPECT_EQ(0u, B);
449     EXPECT_EQ(255u, A);
450 
451     // Check that the author is correct.
452     static const char kAuthorKey[] = "T";
453     EXPECT_EQ(FPDF_OBJECT_STRING,
454               FPDFAnnot_GetValueType(annot.get(), kAuthorKey));
455     unsigned long length_bytes =
456         FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, nullptr, 0);
457     ASSERT_EQ(28u, length_bytes);
458     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
459     EXPECT_EQ(28u, FPDFAnnot_GetStringValue(annot.get(), kAuthorKey, buf.data(),
460                                             length_bytes));
461     EXPECT_EQ(L"Jae Hyun Park", GetPlatformWString(buf.data()));
462 
463     // Check that the content is correct.
464     EXPECT_EQ(
465         FPDF_OBJECT_STRING,
466         FPDFAnnot_GetValueType(annot.get(), pdfium::annotation::kContents));
467     length_bytes = FPDFAnnot_GetStringValue(
468         annot.get(), pdfium::annotation::kContents, nullptr, 0);
469     ASSERT_EQ(2690u, length_bytes);
470     buf = GetFPDFWideStringBuffer(length_bytes);
471     EXPECT_EQ(2690u, FPDFAnnot_GetStringValue(annot.get(),
472                                               pdfium::annotation::kContents,
473                                               buf.data(), length_bytes));
474     static const wchar_t kContents[] =
475         L"This is a note for that highlight annotation. Very long highlight "
476         "annotation. Long long long Long long longLong long longLong long "
477         "longLong long longLong long longLong long longLong long longLong long "
478         "longLong long longLong long longLong long longLong long longLong long "
479         "longLong long longLong long longLong long longLong long longLong long "
480         "longLong long longLong long longLong long longLong long longLong long "
481         "longLong long longLong long longLong long longLong long longLong long "
482         "longLong long longLong long longLong long longLong long longLong long "
483         "longLong long longLong long longLong long longLong long longLong long "
484         "longLong long longLong long longLong long longLong long longLong long "
485         "longLong long longLong long longLong long longLong long longLong long "
486         "longLong long longLong long longLong long longLong long longLong long "
487         "longLong long longLong long longLong long longLong long longLong long "
488         "longLong long longLong long longLong long longLong long longLong long "
489         "longLong long longLong long longLong long longLong long longLong long "
490         "longLong long longLong long longLong long longLong long longLong long "
491         "longLong long longLong long longLong long longLong long longLong long "
492         "longLong long longLong long longLong long longLong long longLong long "
493         "longLong long longLong long longLong long longLong long longLong long "
494         "longLong long long. END";
495     EXPECT_EQ(kContents, GetPlatformWString(buf.data()));
496 
497     // Check that the quadpoints are correct.
498     FS_QUADPOINTSF quadpoints;
499     ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
500     EXPECT_EQ(115.802643f, quadpoints.x1);
501     EXPECT_EQ(718.913940f, quadpoints.y1);
502     EXPECT_EQ(157.211182f, quadpoints.x4);
503     EXPECT_EQ(706.264465f, quadpoints.y4);
504   }
505   UnloadPageNoEvents(page);
506 }
507 
TEST_F(FPDFAnnotEmbedderTest,ExtractInkMultiple)508 TEST_F(FPDFAnnotEmbedderTest, ExtractInkMultiple) {
509   // Open a file with three annotations and load its first page.
510   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
511   FPDF_PAGE page = LoadPageNoEvents(0);
512   ASSERT_TRUE(page);
513 
514   // Check that there is a total of 3 annotation on its first page.
515   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
516 
517   {
518     // Check that the third annotation is of type "ink".
519     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
520     ASSERT_TRUE(annot);
521     EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot.get()));
522 
523     // Check that the annotation color is blue with opacity.
524     unsigned int R;
525     unsigned int G;
526     unsigned int B;
527     unsigned int A;
528     ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
529                                    &G, &B, &A));
530     EXPECT_EQ(0u, R);
531     EXPECT_EQ(0u, G);
532     EXPECT_EQ(255u, B);
533     EXPECT_EQ(76u, A);
534 
535     // Check that there is no content.
536     EXPECT_EQ(2u, FPDFAnnot_GetStringValue(
537                       annot.get(), pdfium::annotation::kContents, nullptr, 0));
538 
539     // Check that the rectangle coordinates are correct.
540     // Note that upon rendering, the rectangle coordinates will be adjusted.
541     FS_RECTF rect;
542     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
543     EXPECT_EQ(351.820404f, rect.left);
544     EXPECT_EQ(583.830688f, rect.bottom);
545     EXPECT_EQ(475.336090f, rect.right);
546     EXPECT_EQ(681.535034f, rect.top);
547   }
548   {
549     const char* expected_hash = []() {
550       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
551 #if BUILDFLAG(IS_APPLE)
552         return "6e00cc75639c5314c8273072915d8f92";
553 #else
554         return "1fb0dd8dd5f0b9bb8d076e48eb59296d";
555 #endif
556       }
557       return "354002e1c4386d38fdde29ef8d61074a";
558     }();
559     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
560     CompareBitmap(bitmap.get(), 612, 792, expected_hash);
561   }
562   UnloadPageNoEvents(page);
563 }
564 
TEST_F(FPDFAnnotEmbedderTest,AddIllegalSubtypeAnnotation)565 TEST_F(FPDFAnnotEmbedderTest, AddIllegalSubtypeAnnotation) {
566   // Open a file with one annotation and load its first page.
567   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
568   FPDF_PAGE page = LoadPage(0);
569   ASSERT_TRUE(page);
570 
571   // Add an annotation with an illegal subtype.
572   ASSERT_FALSE(FPDFPage_CreateAnnot(page, -1));
573 
574   UnloadPage(page);
575 }
576 
TEST_F(FPDFAnnotEmbedderTest,AddFirstTextAnnotation)577 TEST_F(FPDFAnnotEmbedderTest, AddFirstTextAnnotation) {
578   // Open a file with no annotation and load its first page.
579   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
580   FPDF_PAGE page = LoadPage(0);
581   ASSERT_TRUE(page);
582   EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
583 
584   {
585     // Add a text annotation to the page.
586     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_TEXT));
587     ASSERT_TRUE(annot);
588 
589     // Check that there is now 1 annotations on this page.
590     EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
591 
592     // Check that the subtype of the annotation is correct.
593     EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
594   }
595 
596   {
597     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
598     ASSERT_TRUE(annot);
599     EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
600 
601     // Set the color of the annotation.
602     ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, 51,
603                                    102, 153, 204));
604     // Check that the color has been set correctly.
605     unsigned int R;
606     unsigned int G;
607     unsigned int B;
608     unsigned int A;
609     ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
610                                    &G, &B, &A));
611     EXPECT_EQ(51u, R);
612     EXPECT_EQ(102u, G);
613     EXPECT_EQ(153u, B);
614     EXPECT_EQ(204u, A);
615 
616     // Change the color of the annotation.
617     ASSERT_TRUE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, 204,
618                                    153, 102, 51));
619     // Check that the color has been set correctly.
620     ASSERT_TRUE(FPDFAnnot_GetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, &R,
621                                    &G, &B, &A));
622     EXPECT_EQ(204u, R);
623     EXPECT_EQ(153u, G);
624     EXPECT_EQ(102u, B);
625     EXPECT_EQ(51u, A);
626 
627     // Set the annotation rectangle.
628     FS_RECTF rect;
629     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
630     EXPECT_EQ(0.f, rect.left);
631     EXPECT_EQ(0.f, rect.right);
632     rect.left = 35;
633     rect.bottom = 150;
634     rect.right = 53;
635     rect.top = 165;
636     ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
637     // Check that the annotation rectangle has been set correctly.
638     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
639     EXPECT_EQ(35.f, rect.left);
640     EXPECT_EQ(150.f, rect.bottom);
641     EXPECT_EQ(53.f, rect.right);
642     EXPECT_EQ(165.f, rect.top);
643 
644     // Set the content of the annotation.
645     static const wchar_t kContents[] = L"Hello! This is a customized content.";
646     ScopedFPDFWideString text = GetFPDFWideString(kContents);
647     ASSERT_TRUE(FPDFAnnot_SetStringValue(
648         annot.get(), pdfium::annotation::kContents, text.get()));
649     // Check that the content has been set correctly.
650     unsigned long length_bytes = FPDFAnnot_GetStringValue(
651         annot.get(), pdfium::annotation::kContents, nullptr, 0);
652     ASSERT_EQ(74u, length_bytes);
653     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
654     EXPECT_EQ(74u, FPDFAnnot_GetStringValue(annot.get(),
655                                             pdfium::annotation::kContents,
656                                             buf.data(), length_bytes));
657     EXPECT_EQ(kContents, GetPlatformWString(buf.data()));
658   }
659   UnloadPage(page);
660 }
661 
TEST_F(FPDFAnnotEmbedderTest,AddAndSaveLinkAnnotation)662 TEST_F(FPDFAnnotEmbedderTest, AddAndSaveLinkAnnotation) {
663   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
664   FPDF_PAGE page = LoadPage(0);
665   ASSERT_TRUE(page);
666   {
667     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
668     CompareBitmap(bitmap.get(), 200, 200, pdfium::HelloWorldChecksum());
669   }
670   EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
671 
672   constexpr char kUri[] = "https://pdfium.org/";
673 
674   {
675     // Add a link annotation to the page and set its URI.
676     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_LINK));
677     ASSERT_TRUE(annot);
678     EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
679     EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
680     EXPECT_TRUE(FPDFAnnot_SetURI(annot.get(), kUri));
681     VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
682 
683     // Negative tests:
684     EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, nullptr));
685     VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
686     EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), nullptr));
687     VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
688     EXPECT_FALSE(FPDFAnnot_SetURI(nullptr, kUri));
689     VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
690 
691     // Position the link on top of "Hello, world!" without a border.
692     const FS_RECTF kRect = {19.0f, 48.0f, 85.0f, 60.0f};
693     EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &kRect));
694     EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/0.0f,
695                                     /*vertical_radius=*/0.0f,
696                                     /*border_width=*/0.0f));
697 
698     VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0),
699                           kUri);
700   }
701 
702   {
703     // Add an ink annotation to the page. Trying to add a link to it fails.
704     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
705     ASSERT_TRUE(annot);
706     EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
707     EXPECT_EQ(FPDF_ANNOT_INK, FPDFAnnot_GetSubtype(annot.get()));
708     EXPECT_FALSE(FPDFAnnot_SetURI(annot.get(), kUri));
709   }
710 
711   // Remove the ink annotation added above for negative testing.
712   EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
713   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
714 
715   // Save the document, closing the page.
716   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
717   UnloadPage(page);
718 
719   // Reopen the document and make sure it still renders the same. Since the link
720   // does not have a border, it does not affect the rendering.
721   ASSERT_TRUE(OpenSavedDocument());
722   page = LoadSavedPage(0);
723   ASSERT_TRUE(page);
724   VerifySavedRendering(page, 200, 200, pdfium::HelloWorldChecksum());
725   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
726 
727   {
728     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
729     ASSERT_TRUE(annot);
730     EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
731     VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()), kUri);
732     VerifyUriActionInLink(document(), FPDFLink_GetLinkAtPoint(page, 40.0, 50.0),
733                           kUri);
734   }
735 
736   CloseSavedPage(page);
737   CloseSavedDocument();
738 }
739 
TEST_F(FPDFAnnotEmbedderTest,AddAndSaveUnderlineAnnotation)740 TEST_F(FPDFAnnotEmbedderTest, AddAndSaveUnderlineAnnotation) {
741   // Open a file with one annotation and load its first page.
742   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
743   FPDF_PAGE page = LoadPage(0);
744   ASSERT_TRUE(page);
745 
746   // Check that there is a total of one annotation on its first page, and verify
747   // its quadpoints.
748   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
749   FS_QUADPOINTSF quadpoints;
750   {
751     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
752     ASSERT_TRUE(annot);
753     ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
754     EXPECT_EQ(115.802643f, quadpoints.x1);
755     EXPECT_EQ(718.913940f, quadpoints.y1);
756     EXPECT_EQ(157.211182f, quadpoints.x4);
757     EXPECT_EQ(706.264465f, quadpoints.y4);
758   }
759 
760   // Add an underline annotation to the page and set its quadpoints.
761   {
762     ScopedFPDFAnnotation annot(
763         FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE));
764     ASSERT_TRUE(annot);
765     quadpoints.x1 = 140.802643f;
766     quadpoints.x3 = 140.802643f;
767     ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot.get(), &quadpoints));
768   }
769 
770   // Save the document and close the page.
771   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
772   UnloadPage(page);
773 
774   // Open the saved document.
775   const char* checksum = []() {
776     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
777 #if BUILDFLAG(IS_APPLE)
778       return "24994ad69aa612a66d183eaf9a92aa06";
779 #else
780       return "798fa41303381c9ba6d99092f5cd4d2b";
781 #endif
782     }
783     return "dba153419f67b7c0c0e3d22d3e8910d5";
784   }();
785 
786   ASSERT_TRUE(OpenSavedDocument());
787   page = LoadSavedPage(0);
788   ASSERT_TRUE(page);
789   VerifySavedRendering(page, 612, 792, checksum);
790 
791   // Check that the saved document has 2 annotations on the first page
792   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
793 
794   {
795     // Check that the second annotation is an underline annotation and verify
796     // its quadpoints.
797     ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 1));
798     ASSERT_TRUE(new_annot);
799     EXPECT_EQ(FPDF_ANNOT_UNDERLINE, FPDFAnnot_GetSubtype(new_annot.get()));
800     FS_QUADPOINTSF new_quadpoints;
801     ASSERT_TRUE(
802         FPDFAnnot_GetAttachmentPoints(new_annot.get(), 0, &new_quadpoints));
803     EXPECT_NEAR(quadpoints.x1, new_quadpoints.x1, 0.001f);
804     EXPECT_NEAR(quadpoints.y1, new_quadpoints.y1, 0.001f);
805     EXPECT_NEAR(quadpoints.x4, new_quadpoints.x4, 0.001f);
806     EXPECT_NEAR(quadpoints.y4, new_quadpoints.y4, 0.001f);
807   }
808 
809   CloseSavedPage(page);
810   CloseSavedDocument();
811 }
812 
TEST_F(FPDFAnnotEmbedderTest,GetAndSetQuadPoints)813 TEST_F(FPDFAnnotEmbedderTest, GetAndSetQuadPoints) {
814   // Open a file with four annotations and load its first page.
815   ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
816   FPDF_PAGE page = LoadPage(0);
817   ASSERT_TRUE(page);
818   EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
819 
820   // Retrieve the highlight annotation.
821   FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
822   ASSERT_TRUE(annot);
823   ASSERT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
824 
825   FS_QUADPOINTSF quadpoints;
826   ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 0, &quadpoints));
827 
828   {
829     // Verify the current one set of quadpoints.
830     ASSERT_EQ(1u, FPDFAnnot_CountAttachmentPoints(annot));
831 
832     EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
833     EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
834     EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
835     EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
836   }
837 
838   {
839     // Update the quadpoints.
840     FS_QUADPOINTSF new_quadpoints = quadpoints;
841     new_quadpoints.y1 -= 20.f;
842     new_quadpoints.y2 -= 20.f;
843     new_quadpoints.y3 -= 20.f;
844     new_quadpoints.y4 -= 20.f;
845     ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, 0, &new_quadpoints));
846 
847     // Verify added quadpoint set
848     ASSERT_EQ(1u, FPDFAnnot_CountAttachmentPoints(annot));
849     ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 0, &quadpoints));
850     EXPECT_NEAR(new_quadpoints.x1, quadpoints.x1, 0.001f);
851     EXPECT_NEAR(new_quadpoints.y1, quadpoints.y1, 0.001f);
852     EXPECT_NEAR(new_quadpoints.x4, quadpoints.x4, 0.001f);
853     EXPECT_NEAR(new_quadpoints.y4, quadpoints.y4, 0.001f);
854   }
855 
856   {
857     // Append a new set of quadpoints.
858     FS_QUADPOINTSF new_quadpoints = quadpoints;
859     new_quadpoints.y1 += 20.f;
860     new_quadpoints.y2 += 20.f;
861     new_quadpoints.y3 += 20.f;
862     new_quadpoints.y4 += 20.f;
863     ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot, &new_quadpoints));
864 
865     // Verify added quadpoint set
866     ASSERT_EQ(2u, FPDFAnnot_CountAttachmentPoints(annot));
867     ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 1, &quadpoints));
868     EXPECT_NEAR(new_quadpoints.x1, quadpoints.x1, 0.001f);
869     EXPECT_NEAR(new_quadpoints.y1, quadpoints.y1, 0.001f);
870     EXPECT_NEAR(new_quadpoints.x4, quadpoints.x4, 0.001f);
871     EXPECT_NEAR(new_quadpoints.y4, quadpoints.y4, 0.001f);
872   }
873 
874   {
875     // Setting and getting quadpoints at out-of-bound index should fail
876     EXPECT_FALSE(FPDFAnnot_SetAttachmentPoints(annot, 300000, &quadpoints));
877     EXPECT_FALSE(FPDFAnnot_GetAttachmentPoints(annot, 300000, &quadpoints));
878   }
879 
880   FPDFPage_CloseAnnot(annot);
881 
882   // Retrieve the square annotation
883   FPDF_ANNOTATION squareAnnot = FPDFPage_GetAnnot(page, 2);
884 
885   {
886     // Check that attempting to set its quadpoints would fail
887     ASSERT_TRUE(squareAnnot);
888     EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(squareAnnot));
889     EXPECT_EQ(0u, FPDFAnnot_CountAttachmentPoints(squareAnnot));
890     EXPECT_FALSE(FPDFAnnot_SetAttachmentPoints(squareAnnot, 0, &quadpoints));
891   }
892 
893   FPDFPage_CloseAnnot(squareAnnot);
894   UnloadPage(page);
895 }
896 
TEST_F(FPDFAnnotEmbedderTest,ModifyRectQuadpointsWithAP)897 TEST_F(FPDFAnnotEmbedderTest, ModifyRectQuadpointsWithAP) {
898   const char* md5_original = []() {
899     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
900       return "2a9d1df839d5ec81a49f982347d9656c";
901 #if BUILDFLAG(IS_APPLE)
902     return "fc59468d154f397fd298c69f47ef565a";
903 #else
904     return "0e27376094f11490f74c65f3dc3a42c5";
905 #endif
906   }();
907   const char* md5_modified_highlight = []() {
908     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
909       return "0fb1653db0e8e8f7ce5d726bb0074bb5";
910 #if BUILDFLAG(IS_APPLE)
911     return "e64bf648f6e9354d1f3eedb47a2c9498";
912 #else
913     return "66f3caef3a7d488a4fa1ad37fc06310e";
914 #endif
915   }();
916   const char* md5_modified_square = []() {
917     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
918       return "879c77a2cb9f79ba65ffe0bbdd720ce3";
919 #if BUILDFLAG(IS_APPLE)
920     return "a66591662c8e7ad3c6059952e234bebf";
921 #else
922     return "a456dad0bc6801ee2d6408a4394af563";
923 #endif
924   }();
925 
926   // Open a file with four annotations and load its first page.
927   ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
928   FPDF_PAGE page = LoadPage(0);
929   ASSERT_TRUE(page);
930   EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
931 
932   // Check that the original file renders correctly.
933   {
934     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
935     CompareBitmap(bitmap.get(), 612, 792, md5_original);
936   }
937 
938   FS_RECTF rect;
939   FS_RECTF new_rect;
940 
941   // Retrieve the highlight annotation which has its AP stream already defined.
942   {
943     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
944     ASSERT_TRUE(annot);
945     EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
946 
947     // Check that color cannot be set when an AP stream is defined already.
948     EXPECT_FALSE(FPDFAnnot_SetColor(annot.get(), FPDFANNOT_COLORTYPE_Color, 51,
949                                     102, 153, 204));
950 
951     // Verify its attachment points.
952     FS_QUADPOINTSF quadpoints;
953     ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
954     EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
955     EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
956     EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
957     EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
958 
959     // Check that updating the attachment points would succeed.
960     quadpoints.x1 -= 50.f;
961     quadpoints.x2 -= 50.f;
962     quadpoints.x3 -= 50.f;
963     quadpoints.x4 -= 50.f;
964     ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot.get(), 0, &quadpoints));
965     FS_QUADPOINTSF new_quadpoints;
966     ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &new_quadpoints));
967     EXPECT_EQ(quadpoints.x1, new_quadpoints.x1);
968     EXPECT_EQ(quadpoints.y1, new_quadpoints.y1);
969     EXPECT_EQ(quadpoints.x4, new_quadpoints.x4);
970     EXPECT_EQ(quadpoints.y4, new_quadpoints.y4);
971 
972     // Check that updating quadpoints does not change the annotation's position.
973     {
974       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
975       CompareBitmap(bitmap.get(), 612, 792, md5_original);
976     }
977 
978     // Verify its annotation rectangle.
979     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
980     EXPECT_NEAR(67.7299f, rect.left, 0.001f);
981     EXPECT_NEAR(704.296f, rect.bottom, 0.001f);
982     EXPECT_NEAR(136.325f, rect.right, 0.001f);
983     EXPECT_NEAR(721.292f, rect.top, 0.001f);
984 
985     // Check that updating the rectangle would succeed.
986     rect.left -= 60.f;
987     rect.right -= 60.f;
988     ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
989     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
990     EXPECT_EQ(rect.right, new_rect.right);
991   }
992 
993   // Check that updating the rectangle changes the annotation's position.
994   {
995     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
996     CompareBitmap(bitmap.get(), 612, 792, md5_modified_highlight);
997   }
998 
999   {
1000     // Retrieve the square annotation which has its AP stream already defined.
1001     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1002     ASSERT_TRUE(annot);
1003     EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot.get()));
1004 
1005     // Check that updating the rectangle would succeed.
1006     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
1007     rect.left += 70.f;
1008     rect.right += 70.f;
1009     ASSERT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
1010     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
1011     EXPECT_EQ(rect.right, new_rect.right);
1012 
1013     // Check that updating the rectangle changes the square annotation's
1014     // position.
1015     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1016     CompareBitmap(bitmap.get(), 612, 792, md5_modified_square);
1017   }
1018 
1019   UnloadPage(page);
1020 }
1021 
TEST_F(FPDFAnnotEmbedderTest,CountAttachmentPoints)1022 TEST_F(FPDFAnnotEmbedderTest, CountAttachmentPoints) {
1023   // Open a file with multiline markup annotations.
1024   ASSERT_TRUE(OpenDocument("annotation_markup_multiline_no_ap.pdf"));
1025   FPDF_PAGE page = LoadPage(0);
1026   ASSERT_TRUE(page);
1027   {
1028     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1029     ASSERT_TRUE(annot);
1030 
1031     // This is a three line annotation.
1032     EXPECT_EQ(3u, FPDFAnnot_CountAttachmentPoints(annot.get()));
1033   }
1034   UnloadPage(page);
1035 
1036   // null annotation should return 0
1037   EXPECT_EQ(0u, FPDFAnnot_CountAttachmentPoints(nullptr));
1038 }
1039 
TEST_F(FPDFAnnotEmbedderTest,RemoveAnnotation)1040 TEST_F(FPDFAnnotEmbedderTest, RemoveAnnotation) {
1041   // Open a file with 3 annotations on its first page.
1042   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
1043   FPDF_PAGE page = LoadPageNoEvents(0);
1044   ASSERT_TRUE(page);
1045   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
1046 
1047   FS_RECTF rect;
1048 
1049   // Check that the annotations have the expected rectangle coordinates.
1050   {
1051     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1052     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
1053     EXPECT_NEAR(86.1971f, rect.left, 0.001f);
1054   }
1055 
1056   {
1057     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
1058     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
1059     EXPECT_NEAR(149.8127f, rect.left, 0.001f);
1060   }
1061 
1062   {
1063     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1064     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
1065     EXPECT_NEAR(351.8204f, rect.left, 0.001f);
1066   }
1067 
1068   // Check that nothing happens when attempting to remove an annotation with an
1069   // out-of-bound index.
1070   EXPECT_FALSE(FPDFPage_RemoveAnnot(page, 4));
1071   EXPECT_FALSE(FPDFPage_RemoveAnnot(page, -1));
1072   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
1073 
1074   // Remove the second annotation.
1075   EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 1));
1076   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
1077   EXPECT_FALSE(FPDFPage_GetAnnot(page, 2));
1078 
1079   // Save the document and close the page.
1080   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1081   UnloadPageNoEvents(page);
1082 
1083   // TODO(npm): VerifySavedRendering changes annot rect dimensions by 1??
1084   // Open the saved document.
1085   std::string new_file = GetString();
1086   FPDF_FILEACCESS file_access;
1087   memset(&file_access, 0, sizeof(file_access));
1088   file_access.m_FileLen = new_file.size();
1089   file_access.m_GetBlock = GetBlockFromString;
1090   file_access.m_Param = &new_file;
1091   FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
1092   ASSERT_TRUE(new_doc);
1093   FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
1094   ASSERT_TRUE(new_page);
1095 
1096   // Check that the saved document has 2 annotations on the first page.
1097   EXPECT_EQ(2, FPDFPage_GetAnnotCount(new_page));
1098 
1099   // Check that the remaining 2 annotations are the original 1st and 3rd ones
1100   // by verifying their rectangle coordinates.
1101   {
1102     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(new_page, 0));
1103     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
1104     EXPECT_NEAR(86.1971f, rect.left, 0.001f);
1105   }
1106 
1107   {
1108     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(new_page, 1));
1109     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &rect));
1110     EXPECT_NEAR(351.8204f, rect.left, 0.001f);
1111   }
1112   FPDF_ClosePage(new_page);
1113   FPDF_CloseDocument(new_doc);
1114 }
1115 
TEST_F(FPDFAnnotEmbedderTest,AddAndModifyPath)1116 TEST_F(FPDFAnnotEmbedderTest, AddAndModifyPath) {
1117   const char* md5_modified_path = []() {
1118     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
1119       return "fb4d5fac05f7eb5d84a4100898c11197";
1120 #if BUILDFLAG(IS_APPLE)
1121     return "34614087e04b729b7b8c37739dcf9af9";
1122 #else
1123     return "31a94d22460171cd83169daf6a6956ee";
1124 #endif
1125   }();
1126   const char* md5_two_paths = []() {
1127     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
1128       return "fcf3e79b2a91d1294b9bbccff727d3c2";
1129 #if BUILDFLAG(IS_APPLE)
1130     return "6cdaf6b3e5145f435d8ccae6db5cf9af";
1131 #else
1132     return "ed49fefef45f14121f8150cde10006c4";
1133 #endif
1134   }();
1135   const char* md5_new_annot = []() {
1136     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
1137       return "7db6321c8ffe502f4e60622aa16d5417";
1138 #if BUILDFLAG(IS_APPLE)
1139     return "55dab4f158fdc284e439b88c4306373c";
1140 #else
1141     return "cc08493b1f079803930388ecc703be9d";
1142 #endif
1143   }();
1144 
1145   // Open a file with two annotations and load its first page.
1146   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1147   FPDF_PAGE page = LoadPage(0);
1148   ASSERT_TRUE(page);
1149   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
1150 
1151   // Check that the page renders correctly.
1152   {
1153     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1154     CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
1155   }
1156 
1157   {
1158     // Retrieve the stamp annotation which has its AP stream already defined.
1159     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1160     ASSERT_TRUE(annot);
1161 
1162     // Check that this annotation has one path object and retrieve it.
1163     EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
1164     ASSERT_EQ(32, FPDFPage_CountObjects(page));
1165     FPDF_PAGEOBJECT path = FPDFAnnot_GetObject(annot.get(), 1);
1166     EXPECT_FALSE(path);
1167     path = FPDFAnnot_GetObject(annot.get(), 0);
1168     EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(path));
1169     EXPECT_TRUE(path);
1170 
1171     // Modify the color of the path object.
1172     EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, 0, 0, 0, 255));
1173     EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), path));
1174 
1175     // Check that the page with the modified annotation renders correctly.
1176     {
1177       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1178       CompareBitmap(bitmap.get(), 595, 842, md5_modified_path);
1179     }
1180 
1181     // Add a second path object to the same annotation.
1182     FPDF_PAGEOBJECT dot = FPDFPageObj_CreateNewPath(7, 84);
1183     EXPECT_TRUE(FPDFPath_BezierTo(dot, 9, 86, 10, 87, 11, 88));
1184     EXPECT_TRUE(FPDFPageObj_SetStrokeColor(dot, 255, 0, 0, 100));
1185     EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(dot, 14));
1186     EXPECT_TRUE(FPDFPath_SetDrawMode(dot, 0, 1));
1187     EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), dot));
1188     EXPECT_EQ(2, FPDFAnnot_GetObjectCount(annot.get()));
1189 
1190     // The object is in the annontation, not in the page, so the page object
1191     // array should not change.
1192     ASSERT_EQ(32, FPDFPage_CountObjects(page));
1193 
1194     // Check that the page with an annotation with two paths renders correctly.
1195     {
1196       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1197       CompareBitmap(bitmap.get(), 595, 842, md5_two_paths);
1198     }
1199 
1200     // Delete the newly added path object.
1201     EXPECT_TRUE(FPDFAnnot_RemoveObject(annot.get(), 1));
1202     EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
1203     ASSERT_EQ(32, FPDFPage_CountObjects(page));
1204   }
1205 
1206   // Check that the page renders the same as before.
1207   {
1208     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1209     CompareBitmap(bitmap.get(), 595, 842, md5_modified_path);
1210   }
1211 
1212   FS_RECTF rect;
1213 
1214   {
1215     // Create another stamp annotation and set its annotation rectangle.
1216     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
1217     ASSERT_TRUE(annot);
1218     rect.left = 200.f;
1219     rect.bottom = 400.f;
1220     rect.right = 500.f;
1221     rect.top = 600.f;
1222     EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
1223 
1224     // Add a new path to the annotation.
1225     FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(200, 500);
1226     EXPECT_TRUE(FPDFPath_LineTo(check, 300, 400));
1227     EXPECT_TRUE(FPDFPath_LineTo(check, 500, 600));
1228     EXPECT_TRUE(FPDFPath_MoveTo(check, 350, 550));
1229     EXPECT_TRUE(FPDFPath_LineTo(check, 450, 450));
1230     EXPECT_TRUE(FPDFPageObj_SetStrokeColor(check, 0, 255, 255, 180));
1231     EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(check, 8.35f));
1232     EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
1233     EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), check));
1234     EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
1235 
1236     // Check that the annotation's bounding box came from its rectangle.
1237     FS_RECTF new_rect;
1238     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
1239     EXPECT_EQ(rect.left, new_rect.left);
1240     EXPECT_EQ(rect.bottom, new_rect.bottom);
1241     EXPECT_EQ(rect.right, new_rect.right);
1242     EXPECT_EQ(rect.top, new_rect.top);
1243   }
1244 
1245   // Save the document and close the page.
1246   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1247   UnloadPage(page);
1248 
1249   // Open the saved document.
1250   ASSERT_TRUE(OpenSavedDocument());
1251   page = LoadSavedPage(0);
1252   ASSERT_TRUE(page);
1253   VerifySavedRendering(page, 595, 842, md5_new_annot);
1254 
1255   // Check that the document has a correct count of annotations and objects.
1256   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
1257 
1258   {
1259     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1260     ASSERT_TRUE(annot);
1261     EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
1262 
1263     // Check that the new annotation's rectangle is as defined.
1264     FS_RECTF new_rect;
1265     ASSERT_TRUE(FPDFAnnot_GetRect(annot.get(), &new_rect));
1266     EXPECT_EQ(rect.left, new_rect.left);
1267     EXPECT_EQ(rect.bottom, new_rect.bottom);
1268     EXPECT_EQ(rect.right, new_rect.right);
1269     EXPECT_EQ(rect.top, new_rect.top);
1270   }
1271 
1272   CloseSavedPage(page);
1273   CloseSavedDocument();
1274 }
1275 
TEST_F(FPDFAnnotEmbedderTest,ModifyAnnotationFlags)1276 TEST_F(FPDFAnnotEmbedderTest, ModifyAnnotationFlags) {
1277   // Open a file with an annotation and load its first page.
1278   ASSERT_TRUE(OpenDocument("annotation_highlight_rollover_ap.pdf"));
1279   FPDF_PAGE page = LoadPage(0);
1280   ASSERT_TRUE(page);
1281 
1282   // Check that the page renders correctly.
1283   {
1284     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1285     CompareBitmap(bitmap.get(), 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
1286   }
1287 
1288   {
1289     // Retrieve the annotation.
1290     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1291     ASSERT_TRUE(annot);
1292 
1293     // Check that the original flag values are as expected.
1294     int flags = FPDFAnnot_GetFlags(annot.get());
1295     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_INVISIBLE);
1296     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
1297     EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
1298     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOZOOM);
1299     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOROTATE);
1300     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_NOVIEW);
1301     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_READONLY);
1302     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_LOCKED);
1303     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW);
1304 
1305     // Set the HIDDEN flag.
1306     flags |= FPDF_ANNOT_FLAG_HIDDEN;
1307     EXPECT_TRUE(FPDFAnnot_SetFlags(annot.get(), flags));
1308     flags = FPDFAnnot_GetFlags(annot.get());
1309     EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_HIDDEN);
1310     EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
1311 
1312     // Check that the page renders correctly without rendering the annotation.
1313     {
1314       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1315       CompareBitmap(bitmap.get(), 612, 792, pdfium::kBlankPage612By792Checksum);
1316     }
1317 
1318     // Unset the HIDDEN flag.
1319     EXPECT_TRUE(FPDFAnnot_SetFlags(annot.get(), FPDF_ANNOT_FLAG_NONE));
1320     EXPECT_FALSE(FPDFAnnot_GetFlags(annot.get()));
1321     flags &= ~FPDF_ANNOT_FLAG_HIDDEN;
1322     EXPECT_TRUE(FPDFAnnot_SetFlags(annot.get(), flags));
1323     flags = FPDFAnnot_GetFlags(annot.get());
1324     EXPECT_FALSE(flags & FPDF_ANNOT_FLAG_HIDDEN);
1325     EXPECT_TRUE(flags & FPDF_ANNOT_FLAG_PRINT);
1326 
1327     // Check that the page renders correctly as before.
1328     {
1329       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1330       CompareBitmap(bitmap.get(), 612, 792, "dc98f06da047bd8aabfa99562d2cbd1e");
1331     }
1332   }
1333 
1334   UnloadPage(page);
1335 }
1336 
TEST_F(FPDFAnnotEmbedderTest,AddAndModifyImage)1337 TEST_F(FPDFAnnotEmbedderTest, AddAndModifyImage) {
1338   const char* md5_new_image = []() {
1339     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
1340       return "476596330c0e7daa31f115005c1d36eb";
1341 #if BUILDFLAG(IS_APPLE)
1342     return "17ac49518eabbb6a7632a547269c40a3";
1343 #else
1344     return "e79446398d4508bc2cb47e6cf2a677ed";
1345 #endif
1346   }();
1347   const char* md5_modified_image = []() {
1348     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
1349       return "0047c3e7ea7658e1a963fc339f1c587d";
1350 #if BUILDFLAG(IS_APPLE)
1351     return "ce68959f74242d588af7fb82be5ba0ab";
1352 #else
1353     return "425646a517a23104b9ef22881a19b3e2";
1354 #endif
1355   }();
1356 
1357   // Open a file with two annotations and load its first page.
1358   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1359   FPDF_PAGE page = LoadPage(0);
1360   ASSERT_TRUE(page);
1361   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
1362 
1363   // Check that the page renders correctly.
1364   {
1365     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1366     CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
1367   }
1368 
1369   constexpr int kBitmapSize = 200;
1370   FPDF_BITMAP image_bitmap;
1371 
1372   {
1373     // Create a stamp annotation and set its annotation rectangle.
1374     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
1375     ASSERT_TRUE(annot);
1376     FS_RECTF rect;
1377     rect.left = 200.f;
1378     rect.bottom = 600.f;
1379     rect.right = 400.f;
1380     rect.top = 800.f;
1381     EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
1382 
1383     // Add a solid-color translucent image object to the new annotation.
1384     image_bitmap = FPDFBitmap_Create(kBitmapSize, kBitmapSize, 1);
1385     FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize,
1386                         0xeeeecccc);
1387     EXPECT_EQ(kBitmapSize, FPDFBitmap_GetWidth(image_bitmap));
1388     EXPECT_EQ(kBitmapSize, FPDFBitmap_GetHeight(image_bitmap));
1389     FPDF_PAGEOBJECT image_object = FPDFPageObj_NewImageObj(document());
1390     ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
1391     static constexpr FS_MATRIX kBitmapScaleMatrix{kBitmapSize, 0, 0,
1392                                                   kBitmapSize, 0, 0};
1393     ASSERT_TRUE(FPDFPageObj_SetMatrix(image_object, &kBitmapScaleMatrix));
1394     FPDFPageObj_Transform(image_object, 1, 0, 0, 1, 200, 600);
1395     EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), image_object));
1396   }
1397 
1398   // Check that the page renders correctly with the new image object.
1399   {
1400     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1401     CompareBitmap(bitmap.get(), 595, 842, md5_new_image);
1402   }
1403 
1404   {
1405     // Retrieve the newly added stamp annotation and its image object.
1406     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1407     ASSERT_TRUE(annot);
1408     EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
1409     FPDF_PAGEOBJECT image_object = FPDFAnnot_GetObject(annot.get(), 0);
1410     EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image_object));
1411 
1412     // Modify the image in the new annotation.
1413     FPDFBitmap_FillRect(image_bitmap, 0, 0, kBitmapSize, kBitmapSize,
1414                         0xff000000);
1415     ASSERT_TRUE(FPDFImageObj_SetBitmap(&page, 0, image_object, image_bitmap));
1416     EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), image_object));
1417   }
1418 
1419   // Save the document and close the page.
1420   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1421   UnloadPage(page);
1422   FPDFBitmap_Destroy(image_bitmap);
1423 
1424   // Test that the saved document renders the modified image object correctly.
1425   VerifySavedDocument(595, 842, md5_modified_image);
1426 }
1427 
TEST_F(FPDFAnnotEmbedderTest,AddAndModifyText)1428 TEST_F(FPDFAnnotEmbedderTest, AddAndModifyText) {
1429   const char* md5_new_text = []() {
1430     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
1431       return "1e7f98c18775d6e0f4f454747b77cc1a";
1432     }
1433 #if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
1434     return "0c3448974a4e8da2395da917935e5de1";
1435 #elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64)
1436     return "5d449d36926c9f212c6cdb6c276d18cc";
1437 #else
1438     return "a9532f555aca2fd099e2107fa40b61e6";
1439 #endif
1440   }();
1441   const char* md5_modified_text = []() {
1442     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
1443       return "37e35705946806f8f98c51e4e25647a2";
1444     }
1445 #if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_ARM64)
1446     return "9cf1c024a9d2d356bcdd14cb71a32324";
1447 #elif BUILDFLAG(IS_APPLE) && !defined(ARCH_CPU_ARM64)
1448     return "8c992808db99dbe3d74006358a671f05";
1449 #else
1450     return "03cae68322d6a6ba120e738ab325408c";
1451 #endif
1452   }();
1453 
1454   // Open a file with two annotations and load its first page.
1455   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1456   FPDF_PAGE page = LoadPage(0);
1457   ASSERT_TRUE(page);
1458   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
1459 
1460   // Check that the page renders correctly.
1461   {
1462     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1463     CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
1464   }
1465 
1466   {
1467     // Create a stamp annotation and set its annotation rectangle.
1468     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
1469     ASSERT_TRUE(annot);
1470     FS_RECTF rect;
1471     rect.left = 200.f;
1472     rect.bottom = 550.f;
1473     rect.right = 450.f;
1474     rect.top = 650.f;
1475     EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &rect));
1476 
1477     // Add a translucent text object to the new annotation.
1478     FPDF_PAGEOBJECT text_object =
1479         FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
1480     EXPECT_TRUE(text_object);
1481     ScopedFPDFWideString text =
1482         GetFPDFWideString(L"I'm a translucent text laying on other text.");
1483     EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
1484     EXPECT_TRUE(FPDFPageObj_SetFillColor(text_object, 0, 0, 255, 150));
1485     FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 600);
1486     EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), text_object));
1487   }
1488 
1489   // Check that the page renders correctly with the new text object.
1490   {
1491     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1492     CompareBitmap(bitmap.get(), 595, 842, md5_new_text);
1493   }
1494 
1495   {
1496     // Retrieve the newly added stamp annotation and its text object.
1497     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1498     ASSERT_TRUE(annot);
1499     EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get()));
1500     FPDF_PAGEOBJECT text_object = FPDFAnnot_GetObject(annot.get(), 0);
1501     EXPECT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
1502 
1503     // Modify the text in the new annotation.
1504     ScopedFPDFWideString new_text = GetFPDFWideString(L"New text!");
1505     EXPECT_TRUE(FPDFText_SetText(text_object, new_text.get()));
1506     EXPECT_TRUE(FPDFAnnot_UpdateObject(annot.get(), text_object));
1507   }
1508 
1509   // Check that the page renders correctly with the modified text object.
1510   {
1511     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1512     CompareBitmap(bitmap.get(), 595, 842, md5_modified_text);
1513   }
1514 
1515   // Remove the new annotation, and check that the page renders as before.
1516   EXPECT_TRUE(FPDFPage_RemoveAnnot(page, 2));
1517   {
1518     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
1519     CompareBitmap(bitmap.get(), 595, 842, AnnotationStampWithApChecksum());
1520   }
1521 
1522   UnloadPage(page);
1523 }
1524 
TEST_F(FPDFAnnotEmbedderTest,GetSetStringValue)1525 TEST_F(FPDFAnnotEmbedderTest, GetSetStringValue) {
1526   // Open a file with four annotations and load its first page.
1527   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1528   FPDF_PAGE page = LoadPage(0);
1529   ASSERT_TRUE(page);
1530 
1531   static const wchar_t kNewDate[] = L"D:201706282359Z00'00'";
1532 
1533   {
1534     // Retrieve the first annotation.
1535     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1536     ASSERT_TRUE(annot);
1537 
1538     // Check that a non-existent key does not exist.
1539     EXPECT_FALSE(FPDFAnnot_HasKey(annot.get(), "none"));
1540 
1541     // Check that the string value of a non-string dictionary entry is empty.
1542     EXPECT_TRUE(FPDFAnnot_HasKey(annot.get(), pdfium::annotation::kAP));
1543     EXPECT_EQ(FPDF_OBJECT_REFERENCE,
1544               FPDFAnnot_GetValueType(annot.get(), pdfium::annotation::kAP));
1545     EXPECT_EQ(2u, FPDFAnnot_GetStringValue(annot.get(), pdfium::annotation::kAP,
1546                                            nullptr, 0));
1547 
1548     // Check that the string value of the hash is correct.
1549     static const char kHashKey[] = "AAPL:Hash";
1550     EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot.get(), kHashKey));
1551     unsigned long length_bytes =
1552         FPDFAnnot_GetStringValue(annot.get(), kHashKey, nullptr, 0);
1553     ASSERT_EQ(66u, length_bytes);
1554     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
1555     EXPECT_EQ(66u, FPDFAnnot_GetStringValue(annot.get(), kHashKey, buf.data(),
1556                                             length_bytes));
1557     EXPECT_EQ(L"395fbcb98d558681742f30683a62a2ad",
1558               GetPlatformWString(buf.data()));
1559 
1560     // Check that the string value of the modified date is correct.
1561     EXPECT_EQ(FPDF_OBJECT_NAME, FPDFAnnot_GetValueType(annot.get(), kHashKey));
1562     length_bytes = FPDFAnnot_GetStringValue(annot.get(), pdfium::annotation::kM,
1563                                             nullptr, 0);
1564     ASSERT_EQ(44u, length_bytes);
1565     buf = GetFPDFWideStringBuffer(length_bytes);
1566     EXPECT_EQ(44u, FPDFAnnot_GetStringValue(annot.get(), pdfium::annotation::kM,
1567                                             buf.data(), length_bytes));
1568     EXPECT_EQ(L"D:201706071721Z00'00'", GetPlatformWString(buf.data()));
1569 
1570     // Update the date entry for the annotation.
1571     ScopedFPDFWideString text = GetFPDFWideString(kNewDate);
1572     EXPECT_TRUE(FPDFAnnot_SetStringValue(annot.get(), pdfium::annotation::kM,
1573                                          text.get()));
1574   }
1575 
1576   // Save the document and close the page.
1577   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1578   UnloadPage(page);
1579 
1580   const char* md5 = []() {
1581     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
1582       return "a95a65d109eda5671c793ff5f7d2a2df";
1583 #if BUILDFLAG(IS_APPLE)
1584     return "52e93c54796f7f7167edf64e81d12bd7";
1585 #else
1586     return "5143f9a98beb7b00ff40b89110a1089f";
1587 #endif
1588   }();
1589 
1590   // Open the saved annotation.
1591   ASSERT_TRUE(OpenSavedDocument());
1592   page = LoadSavedPage(0);
1593   ASSERT_TRUE(page);
1594   VerifySavedRendering(page, 595, 842, md5);
1595   {
1596     ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0));
1597 
1598     // Check that the string value of the modified date is the newly-set value.
1599     EXPECT_EQ(FPDF_OBJECT_STRING,
1600               FPDFAnnot_GetValueType(new_annot.get(), pdfium::annotation::kM));
1601     unsigned long length_bytes = FPDFAnnot_GetStringValue(
1602         new_annot.get(), pdfium::annotation::kM, nullptr, 0);
1603     ASSERT_EQ(44u, length_bytes);
1604     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
1605     EXPECT_EQ(44u,
1606               FPDFAnnot_GetStringValue(new_annot.get(), pdfium::annotation::kM,
1607                                        buf.data(), length_bytes));
1608     EXPECT_EQ(kNewDate, GetPlatformWString(buf.data()));
1609   }
1610 
1611   CloseSavedPage(page);
1612   CloseSavedDocument();
1613 }
1614 
TEST_F(FPDFAnnotEmbedderTest,GetNumberValue)1615 TEST_F(FPDFAnnotEmbedderTest, GetNumberValue) {
1616   // Open a file with four text annotations and load its first page.
1617   ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
1618   FPDF_PAGE page = LoadPage(0);
1619   ASSERT_TRUE(page);
1620   {
1621     // First two annotations do not have "MaxLen" attribute.
1622     for (int i = 0; i < 2; i++) {
1623       ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
1624       ASSERT_TRUE(annot);
1625 
1626       // Verify that no "MaxLen" key present.
1627       EXPECT_FALSE(FPDFAnnot_HasKey(annot.get(), "MaxLen"));
1628 
1629       float value;
1630       EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), "MaxLen", &value));
1631     }
1632 
1633     // Annotation in index 2 has "MaxLen" of 10.
1634     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1635     ASSERT_TRUE(annot);
1636 
1637     // Verify that "MaxLen" key present.
1638     EXPECT_TRUE(FPDFAnnot_HasKey(annot.get(), "MaxLen"));
1639 
1640     float value;
1641     EXPECT_TRUE(FPDFAnnot_GetNumberValue(annot.get(), "MaxLen", &value));
1642     EXPECT_FLOAT_EQ(10.0f, value);
1643 
1644     // Check bad inputs.
1645     EXPECT_FALSE(FPDFAnnot_GetNumberValue(nullptr, "MaxLen", &value));
1646     EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), nullptr, &value));
1647     EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), "MaxLen", nullptr));
1648     // Ask for key that exists but is not a number.
1649     EXPECT_FALSE(FPDFAnnot_GetNumberValue(annot.get(), "V", &value));
1650   }
1651 
1652   UnloadPage(page);
1653 }
1654 
TEST_F(FPDFAnnotEmbedderTest,GetSetAP)1655 TEST_F(FPDFAnnotEmbedderTest, GetSetAP) {
1656   // Open a file with four annotations and load its first page.
1657   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1658   FPDF_PAGE page = LoadPage(0);
1659   ASSERT_TRUE(page);
1660 
1661   {
1662     static const char kMd5NormalAP[] = "be903df0343fd774fadab9c8900cdf4a";
1663     static constexpr size_t kExpectNormalAPLength = 73970;
1664 
1665     // Retrieve the first annotation.
1666     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1667     ASSERT_TRUE(annot);
1668 
1669     // Check that the string value of an AP returns the expected length.
1670     unsigned long normal_length_bytes = FPDFAnnot_GetAP(
1671         annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL, nullptr, 0);
1672     ASSERT_EQ(kExpectNormalAPLength, normal_length_bytes);
1673 
1674     // Check that the string value of an AP is not returned if the buffer is too
1675     // small. The result buffer should be overwritten with an empty string.
1676     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(normal_length_bytes);
1677     // Write in the buffer to verify it's not overwritten.
1678     memcpy(buf.data(), "abcdefgh", 8);
1679     EXPECT_EQ(kExpectNormalAPLength,
1680               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1681                               buf.data(), normal_length_bytes - 1));
1682     EXPECT_EQ(0, memcmp(buf.data(), "abcdefgh", 8));
1683 
1684     // Check that the string value of an AP is returned through a buffer that is
1685     // the right size.
1686     EXPECT_EQ(kExpectNormalAPLength,
1687               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1688                               buf.data(), normal_length_bytes));
1689     EXPECT_EQ(kMd5NormalAP,
1690               GenerateMD5Base16({reinterpret_cast<uint8_t*>(buf.data()),
1691                                  normal_length_bytes}));
1692 
1693     // Check that the string value of an AP is returned through a buffer that is
1694     // larger than necessary.
1695     buf = GetFPDFWideStringBuffer(normal_length_bytes + 2);
1696     EXPECT_EQ(kExpectNormalAPLength,
1697               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1698                               buf.data(), normal_length_bytes + 2));
1699     EXPECT_EQ(kMd5NormalAP,
1700               GenerateMD5Base16({reinterpret_cast<uint8_t*>(buf.data()),
1701                                  normal_length_bytes}));
1702 
1703     // Check that getting an AP for a mode that does not have an AP returns an
1704     // empty string.
1705     unsigned long rollover_length_bytes = FPDFAnnot_GetAP(
1706         annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
1707     ASSERT_EQ(2u, rollover_length_bytes);
1708 
1709     buf = GetFPDFWideStringBuffer(1000);
1710     EXPECT_EQ(2u,
1711               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
1712                               buf.data(), 1000));
1713     EXPECT_EQ(L"", GetPlatformWString(buf.data()));
1714 
1715     // Check that setting the AP for an invalid appearance mode fails.
1716     ScopedFPDFWideString ap_text = GetFPDFWideString(L"new test ap");
1717     EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), -1, ap_text.get()));
1718     EXPECT_FALSE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_COUNT,
1719                                  ap_text.get()));
1720     EXPECT_FALSE(FPDFAnnot_SetAP(
1721         annot.get(), FPDF_ANNOT_APPEARANCEMODE_COUNT + 1, ap_text.get()));
1722 
1723     // Set the AP correctly now.
1724     EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
1725                                 ap_text.get()));
1726 
1727     // Check that the new annotation value is equal to the value we just set.
1728     rollover_length_bytes = FPDFAnnot_GetAP(
1729         annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
1730     ASSERT_EQ(24u, rollover_length_bytes);
1731     buf = GetFPDFWideStringBuffer(rollover_length_bytes);
1732     EXPECT_EQ(24u,
1733               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
1734                               buf.data(), rollover_length_bytes));
1735     EXPECT_EQ(L"new test ap", GetPlatformWString(buf.data()));
1736 
1737     // Check that the Normal AP was not touched when the Rollover AP was set.
1738     buf = GetFPDFWideStringBuffer(normal_length_bytes);
1739     EXPECT_EQ(kExpectNormalAPLength,
1740               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1741                               buf.data(), normal_length_bytes));
1742     EXPECT_EQ(kMd5NormalAP,
1743               GenerateMD5Base16({reinterpret_cast<uint8_t*>(buf.data()),
1744                                  normal_length_bytes}));
1745   }
1746 
1747   // Save the modified document, then reopen it.
1748   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1749   UnloadPage(page);
1750 
1751   ASSERT_TRUE(OpenSavedDocument());
1752   page = LoadSavedPage(0);
1753   ASSERT_TRUE(page);
1754   {
1755     ScopedFPDFAnnotation new_annot(FPDFPage_GetAnnot(page, 0));
1756 
1757     // Check that the new annotation value is equal to the value we set before
1758     // saving.
1759     unsigned long rollover_length_bytes = FPDFAnnot_GetAP(
1760         new_annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER, nullptr, 0);
1761     ASSERT_EQ(24u, rollover_length_bytes);
1762     std::vector<FPDF_WCHAR> buf =
1763         GetFPDFWideStringBuffer(rollover_length_bytes);
1764     EXPECT_EQ(24u, FPDFAnnot_GetAP(new_annot.get(),
1765                                    FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
1766                                    buf.data(), rollover_length_bytes));
1767     EXPECT_EQ(L"new test ap", GetPlatformWString(buf.data()));
1768   }
1769 
1770   // Close saved document.
1771   CloseSavedPage(page);
1772   CloseSavedDocument();
1773 }
1774 
TEST_F(FPDFAnnotEmbedderTest,RemoveOptionalAP)1775 TEST_F(FPDFAnnotEmbedderTest, RemoveOptionalAP) {
1776   // Open a file with four annotations and load its first page.
1777   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1778   FPDF_PAGE page = LoadPage(0);
1779   ASSERT_TRUE(page);
1780 
1781   {
1782     // Retrieve the first annotation.
1783     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1784     ASSERT_TRUE(annot);
1785 
1786     // Set Down AP. Normal AP is already set.
1787     ScopedFPDFWideString ap_text = GetFPDFWideString(L"new test ap");
1788     EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
1789                                 ap_text.get()));
1790     EXPECT_EQ(73970u,
1791               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1792                               nullptr, 0));
1793     EXPECT_EQ(24u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
1794                                    nullptr, 0));
1795 
1796     // Check that setting the Down AP to null removes the Down entry but keeps
1797     // Normal intact.
1798     EXPECT_TRUE(
1799         FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN, nullptr));
1800     EXPECT_EQ(73970u,
1801               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1802                               nullptr, 0));
1803     EXPECT_EQ(2u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
1804                                   nullptr, 0));
1805   }
1806 
1807   UnloadPage(page);
1808 }
1809 
TEST_F(FPDFAnnotEmbedderTest,RemoveRequiredAP)1810 TEST_F(FPDFAnnotEmbedderTest, RemoveRequiredAP) {
1811   // Open a file with four annotations and load its first page.
1812   ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
1813   FPDF_PAGE page = LoadPage(0);
1814   ASSERT_TRUE(page);
1815 
1816   {
1817     // Retrieve the first annotation.
1818     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1819     ASSERT_TRUE(annot);
1820 
1821     // Set Down AP. Normal AP is already set.
1822     ScopedFPDFWideString ap_text = GetFPDFWideString(L"new test ap");
1823     EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
1824                                 ap_text.get()));
1825     EXPECT_EQ(73970u,
1826               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1827                               nullptr, 0));
1828     EXPECT_EQ(24u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
1829                                    nullptr, 0));
1830 
1831     // Check that setting the Normal AP to null removes the whole AP dictionary.
1832     EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1833                                 nullptr));
1834     EXPECT_EQ(2u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_NORMAL,
1835                                   nullptr, 0));
1836     EXPECT_EQ(2u, FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_DOWN,
1837                                   nullptr, 0));
1838   }
1839 
1840   UnloadPage(page);
1841 }
1842 
TEST_F(FPDFAnnotEmbedderTest,ExtractLinkedAnnotations)1843 TEST_F(FPDFAnnotEmbedderTest, ExtractLinkedAnnotations) {
1844   // Open a file with annotations and load its first page.
1845   ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
1846   FPDF_PAGE page = LoadPage(0);
1847   ASSERT_TRUE(page);
1848   EXPECT_EQ(-1, FPDFPage_GetAnnotIndex(page, nullptr));
1849 
1850   {
1851     // Retrieve the highlight annotation which has its popup defined.
1852     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1853     ASSERT_TRUE(annot);
1854     EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
1855     EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot.get()));
1856     static const char kPopupKey[] = "Popup";
1857     ASSERT_TRUE(FPDFAnnot_HasKey(annot.get(), kPopupKey));
1858     ASSERT_EQ(FPDF_OBJECT_REFERENCE,
1859               FPDFAnnot_GetValueType(annot.get(), kPopupKey));
1860 
1861     // Retrieve and verify the popup of the highlight annotation.
1862     ScopedFPDFAnnotation popup(
1863         FPDFAnnot_GetLinkedAnnot(annot.get(), kPopupKey));
1864     ASSERT_TRUE(popup);
1865     EXPECT_EQ(FPDF_ANNOT_POPUP, FPDFAnnot_GetSubtype(popup.get()));
1866     EXPECT_EQ(1, FPDFPage_GetAnnotIndex(page, popup.get()));
1867     FS_RECTF rect;
1868     ASSERT_TRUE(FPDFAnnot_GetRect(popup.get(), &rect));
1869     EXPECT_NEAR(612.0f, rect.left, 0.001f);
1870     EXPECT_NEAR(578.792, rect.bottom, 0.001f);
1871 
1872     // Attempting to retrieve |annot|'s "IRT"-linked annotation would fail,
1873     // since "IRT" is not a key in |annot|'s dictionary.
1874     static const char kIRTKey[] = "IRT";
1875     ASSERT_FALSE(FPDFAnnot_HasKey(annot.get(), kIRTKey));
1876     EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot.get(), kIRTKey));
1877 
1878     // Attempting to retrieve |annot|'s parent dictionary as an annotation
1879     // would fail, since its parent is not an annotation.
1880     ASSERT_TRUE(FPDFAnnot_HasKey(annot.get(), pdfium::annotation::kP));
1881     EXPECT_EQ(FPDF_OBJECT_REFERENCE,
1882               FPDFAnnot_GetValueType(annot.get(), pdfium::annotation::kP));
1883     EXPECT_FALSE(FPDFAnnot_GetLinkedAnnot(annot.get(), pdfium::annotation::kP));
1884   }
1885 
1886   UnloadPage(page);
1887 }
1888 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldFlagsTextField)1889 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsTextField) {
1890   // Open file with form text fields.
1891   ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
1892   FPDF_PAGE page = LoadPage(0);
1893   ASSERT_TRUE(page);
1894 
1895   {
1896     // Retrieve the first annotation: user-editable text field.
1897     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1898     ASSERT_TRUE(annot);
1899 
1900     // Check that the flag values are as expected.
1901     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
1902     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
1903     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
1904     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
1905     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE);
1906     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
1907   }
1908 
1909   {
1910     // Retrieve the second annotation: read-only text field.
1911     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
1912     ASSERT_TRUE(annot);
1913 
1914     // Check that the flag values are as expected.
1915     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
1916     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
1917     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
1918     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
1919     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE);
1920     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
1921   }
1922 
1923   {
1924     // Retrieve the fourth annotation: user-editable password text field.
1925     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
1926     ASSERT_TRUE(annot);
1927 
1928     // Check that the flag values are as expected.
1929     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
1930     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
1931     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
1932     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
1933     EXPECT_FALSE(flags & FPDF_FORMFLAG_TEXT_MULTILINE);
1934     EXPECT_TRUE(flags & FPDF_FORMFLAG_TEXT_PASSWORD);
1935   }
1936 
1937   UnloadPage(page);
1938 }
1939 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldFlagsComboBox)1940 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldFlagsComboBox) {
1941   // Open file with form text fields.
1942   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
1943   FPDF_PAGE page = LoadPage(0);
1944   ASSERT_TRUE(page);
1945 
1946   {
1947     // Retrieve the first annotation: user-editable combobox.
1948     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
1949     ASSERT_TRUE(annot);
1950 
1951     // Check that the flag values are as expected.
1952     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
1953     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
1954     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
1955     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
1956     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
1957     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
1958     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
1959   }
1960 
1961   {
1962     // Retrieve the second annotation: regular combobox.
1963     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
1964     ASSERT_TRUE(annot);
1965 
1966     // Check that the flag values are as expected.
1967     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
1968     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
1969     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
1970     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
1971     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
1972     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
1973     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
1974   }
1975 
1976   {
1977     // Retrieve the third annotation: read-only combobox.
1978     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
1979     ASSERT_TRUE(annot);
1980 
1981     // Check that the flag values are as expected.
1982     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
1983     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
1984     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
1985     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
1986     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
1987     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
1988     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
1989   }
1990 
1991   UnloadPage(page);
1992 }
1993 
TEST_F(FPDFAnnotEmbedderTest,GetFormAnnotNull)1994 TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotNull) {
1995   // Open file with form text fields.
1996   ASSERT_TRUE(OpenDocument("text_form.pdf"));
1997   FPDF_PAGE page = LoadPage(0);
1998   ASSERT_TRUE(page);
1999 
2000   // Attempt to get an annotation where no annotation exists on page.
2001   static const FS_POINTF kOriginPoint = {0.0f, 0.0f};
2002   EXPECT_FALSE(
2003       FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kOriginPoint));
2004 
2005   static const FS_POINTF kValidPoint = {120.0f, 120.0f};
2006   {
2007     // Verify there is an annotation.
2008     ScopedFPDFAnnotation annot(
2009         FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kValidPoint));
2010     EXPECT_TRUE(annot);
2011   }
2012 
2013   // Try other bad inputs at a valid location.
2014   EXPECT_FALSE(FPDFAnnot_GetFormFieldAtPoint(nullptr, nullptr, &kValidPoint));
2015   EXPECT_FALSE(FPDFAnnot_GetFormFieldAtPoint(nullptr, page, &kValidPoint));
2016   EXPECT_FALSE(
2017       FPDFAnnot_GetFormFieldAtPoint(form_handle(), nullptr, &kValidPoint));
2018 
2019   UnloadPage(page);
2020 }
2021 
TEST_F(FPDFAnnotEmbedderTest,GetFormAnnotAndCheckFlagsTextField)2022 TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsTextField) {
2023   // Open file with form text fields.
2024   ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
2025   FPDF_PAGE page = LoadPage(0);
2026   ASSERT_TRUE(page);
2027 
2028   {
2029     // Retrieve user-editable text field annotation.
2030     static const FS_POINTF kPoint = {105.0f, 118.0f};
2031     ScopedFPDFAnnotation annot(
2032         FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
2033     ASSERT_TRUE(annot);
2034 
2035     // Check that interactive form annotation flag values are as expected.
2036     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
2037     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
2038     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
2039     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
2040   }
2041 
2042   {
2043     // Retrieve read-only text field annotation.
2044     static const FS_POINTF kPoint = {105.0f, 202.0f};
2045     ScopedFPDFAnnotation annot(
2046         FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
2047     ASSERT_TRUE(annot);
2048 
2049     // Check that interactive form annotation flag values are as expected.
2050     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
2051     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
2052     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
2053     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
2054   }
2055 
2056   UnloadPage(page);
2057 }
2058 
TEST_F(FPDFAnnotEmbedderTest,GetFormAnnotAndCheckFlagsComboBox)2059 TEST_F(FPDFAnnotEmbedderTest, GetFormAnnotAndCheckFlagsComboBox) {
2060   // Open file with form comboboxes.
2061   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2062   FPDF_PAGE page = LoadPage(0);
2063   ASSERT_TRUE(page);
2064 
2065   {
2066     // Retrieve user-editable combobox annotation.
2067     static const FS_POINTF kPoint = {102.0f, 363.0f};
2068     ScopedFPDFAnnotation annot(
2069         FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
2070     ASSERT_TRUE(annot);
2071 
2072     // Check that interactive form annotation flag values are as expected.
2073     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
2074     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
2075     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
2076     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
2077     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
2078     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
2079     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
2080   }
2081 
2082   {
2083     // Retrieve regular combobox annotation.
2084     static const FS_POINTF kPoint = {102.0f, 413.0f};
2085     ScopedFPDFAnnotation annot(
2086         FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
2087     ASSERT_TRUE(annot);
2088 
2089     // Check that interactive form annotation flag values are as expected.
2090     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
2091     EXPECT_FALSE(flags & FPDF_FORMFLAG_READONLY);
2092     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
2093     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
2094     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
2095     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
2096     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
2097   }
2098 
2099   {
2100     // Retrieve read-only combobox annotation.
2101     static const FS_POINTF kPoint = {102.0f, 513.0f};
2102     ScopedFPDFAnnotation annot(
2103         FPDFAnnot_GetFormFieldAtPoint(form_handle(), page, &kPoint));
2104     ASSERT_TRUE(annot);
2105 
2106     // Check that interactive form annotation flag values are as expected.
2107     int flags = FPDFAnnot_GetFormFieldFlags(form_handle(), annot.get());
2108     EXPECT_TRUE(flags & FPDF_FORMFLAG_READONLY);
2109     EXPECT_FALSE(flags & FPDF_FORMFLAG_REQUIRED);
2110     EXPECT_FALSE(flags & FPDF_FORMFLAG_NOEXPORT);
2111     EXPECT_TRUE(flags & FPDF_FORMFLAG_CHOICE_COMBO);
2112     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_EDIT);
2113     EXPECT_FALSE(flags & FPDF_FORMFLAG_CHOICE_MULTI_SELECT);
2114   }
2115 
2116   UnloadPage(page);
2117 }
2118 
TEST_F(FPDFAnnotEmbedderTest,BUG_1206)2119 TEST_F(FPDFAnnotEmbedderTest, BUG_1206) {
2120   const char* expected_bitmap = []() {
2121     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
2122       return "a1ea1ceebb26922fae576cb79ce63af0";
2123     return "0d9fc05c6762fd788bd23fd87a4967bc";
2124   }();
2125   static constexpr size_t kExpectedSize = 1590;
2126 
2127   ASSERT_TRUE(OpenDocument("bug_1206.pdf"));
2128 
2129   FPDF_PAGE page = LoadPage(0);
2130   ASSERT_TRUE(page);
2131 
2132   ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2133   EXPECT_EQ(kExpectedSize, GetString().size());
2134   ClearString();
2135 
2136   for (size_t i = 0; i < 10; ++i) {
2137     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
2138     CompareBitmap(bitmap.get(), 612, 792, expected_bitmap);
2139 
2140     ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2141     // TODO(https://crbug.com/pdfium/1206): This is wrong. The size should be
2142     // equal, not bigger.
2143     EXPECT_LT(kExpectedSize, GetString().size());
2144     ClearString();
2145   }
2146 
2147   UnloadPage(page);
2148 }
2149 
TEST_F(FPDFAnnotEmbedderTest,BUG_1212)2150 TEST_F(FPDFAnnotEmbedderTest, BUG_1212) {
2151   ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2152   FPDF_PAGE page = LoadPage(0);
2153   ASSERT_TRUE(page);
2154   EXPECT_EQ(0, FPDFPage_GetAnnotCount(page));
2155 
2156   static const char kTestKey[] = "test";
2157   static const wchar_t kData[] = L"\xf6\xe4";
2158   static const size_t kBufSize = 12;
2159   std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(kBufSize);
2160 
2161   {
2162     // Add a text annotation to the page.
2163     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_TEXT));
2164     ASSERT_TRUE(annot);
2165     EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
2166     EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
2167 
2168     // Make sure there is no test key, add set a value there, and read it back.
2169     std::fill(buf.begin(), buf.end(), 'x');
2170     ASSERT_EQ(2u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
2171                                            kBufSize));
2172     EXPECT_EQ(L"", GetPlatformWString(buf.data()));
2173 
2174     ScopedFPDFWideString text = GetFPDFWideString(kData);
2175     EXPECT_TRUE(FPDFAnnot_SetStringValue(annot.get(), kTestKey, text.get()));
2176 
2177     std::fill(buf.begin(), buf.end(), 'x');
2178     ASSERT_EQ(6u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
2179                                            kBufSize));
2180     EXPECT_EQ(kData, GetPlatformWString(buf.data()));
2181   }
2182 
2183   {
2184     ScopedFPDFAnnotation annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_STAMP));
2185     ASSERT_TRUE(annot);
2186     const FS_RECTF bounding_rect{206.0f, 753.0f, 339.0f, 709.0f};
2187     EXPECT_TRUE(FPDFAnnot_SetRect(annot.get(), &bounding_rect));
2188     EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
2189     EXPECT_EQ(FPDF_ANNOT_STAMP, FPDFAnnot_GetSubtype(annot.get()));
2190     // Also do the same test for its appearance string.
2191     std::fill(buf.begin(), buf.end(), 'x');
2192     ASSERT_EQ(2u,
2193               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
2194                               buf.data(), kBufSize));
2195     EXPECT_EQ(L"", GetPlatformWString(buf.data()));
2196 
2197     ScopedFPDFWideString text = GetFPDFWideString(kData);
2198     EXPECT_TRUE(FPDFAnnot_SetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
2199                                 text.get()));
2200 
2201     std::fill(buf.begin(), buf.end(), 'x');
2202     ASSERT_EQ(6u,
2203               FPDFAnnot_GetAP(annot.get(), FPDF_ANNOT_APPEARANCEMODE_ROLLOVER,
2204                               buf.data(), kBufSize));
2205     EXPECT_EQ(kData, GetPlatformWString(buf.data()));
2206   }
2207 
2208   UnloadPage(page);
2209 
2210   {
2211     // Save a copy, open the copy, and check the annotation again.
2212     // Note that it renders the rotation.
2213     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2214     ASSERT_TRUE(OpenSavedDocument());
2215     FPDF_PAGE saved_page = LoadSavedPage(0);
2216     ASSERT_TRUE(saved_page);
2217 
2218     EXPECT_EQ(2, FPDFPage_GetAnnotCount(saved_page));
2219     {
2220       ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(saved_page, 0));
2221       ASSERT_TRUE(annot);
2222       EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
2223 
2224       std::fill(buf.begin(), buf.end(), 'x');
2225       ASSERT_EQ(6u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
2226                                              kBufSize));
2227       EXPECT_EQ(kData, GetPlatformWString(buf.data()));
2228     }
2229 
2230     {
2231       ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(saved_page, 0));
2232       ASSERT_TRUE(annot);
2233       // TODO(thestig): This return FPDF_ANNOT_UNKNOWN for some reason.
2234       // EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot.get()));
2235 
2236       std::fill(buf.begin(), buf.end(), 'x');
2237       ASSERT_EQ(6u, FPDFAnnot_GetStringValue(annot.get(), kTestKey, buf.data(),
2238                                              kBufSize));
2239       EXPECT_EQ(kData, GetPlatformWString(buf.data()));
2240     }
2241 
2242     CloseSavedPage(saved_page);
2243     CloseSavedDocument();
2244   }
2245 }
2246 
TEST_F(FPDFAnnotEmbedderTest,GetOptionCountCombobox)2247 TEST_F(FPDFAnnotEmbedderTest, GetOptionCountCombobox) {
2248   // Open a file with combobox widget annotations and load its first page.
2249   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2250   FPDF_PAGE page = LoadPage(0);
2251   ASSERT_TRUE(page);
2252 
2253   {
2254     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2255     ASSERT_TRUE(annot);
2256 
2257     EXPECT_EQ(3, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
2258 
2259     annot.reset(FPDFPage_GetAnnot(page, 1));
2260     ASSERT_TRUE(annot);
2261 
2262     EXPECT_EQ(26, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
2263 
2264     // Check bad form handle / annot.
2265     EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(nullptr, nullptr));
2266     EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(form_handle(), nullptr));
2267     EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(nullptr, annot.get()));
2268   }
2269 
2270   UnloadPage(page);
2271 }
2272 
TEST_F(FPDFAnnotEmbedderTest,GetOptionCountListbox)2273 TEST_F(FPDFAnnotEmbedderTest, GetOptionCountListbox) {
2274   // Open a file with listbox widget annotations and load its first page.
2275   ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
2276   FPDF_PAGE page = LoadPage(0);
2277   ASSERT_TRUE(page);
2278 
2279   {
2280     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2281     ASSERT_TRUE(annot);
2282 
2283     EXPECT_EQ(3, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
2284 
2285     annot.reset(FPDFPage_GetAnnot(page, 1));
2286     ASSERT_TRUE(annot);
2287 
2288     EXPECT_EQ(26, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
2289   }
2290 
2291   UnloadPage(page);
2292 }
2293 
TEST_F(FPDFAnnotEmbedderTest,GetOptionCountInvalidAnnotations)2294 TEST_F(FPDFAnnotEmbedderTest, GetOptionCountInvalidAnnotations) {
2295   // Open a file with ink annotations and load its first page.
2296   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
2297   FPDF_PAGE page = LoadPage(0);
2298   ASSERT_TRUE(page);
2299 
2300   {
2301     // annotations do not have "Opt" array and will return -1
2302     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2303     ASSERT_TRUE(annot);
2304 
2305     EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
2306 
2307     annot.reset(FPDFPage_GetAnnot(page, 1));
2308     ASSERT_TRUE(annot);
2309 
2310     EXPECT_EQ(-1, FPDFAnnot_GetOptionCount(form_handle(), annot.get()));
2311   }
2312 
2313   UnloadPage(page);
2314 }
2315 
TEST_F(FPDFAnnotEmbedderTest,GetOptionLabelCombobox)2316 TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelCombobox) {
2317   // Open a file with combobox widget annotations and load its first page.
2318   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2319   FPDF_PAGE page = LoadPage(0);
2320   ASSERT_TRUE(page);
2321 
2322   {
2323     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2324     ASSERT_TRUE(annot);
2325 
2326     int index = 0;
2327     unsigned long length_bytes =
2328         FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
2329     ASSERT_EQ(8u, length_bytes);
2330     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2331     EXPECT_EQ(8u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
2332                                            buf.data(), length_bytes));
2333     EXPECT_EQ(L"Foo", GetPlatformWString(buf.data()));
2334 
2335     annot.reset(FPDFPage_GetAnnot(page, 1));
2336     ASSERT_TRUE(annot);
2337 
2338     index = 0;
2339     length_bytes =
2340         FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
2341     ASSERT_EQ(12u, length_bytes);
2342     buf = GetFPDFWideStringBuffer(length_bytes);
2343     EXPECT_EQ(12u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
2344                                             buf.data(), length_bytes));
2345     EXPECT_EQ(L"Apple", GetPlatformWString(buf.data()));
2346 
2347     index = 25;
2348     length_bytes =
2349         FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
2350     buf = GetFPDFWideStringBuffer(length_bytes);
2351     EXPECT_EQ(18u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
2352                                             buf.data(), length_bytes));
2353     EXPECT_EQ(L"Zucchini", GetPlatformWString(buf.data()));
2354 
2355     // Indices out of range
2356     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), -1,
2357                                            nullptr, 0));
2358     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 26,
2359                                            nullptr, 0));
2360 
2361     // Check bad form handle / annot.
2362     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(nullptr, nullptr, 0, nullptr, 0));
2363     EXPECT_EQ(0u,
2364               FPDFAnnot_GetOptionLabel(nullptr, annot.get(), 0, nullptr, 0));
2365     EXPECT_EQ(0u,
2366               FPDFAnnot_GetOptionLabel(form_handle(), nullptr, 0, nullptr, 0));
2367   }
2368 
2369   UnloadPage(page);
2370 }
2371 
TEST_F(FPDFAnnotEmbedderTest,GetOptionLabelListbox)2372 TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelListbox) {
2373   // Open a file with listbox widget annotations and load its first page.
2374   ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
2375   FPDF_PAGE page = LoadPage(0);
2376   ASSERT_TRUE(page);
2377 
2378   {
2379     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2380     ASSERT_TRUE(annot);
2381 
2382     int index = 0;
2383     unsigned long length_bytes =
2384         FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
2385     ASSERT_EQ(8u, length_bytes);
2386     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2387     EXPECT_EQ(8u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
2388                                            buf.data(), length_bytes));
2389     EXPECT_EQ(L"Foo", GetPlatformWString(buf.data()));
2390 
2391     annot.reset(FPDFPage_GetAnnot(page, 1));
2392     ASSERT_TRUE(annot);
2393 
2394     index = 0;
2395     length_bytes =
2396         FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
2397     ASSERT_EQ(12u, length_bytes);
2398     buf = GetFPDFWideStringBuffer(length_bytes);
2399     EXPECT_EQ(12u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
2400                                             buf.data(), length_bytes));
2401     EXPECT_EQ(L"Apple", GetPlatformWString(buf.data()));
2402 
2403     index = 25;
2404     length_bytes =
2405         FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index, nullptr, 0);
2406     ASSERT_EQ(18u, length_bytes);
2407     buf = GetFPDFWideStringBuffer(length_bytes);
2408     EXPECT_EQ(18u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), index,
2409                                             buf.data(), length_bytes));
2410     EXPECT_EQ(L"Zucchini", GetPlatformWString(buf.data()));
2411 
2412     // indices out of range
2413     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), -1,
2414                                            nullptr, 0));
2415     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 26,
2416                                            nullptr, 0));
2417   }
2418 
2419   UnloadPage(page);
2420 }
2421 
TEST_F(FPDFAnnotEmbedderTest,GetOptionLabelInvalidAnnotations)2422 TEST_F(FPDFAnnotEmbedderTest, GetOptionLabelInvalidAnnotations) {
2423   // Open a file with ink annotations and load its first page.
2424   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
2425   FPDF_PAGE page = LoadPage(0);
2426   ASSERT_TRUE(page);
2427 
2428   {
2429     // annotations do not have "Opt" array and will return 0
2430     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2431     ASSERT_TRUE(annot);
2432 
2433     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 0,
2434                                            nullptr, 0));
2435 
2436     annot.reset(FPDFPage_GetAnnot(page, 1));
2437     ASSERT_TRUE(annot);
2438 
2439     EXPECT_EQ(0u, FPDFAnnot_GetOptionLabel(form_handle(), annot.get(), 0,
2440                                            nullptr, 0));
2441   }
2442 
2443   UnloadPage(page);
2444 }
2445 
TEST_F(FPDFAnnotEmbedderTest,IsOptionSelectedCombobox)2446 TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedCombobox) {
2447   // Open a file with combobox widget annotations and load its first page.
2448   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2449   FPDF_PAGE page = LoadPage(0);
2450   ASSERT_TRUE(page);
2451 
2452   {
2453     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2454     ASSERT_TRUE(annot);
2455 
2456     // Checks for Combobox with no Values (/V) or Selected Indices (/I) objects.
2457     int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2458     ASSERT_EQ(3, count);
2459     for (int i = 0; i < count; i++) {
2460       EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2461     }
2462 
2463     annot.reset(FPDFPage_GetAnnot(page, 1));
2464     ASSERT_TRUE(annot);
2465 
2466     // Checks for Combobox with Values (/V) object which is just a string.
2467     count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2468     ASSERT_EQ(26, count);
2469     for (int i = 0; i < count; i++) {
2470       EXPECT_EQ(i == 1,
2471                 FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2472     }
2473 
2474     // Checks for index outside bound i.e. (index >= CountOption()).
2475     EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
2476                                             /*index=*/26));
2477     // Checks for negetive index.
2478     EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
2479                                             /*index=*/-1));
2480 
2481     // Checks for bad form handle/annot.
2482     EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, nullptr, /*index=*/0));
2483     EXPECT_FALSE(
2484         FPDFAnnot_IsOptionSelected(form_handle(), nullptr, /*index=*/0));
2485     EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, annot.get(), /*index=*/0));
2486   }
2487 
2488   UnloadPage(page);
2489 }
2490 
TEST_F(FPDFAnnotEmbedderTest,IsOptionSelectedListbox)2491 TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedListbox) {
2492   // Open a file with listbox widget annotations and load its first page.
2493   ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
2494   FPDF_PAGE page = LoadPage(0);
2495   ASSERT_TRUE(page);
2496 
2497   {
2498     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2499     ASSERT_TRUE(annot);
2500 
2501     // Checks for Listbox with no Values (/V) or Selected Indices (/I) objects.
2502     int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2503     ASSERT_EQ(3, count);
2504     for (int i = 0; i < count; i++) {
2505       EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2506     }
2507 
2508     annot.reset(FPDFPage_GetAnnot(page, 1));
2509     ASSERT_TRUE(annot);
2510 
2511     // Checks for Listbox with Values (/V) object which is just a string.
2512     count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2513     ASSERT_EQ(26, count);
2514     for (int i = 0; i < count; i++) {
2515       EXPECT_EQ(i == 1,
2516                 FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2517     }
2518 
2519     annot.reset(FPDFPage_GetAnnot(page, 3));
2520     ASSERT_TRUE(annot);
2521 
2522     // Checks for Listbox with only Selected indices (/I) object which is an
2523     // array with multiple objects.
2524     count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2525     ASSERT_EQ(5, count);
2526     for (int i = 0; i < count; i++) {
2527       bool expected = (i == 1 || i == 3);
2528       EXPECT_EQ(expected,
2529                 FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2530     }
2531 
2532     annot.reset(FPDFPage_GetAnnot(page, 4));
2533     ASSERT_TRUE(annot);
2534 
2535     // Checks for Listbox with Values (/V) object which is an array with
2536     // multiple objects.
2537     count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2538     ASSERT_EQ(5, count);
2539     for (int i = 0; i < count; i++) {
2540       bool expected = (i == 2 || i == 4);
2541       EXPECT_EQ(expected,
2542                 FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2543     }
2544 
2545     annot.reset(FPDFPage_GetAnnot(page, 5));
2546     ASSERT_TRUE(annot);
2547 
2548     // Checks for Listbox with both Values (/V) and Selected Indices (/I)
2549     // objects conflict with different lengths.
2550     count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
2551     ASSERT_EQ(5, count);
2552     for (int i = 0; i < count; i++) {
2553       bool expected = (i == 0 || i == 2);
2554       EXPECT_EQ(expected,
2555                 FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
2556     }
2557   }
2558 
2559   UnloadPage(page);
2560 }
2561 
TEST_F(FPDFAnnotEmbedderTest,IsOptionSelectedInvalidAnnotations)2562 TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedInvalidAnnotations) {
2563   // Open a file with multiple form field annotations and load its first page.
2564   ASSERT_TRUE(OpenDocument("multiple_form_types.pdf"));
2565   FPDF_PAGE page = LoadPage(0);
2566   ASSERT_TRUE(page);
2567 
2568   {
2569     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2570     ASSERT_TRUE(annot);
2571 
2572     // Checks for link annotation.
2573     EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
2574                                             /*index=*/0));
2575 
2576     annot.reset(FPDFPage_GetAnnot(page, 3));
2577     ASSERT_TRUE(annot);
2578 
2579     // Checks for text field annotation.
2580     EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
2581                                             /*index=*/0));
2582   }
2583 
2584   UnloadPage(page);
2585 }
2586 
TEST_F(FPDFAnnotEmbedderTest,GetFontSizeCombobox)2587 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeCombobox) {
2588   // Open a file with combobox annotations and load its first page.
2589   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2590   FPDF_PAGE page = LoadPage(0);
2591   ASSERT_TRUE(page);
2592 
2593   {
2594     // All 3 widgets have Tf font size 12.
2595     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2596     ASSERT_TRUE(annot);
2597 
2598     float value;
2599     ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
2600     EXPECT_EQ(12.0, value);
2601 
2602     annot.reset(FPDFPage_GetAnnot(page, 1));
2603     ASSERT_TRUE(annot);
2604 
2605     float value_two;
2606     ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_two));
2607     EXPECT_EQ(12.0, value_two);
2608 
2609     annot.reset(FPDFPage_GetAnnot(page, 2));
2610     ASSERT_TRUE(annot);
2611 
2612     float value_three;
2613     ASSERT_TRUE(
2614         FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_three));
2615     EXPECT_EQ(12.0, value_three);
2616   }
2617 
2618   UnloadPage(page);
2619 }
2620 
TEST_F(FPDFAnnotEmbedderTest,GetFontSizeTextField)2621 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeTextField) {
2622   // Open a file with textfield annotations and load its first page.
2623   ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
2624   FPDF_PAGE page = LoadPage(0);
2625   ASSERT_TRUE(page);
2626 
2627   {
2628     // All 4 widgets have Tf font size 12.
2629     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2630     ASSERT_TRUE(annot);
2631 
2632     float value;
2633     ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
2634     EXPECT_EQ(12.0, value);
2635 
2636     annot.reset(FPDFPage_GetAnnot(page, 1));
2637     ASSERT_TRUE(annot);
2638 
2639     float value_two;
2640     ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_two));
2641     EXPECT_EQ(12.0, value_two);
2642 
2643     annot.reset(FPDFPage_GetAnnot(page, 2));
2644     ASSERT_TRUE(annot);
2645 
2646     float value_three;
2647     ASSERT_TRUE(
2648         FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_three));
2649     EXPECT_EQ(12.0, value_three);
2650 
2651     float value_four;
2652     ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value_four));
2653     EXPECT_EQ(12.0, value_four);
2654   }
2655 
2656   UnloadPage(page);
2657 }
2658 
TEST_F(FPDFAnnotEmbedderTest,GetFontSizeInvalidAnnotationTypes)2659 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeInvalidAnnotationTypes) {
2660   // Open a file with ink annotations and load its first page.
2661   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
2662   FPDF_PAGE page = LoadPage(0);
2663   ASSERT_TRUE(page);
2664 
2665   {
2666     // Annotations that do not have variable text and will return -1.
2667     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2668     ASSERT_TRUE(annot);
2669 
2670     float value;
2671     ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
2672 
2673     annot.reset(FPDFPage_GetAnnot(page, 1));
2674     ASSERT_TRUE(annot);
2675 
2676     ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
2677   }
2678 
2679   UnloadPage(page);
2680 }
2681 
TEST_F(FPDFAnnotEmbedderTest,GetFontSizeInvalidArguments)2682 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeInvalidArguments) {
2683   // Open a file with combobox annotations and load its first page.
2684   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2685   FPDF_PAGE page = LoadPage(0);
2686   ASSERT_TRUE(page);
2687 
2688   {
2689     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2690     ASSERT_TRUE(annot);
2691 
2692     // Check bad form handle / annot.
2693     float value;
2694     ASSERT_FALSE(FPDFAnnot_GetFontSize(nullptr, annot.get(), &value));
2695     ASSERT_FALSE(FPDFAnnot_GetFontSize(form_handle(), nullptr, &value));
2696     ASSERT_FALSE(FPDFAnnot_GetFontSize(nullptr, nullptr, &value));
2697   }
2698 
2699   UnloadPage(page);
2700 }
2701 
TEST_F(FPDFAnnotEmbedderTest,GetFontSizeNegative)2702 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeNegative) {
2703   // Open a file with textfield annotations and load its first page.
2704   ASSERT_TRUE(OpenDocument("text_form_negative_fontsize.pdf"));
2705   FPDF_PAGE page = LoadPage(0);
2706   ASSERT_TRUE(page);
2707 
2708   {
2709     // Obtain the first annotation, a text field with negative font size, -12.
2710     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2711     ASSERT_TRUE(annot);
2712 
2713     float value;
2714     ASSERT_TRUE(FPDFAnnot_GetFontSize(form_handle(), annot.get(), &value));
2715     EXPECT_EQ(-12.0, value);
2716   }
2717 
2718   UnloadPage(page);
2719 }
2720 
TEST_F(FPDFAnnotEmbedderTest,IsCheckedCheckbox)2721 TEST_F(FPDFAnnotEmbedderTest, IsCheckedCheckbox) {
2722   // Open a file with checkbox and radiobuttons widget annotations and load its
2723   // first page.
2724   ASSERT_TRUE(OpenDocument("click_form.pdf"));
2725   FPDF_PAGE page = LoadPage(0);
2726   ASSERT_TRUE(page);
2727 
2728   {
2729     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
2730     ASSERT_TRUE(annot);
2731     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2732   }
2733 
2734   UnloadPage(page);
2735 }
2736 
TEST_F(FPDFAnnotEmbedderTest,IsCheckedCheckboxReadOnly)2737 TEST_F(FPDFAnnotEmbedderTest, IsCheckedCheckboxReadOnly) {
2738   // Open a file with checkbox and radiobutton widget annotations and load its
2739   // first page.
2740   ASSERT_TRUE(OpenDocument("click_form.pdf"));
2741   FPDF_PAGE page = LoadPage(0);
2742   ASSERT_TRUE(page);
2743 
2744   {
2745     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2746     ASSERT_TRUE(annot);
2747     ASSERT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2748   }
2749 
2750   UnloadPage(page);
2751 }
2752 
TEST_F(FPDFAnnotEmbedderTest,IsCheckedRadioButton)2753 TEST_F(FPDFAnnotEmbedderTest, IsCheckedRadioButton) {
2754   // Open a file with checkbox and radiobutton widget annotations and load its
2755   // first page.
2756   ASSERT_TRUE(OpenDocument("click_form.pdf"));
2757   FPDF_PAGE page = LoadPage(0);
2758   ASSERT_TRUE(page);
2759 
2760   {
2761     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 5));
2762     ASSERT_TRUE(annot);
2763     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2764 
2765     annot.reset(FPDFPage_GetAnnot(page, 6));
2766     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2767 
2768     annot.reset(FPDFPage_GetAnnot(page, 7));
2769     ASSERT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2770   }
2771 
2772   UnloadPage(page);
2773 }
2774 
TEST_F(FPDFAnnotEmbedderTest,IsCheckedRadioButtonReadOnly)2775 TEST_F(FPDFAnnotEmbedderTest, IsCheckedRadioButtonReadOnly) {
2776   // Open a file with checkbox and radiobutton widget annotations and load its
2777   // first page.
2778   ASSERT_TRUE(OpenDocument("click_form.pdf"));
2779   FPDF_PAGE page = LoadPage(0);
2780   ASSERT_TRUE(page);
2781 
2782   {
2783     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
2784     ASSERT_TRUE(annot);
2785     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2786 
2787     annot.reset(FPDFPage_GetAnnot(page, 3));
2788     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2789 
2790     annot.reset(FPDFPage_GetAnnot(page, 4));
2791     ASSERT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2792   }
2793 
2794   UnloadPage(page);
2795 }
2796 
TEST_F(FPDFAnnotEmbedderTest,IsCheckedInvalidArguments)2797 TEST_F(FPDFAnnotEmbedderTest, IsCheckedInvalidArguments) {
2798   // Open a file with checkbox and radiobuttons widget annotations and load its
2799   // first page.
2800   ASSERT_TRUE(OpenDocument("click_form.pdf"));
2801   FPDF_PAGE page = LoadPage(0);
2802   ASSERT_TRUE(page);
2803 
2804   {
2805     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2806     ASSERT_TRUE(annot);
2807     ASSERT_FALSE(FPDFAnnot_IsChecked(nullptr, annot.get()));
2808     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), nullptr));
2809     ASSERT_FALSE(FPDFAnnot_IsChecked(nullptr, nullptr));
2810   }
2811 
2812   UnloadPage(page);
2813 }
2814 
TEST_F(FPDFAnnotEmbedderTest,IsCheckedInvalidWidgetType)2815 TEST_F(FPDFAnnotEmbedderTest, IsCheckedInvalidWidgetType) {
2816   // Open a file with text widget annotations and load its first page.
2817   ASSERT_TRUE(OpenDocument("text_form.pdf"));
2818   FPDF_PAGE page = LoadPage(0);
2819   ASSERT_TRUE(page);
2820 
2821   {
2822     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2823     ASSERT_TRUE(annot);
2824     ASSERT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot.get()));
2825   }
2826 
2827   UnloadPage(page);
2828 }
2829 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldType)2830 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldType) {
2831   ASSERT_TRUE(OpenDocument("multiple_form_types.pdf"));
2832   FPDF_PAGE page = LoadPage(0);
2833   ASSERT_TRUE(page);
2834 
2835   EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(form_handle(), nullptr));
2836 
2837   {
2838     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
2839     ASSERT_TRUE(annot);
2840     EXPECT_EQ(-1, FPDFAnnot_GetFormFieldType(nullptr, annot.get()));
2841   }
2842 
2843   constexpr int kExpectedAnnotTypes[] = {-1,
2844                                          FPDF_FORMFIELD_COMBOBOX,
2845                                          FPDF_FORMFIELD_LISTBOX,
2846                                          FPDF_FORMFIELD_TEXTFIELD,
2847                                          FPDF_FORMFIELD_CHECKBOX,
2848                                          FPDF_FORMFIELD_RADIOBUTTON};
2849 
2850   for (size_t i = 0; i < std::size(kExpectedAnnotTypes); ++i) {
2851     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, i));
2852     ASSERT_TRUE(annot);
2853     EXPECT_EQ(kExpectedAnnotTypes[i],
2854               FPDFAnnot_GetFormFieldType(form_handle(), annot.get()));
2855   }
2856   UnloadPage(page);
2857 }
2858 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldValueTextField)2859 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldValueTextField) {
2860   ASSERT_TRUE(OpenDocument("text_form_multiple.pdf"));
2861   FPDF_PAGE page = LoadPage(0);
2862   ASSERT_TRUE(page);
2863 
2864   {
2865     EXPECT_EQ(0u,
2866               FPDFAnnot_GetFormFieldValue(form_handle(), nullptr, nullptr, 0));
2867 
2868     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2869     ASSERT_TRUE(annot);
2870 
2871     EXPECT_EQ(0u,
2872               FPDFAnnot_GetFormFieldValue(nullptr, annot.get(), nullptr, 0));
2873 
2874     unsigned long length_bytes =
2875         FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
2876     ASSERT_EQ(2u, length_bytes);
2877     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2878     EXPECT_EQ(2u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
2879                                               buf.data(), length_bytes));
2880     EXPECT_EQ(L"", GetPlatformWString(buf.data()));
2881   }
2882   {
2883     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
2884     ASSERT_TRUE(annot);
2885 
2886     unsigned long length_bytes =
2887         FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
2888     ASSERT_EQ(18u, length_bytes);
2889     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2890     EXPECT_EQ(18u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
2891                                                buf.data(), length_bytes));
2892     EXPECT_EQ(L"Elephant", GetPlatformWString(buf.data()));
2893   }
2894   UnloadPage(page);
2895 }
2896 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldValueComboBox)2897 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldValueComboBox) {
2898   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2899   FPDF_PAGE page = LoadPage(0);
2900   ASSERT_TRUE(page);
2901 
2902   {
2903     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2904     ASSERT_TRUE(annot);
2905 
2906     unsigned long length_bytes =
2907         FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
2908     ASSERT_EQ(2u, length_bytes);
2909     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2910     EXPECT_EQ(2u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
2911                                               buf.data(), length_bytes));
2912     EXPECT_EQ(L"", GetPlatformWString(buf.data()));
2913   }
2914   {
2915     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
2916     ASSERT_TRUE(annot);
2917 
2918     unsigned long length_bytes =
2919         FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(), nullptr, 0);
2920     ASSERT_EQ(14u, length_bytes);
2921     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2922     EXPECT_EQ(14u, FPDFAnnot_GetFormFieldValue(form_handle(), annot.get(),
2923                                                buf.data(), length_bytes));
2924     EXPECT_EQ(L"Banana", GetPlatformWString(buf.data()));
2925   }
2926   UnloadPage(page);
2927 }
2928 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldNameTextField)2929 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldNameTextField) {
2930   ASSERT_TRUE(OpenDocument("text_form.pdf"));
2931   FPDF_PAGE page = LoadPage(0);
2932   ASSERT_TRUE(page);
2933 
2934   {
2935     EXPECT_EQ(0u,
2936               FPDFAnnot_GetFormFieldName(form_handle(), nullptr, nullptr, 0));
2937 
2938     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2939     ASSERT_TRUE(annot);
2940 
2941     EXPECT_EQ(0u, FPDFAnnot_GetFormFieldName(nullptr, annot.get(), nullptr, 0));
2942 
2943     unsigned long length_bytes =
2944         FPDFAnnot_GetFormFieldName(form_handle(), annot.get(), nullptr, 0);
2945     ASSERT_EQ(18u, length_bytes);
2946     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2947     EXPECT_EQ(18u, FPDFAnnot_GetFormFieldName(form_handle(), annot.get(),
2948                                               buf.data(), length_bytes));
2949     EXPECT_EQ(L"Text Box", GetPlatformWString(buf.data()));
2950   }
2951   UnloadPage(page);
2952 }
2953 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldNameComboBox)2954 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldNameComboBox) {
2955   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
2956   FPDF_PAGE page = LoadPage(0);
2957   ASSERT_TRUE(page);
2958 
2959   {
2960     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
2961     ASSERT_TRUE(annot);
2962 
2963     unsigned long length_bytes =
2964         FPDFAnnot_GetFormFieldName(form_handle(), annot.get(), nullptr, 0);
2965     ASSERT_EQ(30u, length_bytes);
2966     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
2967     EXPECT_EQ(30u, FPDFAnnot_GetFormFieldName(form_handle(), annot.get(),
2968                                               buf.data(), length_bytes));
2969     EXPECT_EQ(L"Combo_Editable", GetPlatformWString(buf.data()));
2970   }
2971   UnloadPage(page);
2972 }
2973 
TEST_F(FPDFAnnotEmbedderTest,FocusableAnnotSubtypes)2974 TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotSubtypes) {
2975   ASSERT_TRUE(OpenDocument("annots.pdf"));
2976   FPDF_PAGE page = LoadPage(0);
2977   ASSERT_TRUE(page);
2978 
2979   // Verify widgets are by default focusable.
2980   const FPDF_ANNOTATION_SUBTYPE kDefaultSubtypes[] = {FPDF_ANNOT_WIDGET};
2981   VerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes);
2982 
2983   // Expected annot subtypes for page 0 of annots.pdf.
2984   const FPDF_ANNOTATION_SUBTYPE kExpectedAnnotSubtypes[] = {
2985       FPDF_ANNOT_LINK,  FPDF_ANNOT_LINK,      FPDF_ANNOT_LINK,
2986       FPDF_ANNOT_LINK,  FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_HIGHLIGHT,
2987       FPDF_ANNOT_POPUP, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_WIDGET,
2988   };
2989 
2990   const FPDF_ANNOTATION_SUBTYPE kExpectedDefaultFocusableSubtypes[] = {
2991       FPDF_ANNOT_WIDGET};
2992   VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
2993                                           kExpectedAnnotSubtypes,
2994                                           kExpectedDefaultFocusableSubtypes);
2995 
2996   // Make no annotation type focusable using the preferred method.
2997   ASSERT_TRUE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr, 0));
2998   ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
2999 
3000   // Restore the focusable type count back to 1, then set it back to 0 using a
3001   // different method.
3002   SetAndVerifyFocusableAnnotSubtypes(form_handle(), kDefaultSubtypes);
3003   ASSERT_TRUE(
3004       FPDFAnnot_SetFocusableSubtypes(form_handle(), kDefaultSubtypes, 0));
3005   ASSERT_EQ(0, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
3006 
3007   VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
3008                                           kExpectedAnnotSubtypes, {});
3009 
3010   // Now make links focusable.
3011   const FPDF_ANNOTATION_SUBTYPE kLinkSubtypes[] = {FPDF_ANNOT_LINK};
3012   SetAndVerifyFocusableAnnotSubtypes(form_handle(), kLinkSubtypes);
3013 
3014   const FPDF_ANNOTATION_SUBTYPE kExpectedLinkocusableSubtypes[] = {
3015       FPDF_ANNOT_LINK};
3016   VerifyAnnotationSubtypesAndFocusability(form_handle(), page,
3017                                           kExpectedAnnotSubtypes,
3018                                           kExpectedLinkocusableSubtypes);
3019 
3020   // Test invalid parameters.
3021   EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(nullptr, kDefaultSubtypes,
3022                                               std::size(kDefaultSubtypes)));
3023   EXPECT_FALSE(FPDFAnnot_SetFocusableSubtypes(form_handle(), nullptr,
3024                                               std::size(kDefaultSubtypes)));
3025   EXPECT_EQ(-1, FPDFAnnot_GetFocusableSubtypesCount(nullptr));
3026 
3027   std::vector<FPDF_ANNOTATION_SUBTYPE> subtypes(1);
3028   EXPECT_FALSE(FPDFAnnot_GetFocusableSubtypes(nullptr, subtypes.data(),
3029                                               subtypes.size()));
3030   EXPECT_FALSE(
3031       FPDFAnnot_GetFocusableSubtypes(form_handle(), nullptr, subtypes.size()));
3032   EXPECT_FALSE(
3033       FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(), 0));
3034 
3035   UnloadPage(page);
3036 }
3037 
TEST_F(FPDFAnnotEmbedderTest,FocusableAnnotRendering)3038 TEST_F(FPDFAnnotEmbedderTest, FocusableAnnotRendering) {
3039   ASSERT_TRUE(OpenDocument("annots.pdf"));
3040   FPDF_PAGE page = LoadPage(0);
3041   ASSERT_TRUE(page);
3042 
3043   {
3044     const char* md5_sum = []() {
3045       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
3046         return "c09b129c071ec1569deb003676b617b0";
3047 #if BUILDFLAG(IS_APPLE)
3048       return "108a46c517c4eaace9982ee83e8e3296";
3049 #else
3050       return "5550d8dcb4d1af1f50e8b4bcaef2ee60";
3051 #endif
3052     }();
3053     // Check the initial rendering.
3054     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
3055     CompareBitmap(bitmap.get(), 612, 792, md5_sum);
3056   }
3057 
3058   // Make links and highlights focusable.
3059   static constexpr FPDF_ANNOTATION_SUBTYPE kSubTypes[] = {FPDF_ANNOT_LINK,
3060                                                           FPDF_ANNOT_HIGHLIGHT};
3061   constexpr int kSubTypesCount = std::size(kSubTypes);
3062   ASSERT_TRUE(
3063       FPDFAnnot_SetFocusableSubtypes(form_handle(), kSubTypes, kSubTypesCount));
3064   ASSERT_EQ(kSubTypesCount, FPDFAnnot_GetFocusableSubtypesCount(form_handle()));
3065   std::vector<FPDF_ANNOTATION_SUBTYPE> subtypes(kSubTypesCount);
3066   ASSERT_TRUE(FPDFAnnot_GetFocusableSubtypes(form_handle(), subtypes.data(),
3067                                              subtypes.size()));
3068   ASSERT_EQ(FPDF_ANNOT_LINK, subtypes[0]);
3069   ASSERT_EQ(FPDF_ANNOT_HIGHLIGHT, subtypes[1]);
3070 
3071   {
3072     const char* md5_sum = []() {
3073       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
3074         return "277f1b9e70031539d034d22bc6064838";
3075 #if BUILDFLAG(IS_APPLE)
3076       return "eb3869335e7a219e1b5f25c1c6037b97";
3077 #else
3078       return "805fe7bb751ac4ed2b82bb66efe6db40";
3079 #endif
3080     }();
3081     // Focus the first link and check the rendering.
3082     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3083     ASSERT_TRUE(annot);
3084     EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
3085     EXPECT_TRUE(FORM_SetFocusedAnnot(form_handle(), annot.get()));
3086     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
3087     CompareBitmap(bitmap.get(), 612, 792, md5_sum);
3088   }
3089 
3090   {
3091     const char* md5_sum = []() {
3092       if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
3093         return "d980005939cd4ae0a199d8600a0abdf3";
3094 #if BUILDFLAG(IS_APPLE)
3095       return "d20b1978da2362d3942ea0fc6d230997";
3096 #else
3097       return "c5c5dcb462af3ef5f43b298ec048feef";
3098 #endif
3099     }();
3100     // Focus the first highlight and check the rendering.
3101     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 4));
3102     ASSERT_TRUE(annot);
3103     EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
3104     EXPECT_TRUE(FORM_SetFocusedAnnot(form_handle(), annot.get()));
3105     ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
3106     CompareBitmap(bitmap.get(), 612, 792, md5_sum);
3107   }
3108 
3109   UnloadPage(page);
3110 }
3111 
TEST_F(FPDFAnnotEmbedderTest,GetLinkFromAnnotation)3112 TEST_F(FPDFAnnotEmbedderTest, GetLinkFromAnnotation) {
3113   ASSERT_TRUE(OpenDocument("annots.pdf"));
3114   FPDF_PAGE page = LoadPage(0);
3115   ASSERT_TRUE(page);
3116   {
3117     constexpr char kExpectedResult[] =
3118         "https://cs.chromium.org/chromium/src/third_party/pdfium/public/"
3119         "fpdf_text.h";
3120 
3121     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
3122     ASSERT_TRUE(annot);
3123     EXPECT_EQ(FPDF_ANNOT_LINK, FPDFAnnot_GetSubtype(annot.get()));
3124     VerifyUriActionInLink(document(), FPDFAnnot_GetLink(annot.get()),
3125                           kExpectedResult);
3126   }
3127 
3128   {
3129     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 4));
3130     ASSERT_TRUE(annot);
3131     EXPECT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot.get()));
3132     EXPECT_FALSE(FPDFAnnot_GetLink(annot.get()));
3133   }
3134 
3135   EXPECT_FALSE(FPDFAnnot_GetLink(nullptr));
3136 
3137   UnloadPage(page);
3138 }
3139 
TEST_F(FPDFAnnotEmbedderTest,GetFormControlCountRadioButton)3140 TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountRadioButton) {
3141   // Open a file with radio button widget annotations and load its first page.
3142   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3143   FPDF_PAGE page = LoadPage(0);
3144   ASSERT_TRUE(page);
3145 
3146   {
3147     // Checks for bad annot.
3148     EXPECT_EQ(-1,
3149               FPDFAnnot_GetFormControlCount(form_handle(), /*annot=*/nullptr));
3150 
3151     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
3152     ASSERT_TRUE(annot);
3153 
3154     // Checks for bad form handle.
3155     EXPECT_EQ(-1,
3156               FPDFAnnot_GetFormControlCount(/*hHandle=*/nullptr, annot.get()));
3157 
3158     EXPECT_EQ(3, FPDFAnnot_GetFormControlCount(form_handle(), annot.get()));
3159   }
3160 
3161   UnloadPage(page);
3162 }
3163 
TEST_F(FPDFAnnotEmbedderTest,GetFormControlCountCheckBox)3164 TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountCheckBox) {
3165   // Open a file with checkbox widget annotations and load its first page.
3166   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3167   FPDF_PAGE page = LoadPage(0);
3168   ASSERT_TRUE(page);
3169 
3170   {
3171     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3172     ASSERT_TRUE(annot);
3173     EXPECT_EQ(1, FPDFAnnot_GetFormControlCount(form_handle(), annot.get()));
3174   }
3175 
3176   UnloadPage(page);
3177 }
3178 
TEST_F(FPDFAnnotEmbedderTest,GetFormControlCountInvalidAnnotation)3179 TEST_F(FPDFAnnotEmbedderTest, GetFormControlCountInvalidAnnotation) {
3180   // Open a file with ink annotations and load its first page.
3181   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
3182   FPDF_PAGE page = LoadPage(0);
3183   ASSERT_TRUE(page);
3184 
3185   {
3186     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3187     ASSERT_TRUE(annot);
3188     EXPECT_EQ(-1, FPDFAnnot_GetFormControlCount(form_handle(), annot.get()));
3189   }
3190 
3191   UnloadPage(page);
3192 }
3193 
TEST_F(FPDFAnnotEmbedderTest,GetFormControlIndexRadioButton)3194 TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexRadioButton) {
3195   // Open a file with radio button widget annotations and load its first page.
3196   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3197   FPDF_PAGE page = LoadPage(0);
3198   ASSERT_TRUE(page);
3199 
3200   {
3201     // Checks for bad annot.
3202     EXPECT_EQ(-1,
3203               FPDFAnnot_GetFormControlIndex(form_handle(), /*annot=*/nullptr));
3204 
3205     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 3));
3206     ASSERT_TRUE(annot);
3207 
3208     // Checks for bad form handle.
3209     EXPECT_EQ(-1,
3210               FPDFAnnot_GetFormControlIndex(/*hHandle=*/nullptr, annot.get()));
3211 
3212     EXPECT_EQ(1, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get()));
3213   }
3214 
3215   UnloadPage(page);
3216 }
3217 
TEST_F(FPDFAnnotEmbedderTest,GetFormControlIndexCheckBox)3218 TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexCheckBox) {
3219   // Open a file with checkbox widget annotations and load its first page.
3220   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3221   FPDF_PAGE page = LoadPage(0);
3222   ASSERT_TRUE(page);
3223 
3224   {
3225     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3226     ASSERT_TRUE(annot);
3227     EXPECT_EQ(0, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get()));
3228   }
3229 
3230   UnloadPage(page);
3231 }
3232 
TEST_F(FPDFAnnotEmbedderTest,GetFormControlIndexInvalidAnnotation)3233 TEST_F(FPDFAnnotEmbedderTest, GetFormControlIndexInvalidAnnotation) {
3234   // Open a file with ink annotations and load its first page.
3235   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
3236   FPDF_PAGE page = LoadPage(0);
3237   ASSERT_TRUE(page);
3238 
3239   {
3240     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3241     ASSERT_TRUE(annot);
3242     EXPECT_EQ(-1, FPDFAnnot_GetFormControlIndex(form_handle(), annot.get()));
3243   }
3244 
3245   UnloadPage(page);
3246 }
3247 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldExportValueRadioButton)3248 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueRadioButton) {
3249   // Open a file with radio button widget annotations and load its first page.
3250   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3251   FPDF_PAGE page = LoadPage(0);
3252   ASSERT_TRUE(page);
3253 
3254   {
3255     // Checks for bad annot.
3256     EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(
3257                       form_handle(), /*annot=*/nullptr,
3258                       /*buffer=*/nullptr, /*buflen=*/0));
3259 
3260     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 6));
3261     ASSERT_TRUE(annot);
3262 
3263     // Checks for bad form handle.
3264     EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(
3265                       /*hHandle=*/nullptr, annot.get(),
3266                       /*buffer=*/nullptr, /*buflen=*/0));
3267 
3268     unsigned long length_bytes =
3269         FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
3270                                           /*buffer=*/nullptr, /*buflen=*/0);
3271     ASSERT_EQ(14u, length_bytes);
3272     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
3273     EXPECT_EQ(14u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
3274                                                      buf.data(), length_bytes));
3275     EXPECT_EQ(L"value2", GetPlatformWString(buf.data()));
3276   }
3277 
3278   UnloadPage(page);
3279 }
3280 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldExportValueCheckBox)3281 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueCheckBox) {
3282   // Open a file with checkbox widget annotations and load its first page.
3283   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3284   FPDF_PAGE page = LoadPage(0);
3285   ASSERT_TRUE(page);
3286 
3287   {
3288     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3289     ASSERT_TRUE(annot);
3290 
3291     unsigned long length_bytes =
3292         FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
3293                                           /*buffer=*/nullptr, /*buflen=*/0);
3294     ASSERT_EQ(8u, length_bytes);
3295     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
3296     EXPECT_EQ(8u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
3297                                                     buf.data(), length_bytes));
3298     EXPECT_EQ(L"Yes", GetPlatformWString(buf.data()));
3299   }
3300 
3301   UnloadPage(page);
3302 }
3303 
TEST_F(FPDFAnnotEmbedderTest,GetFormFieldExportValueInvalidAnnotation)3304 TEST_F(FPDFAnnotEmbedderTest, GetFormFieldExportValueInvalidAnnotation) {
3305   // Open a file with ink annotations and load its first page.
3306   ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
3307   FPDF_PAGE page = LoadPage(0);
3308   ASSERT_TRUE(page);
3309 
3310   {
3311     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3312     ASSERT_TRUE(annot);
3313     EXPECT_EQ(0u, FPDFAnnot_GetFormFieldExportValue(form_handle(), annot.get(),
3314                                                     /*buffer=*/nullptr,
3315                                                     /*buflen=*/0));
3316   }
3317 
3318   UnloadPage(page);
3319 }
3320 
TEST_F(FPDFAnnotEmbedderTest,Redactannotation)3321 TEST_F(FPDFAnnotEmbedderTest, Redactannotation) {
3322   ASSERT_TRUE(OpenDocument("redact_annot.pdf"));
3323   FPDF_PAGE page = LoadPage(0);
3324   ASSERT_TRUE(page);
3325   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
3326 
3327   {
3328     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3329     ASSERT_TRUE(annot);
3330     EXPECT_EQ(FPDF_ANNOT_REDACT, FPDFAnnot_GetSubtype(annot.get()));
3331   }
3332 
3333   UnloadPage(page);
3334 }
3335 
TEST_F(FPDFAnnotEmbedderTest,PolygonAnnotation)3336 TEST_F(FPDFAnnotEmbedderTest, PolygonAnnotation) {
3337   ASSERT_TRUE(OpenDocument("polygon_annot.pdf"));
3338   FPDF_PAGE page = LoadPage(0);
3339   ASSERT_TRUE(page);
3340   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
3341 
3342   {
3343     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3344     ASSERT_TRUE(annot);
3345 
3346     // FPDFAnnot_GetVertices() positive testing.
3347     unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
3348     const size_t kExpectedSize = 3;
3349     ASSERT_EQ(kExpectedSize, size);
3350     std::vector<FS_POINTF> vertices_buffer(size);
3351     EXPECT_EQ(size,
3352               FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size));
3353     EXPECT_FLOAT_EQ(159.0f, vertices_buffer[0].x);
3354     EXPECT_FLOAT_EQ(296.0f, vertices_buffer[0].y);
3355     EXPECT_FLOAT_EQ(350.0f, vertices_buffer[1].x);
3356     EXPECT_FLOAT_EQ(411.0f, vertices_buffer[1].y);
3357     EXPECT_FLOAT_EQ(472.0f, vertices_buffer[2].x);
3358     EXPECT_FLOAT_EQ(243.42f, vertices_buffer[2].y);
3359 
3360     // FPDFAnnot_GetVertices() negative testing.
3361     EXPECT_EQ(0U, FPDFAnnot_GetVertices(nullptr, nullptr, 0));
3362 
3363     // vertices_buffer is not overwritten if it is too small.
3364     vertices_buffer.resize(1);
3365     vertices_buffer[0].x = 42;
3366     vertices_buffer[0].y = 43;
3367     size = FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(),
3368                                  vertices_buffer.size());
3369     EXPECT_EQ(kExpectedSize, size);
3370     EXPECT_FLOAT_EQ(42, vertices_buffer[0].x);
3371     EXPECT_FLOAT_EQ(43, vertices_buffer[0].y);
3372   }
3373 
3374   {
3375     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
3376     ASSERT_TRUE(annot);
3377 
3378     // This has an odd number of elements in the vertices array, ignore the last
3379     // element.
3380     unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
3381     const size_t kExpectedSize = 3;
3382     ASSERT_EQ(kExpectedSize, size);
3383     std::vector<FS_POINTF> vertices_buffer(size);
3384     EXPECT_EQ(size,
3385               FPDFAnnot_GetVertices(annot.get(), vertices_buffer.data(), size));
3386     EXPECT_FLOAT_EQ(259.0f, vertices_buffer[0].x);
3387     EXPECT_FLOAT_EQ(396.0f, vertices_buffer[0].y);
3388     EXPECT_FLOAT_EQ(450.0f, vertices_buffer[1].x);
3389     EXPECT_FLOAT_EQ(511.0f, vertices_buffer[1].y);
3390     EXPECT_FLOAT_EQ(572.0f, vertices_buffer[2].x);
3391     EXPECT_FLOAT_EQ(343.0f, vertices_buffer[2].y);
3392   }
3393 
3394   {
3395     // Wrong annotation type.
3396     ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
3397     EXPECT_EQ(0U, FPDFAnnot_GetVertices(ink_annot.get(), nullptr, 0));
3398   }
3399 
3400   UnloadPage(page);
3401 }
3402 
TEST_F(FPDFAnnotEmbedderTest,InkAnnotation)3403 TEST_F(FPDFAnnotEmbedderTest, InkAnnotation) {
3404   ASSERT_TRUE(OpenDocument("ink_annot.pdf"));
3405   FPDF_PAGE page = LoadPage(0);
3406   ASSERT_TRUE(page);
3407   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
3408 
3409   {
3410     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3411     ASSERT_TRUE(annot);
3412 
3413     // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() positive
3414     // testing.
3415     unsigned long size = FPDFAnnot_GetInkListCount(annot.get());
3416     const size_t kExpectedSize = 1;
3417     ASSERT_EQ(kExpectedSize, size);
3418     const unsigned long kPathIndex = 0;
3419     unsigned long path_size =
3420         FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0);
3421     const size_t kExpectedPathSize = 3;
3422     ASSERT_EQ(kExpectedPathSize, path_size);
3423     std::vector<FS_POINTF> path_buffer(path_size);
3424     EXPECT_EQ(path_size,
3425               FPDFAnnot_GetInkListPath(annot.get(), kPathIndex,
3426                                        path_buffer.data(), path_size));
3427     EXPECT_FLOAT_EQ(159.0f, path_buffer[0].x);
3428     EXPECT_FLOAT_EQ(296.0f, path_buffer[0].y);
3429     EXPECT_FLOAT_EQ(350.0f, path_buffer[1].x);
3430     EXPECT_FLOAT_EQ(411.0f, path_buffer[1].y);
3431     EXPECT_FLOAT_EQ(472.0f, path_buffer[2].x);
3432     EXPECT_FLOAT_EQ(243.42f, path_buffer[2].y);
3433 
3434     // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() negative
3435     // testing.
3436     EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(nullptr));
3437     EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 0, nullptr, 0));
3438 
3439     // out of bounds path_index.
3440     EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 42, nullptr, 0));
3441 
3442     // path_buffer is not overwritten if it is too small.
3443     path_buffer.resize(1);
3444     path_buffer[0].x = 42;
3445     path_buffer[0].y = 43;
3446     path_size = FPDFAnnot_GetInkListPath(
3447         annot.get(), kPathIndex, path_buffer.data(), path_buffer.size());
3448     EXPECT_EQ(kExpectedPathSize, path_size);
3449     EXPECT_FLOAT_EQ(42, path_buffer[0].x);
3450     EXPECT_FLOAT_EQ(43, path_buffer[0].y);
3451   }
3452 
3453   {
3454     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
3455     ASSERT_TRUE(annot);
3456 
3457     // This has an odd number of elements in the path array, ignore the last
3458     // element.
3459     unsigned long size = FPDFAnnot_GetInkListCount(annot.get());
3460     const size_t kExpectedSize = 1;
3461     ASSERT_EQ(kExpectedSize, size);
3462     const unsigned long kPathIndex = 0;
3463     unsigned long path_size =
3464         FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0);
3465     const size_t kExpectedPathSize = 3;
3466     ASSERT_EQ(kExpectedPathSize, path_size);
3467     std::vector<FS_POINTF> path_buffer(path_size);
3468     EXPECT_EQ(path_size,
3469               FPDFAnnot_GetInkListPath(annot.get(), kPathIndex,
3470                                        path_buffer.data(), path_size));
3471     EXPECT_FLOAT_EQ(259.0f, path_buffer[0].x);
3472     EXPECT_FLOAT_EQ(396.0f, path_buffer[0].y);
3473     EXPECT_FLOAT_EQ(450.0f, path_buffer[1].x);
3474     EXPECT_FLOAT_EQ(511.0f, path_buffer[1].y);
3475     EXPECT_FLOAT_EQ(572.0f, path_buffer[2].x);
3476     EXPECT_FLOAT_EQ(343.0f, path_buffer[2].y);
3477   }
3478 
3479   {
3480     // Wrong annotation type.
3481     ScopedFPDFAnnotation polygon_annot(
3482         FPDFPage_CreateAnnot(page, FPDF_ANNOT_POLYGON));
3483     EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(polygon_annot.get()));
3484     const unsigned long kPathIndex = 0;
3485     EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(polygon_annot.get(), kPathIndex,
3486                                            nullptr, 0));
3487   }
3488 
3489   UnloadPage(page);
3490 }
3491 
TEST_F(FPDFAnnotEmbedderTest,LineAnnotation)3492 TEST_F(FPDFAnnotEmbedderTest, LineAnnotation) {
3493   ASSERT_TRUE(OpenDocument("line_annot.pdf"));
3494   FPDF_PAGE page = LoadPage(0);
3495   ASSERT_TRUE(page);
3496   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
3497 
3498   {
3499     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3500     ASSERT_TRUE(annot);
3501 
3502     // FPDFAnnot_GetVertices() positive testing.
3503     FS_POINTF start;
3504     FS_POINTF end;
3505     ASSERT_TRUE(FPDFAnnot_GetLine(annot.get(), &start, &end));
3506     EXPECT_FLOAT_EQ(159.0f, start.x);
3507     EXPECT_FLOAT_EQ(296.0f, start.y);
3508     EXPECT_FLOAT_EQ(472.0f, end.x);
3509     EXPECT_FLOAT_EQ(243.42f, end.y);
3510 
3511     // FPDFAnnot_GetVertices() negative testing.
3512     EXPECT_FALSE(FPDFAnnot_GetLine(nullptr, nullptr, nullptr));
3513     EXPECT_FALSE(FPDFAnnot_GetLine(annot.get(), nullptr, nullptr));
3514   }
3515 
3516   {
3517     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
3518     ASSERT_TRUE(annot);
3519 
3520     // Too few elements in the line array.
3521     FS_POINTF start;
3522     FS_POINTF end;
3523     EXPECT_FALSE(FPDFAnnot_GetLine(annot.get(), &start, &end));
3524   }
3525 
3526   {
3527     // Wrong annotation type.
3528     ScopedFPDFAnnotation ink_annot(FPDFPage_CreateAnnot(page, FPDF_ANNOT_INK));
3529     FS_POINTF start;
3530     FS_POINTF end;
3531     EXPECT_FALSE(FPDFAnnot_GetLine(ink_annot.get(), &start, &end));
3532   }
3533 
3534   UnloadPage(page);
3535 }
3536 
TEST_F(FPDFAnnotEmbedderTest,AnnotationBorder)3537 TEST_F(FPDFAnnotEmbedderTest, AnnotationBorder) {
3538   ASSERT_TRUE(OpenDocument("line_annot.pdf"));
3539   FPDF_PAGE page = LoadPage(0);
3540   ASSERT_TRUE(page);
3541   EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
3542 
3543   {
3544     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3545     ASSERT_TRUE(annot);
3546 
3547     // FPDFAnnot_GetBorder() positive testing.
3548     float horizontal_radius;
3549     float vertical_radius;
3550     float border_width;
3551     ASSERT_TRUE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius,
3552                                     &vertical_radius, &border_width));
3553     EXPECT_FLOAT_EQ(0.25f, horizontal_radius);
3554     EXPECT_FLOAT_EQ(0.5f, vertical_radius);
3555     EXPECT_FLOAT_EQ(2.0f, border_width);
3556 
3557     // FPDFAnnot_GetBorder() negative testing.
3558     EXPECT_FALSE(FPDFAnnot_GetBorder(nullptr, nullptr, nullptr, nullptr));
3559     EXPECT_FALSE(FPDFAnnot_GetBorder(annot.get(), nullptr, nullptr, nullptr));
3560   }
3561 
3562   {
3563     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
3564     ASSERT_TRUE(annot);
3565 
3566     // Too few elements in the border array.
3567     float horizontal_radius;
3568     float vertical_radius;
3569     float border_width;
3570     EXPECT_FALSE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius,
3571                                      &vertical_radius, &border_width));
3572 
3573     // FPDFAnnot_SetBorder() positive testing.
3574     EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f,
3575                                     /*vertical_radius=*/3.5f,
3576                                     /*border_width=*/4.0f));
3577 
3578     EXPECT_TRUE(FPDFAnnot_GetBorder(annot.get(), &horizontal_radius,
3579                                     &vertical_radius, &border_width));
3580     EXPECT_FLOAT_EQ(2.0f, horizontal_radius);
3581     EXPECT_FLOAT_EQ(3.5f, vertical_radius);
3582     EXPECT_FLOAT_EQ(4.0f, border_width);
3583 
3584     // FPDFAnnot_SetBorder() negative testing.
3585     EXPECT_FALSE(FPDFAnnot_SetBorder(nullptr, /*horizontal_radius=*/1.0f,
3586                                      /*vertical_radius=*/2.5f,
3587                                      /*border_width=*/3.0f));
3588   }
3589 
3590   UnloadPage(page);
3591 }
3592 
TEST_F(FPDFAnnotEmbedderTest,AnnotationJavaScript)3593 TEST_F(FPDFAnnotEmbedderTest, AnnotationJavaScript) {
3594   ASSERT_TRUE(OpenDocument("annot_javascript.pdf"));
3595   FPDF_PAGE page = LoadPage(0);
3596   ASSERT_TRUE(page);
3597   EXPECT_EQ(1, FPDFPage_GetAnnotCount(page));
3598 
3599   {
3600     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3601     ASSERT_TRUE(annot);
3602 
3603     // FPDFAnnot_GetFormAdditionalActionJavaScript() positive testing.
3604     unsigned long length_bytes = FPDFAnnot_GetFormAdditionalActionJavaScript(
3605         form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT, nullptr, 0);
3606     ASSERT_EQ(62u, length_bytes);
3607     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
3608     EXPECT_EQ(62u, FPDFAnnot_GetFormAdditionalActionJavaScript(
3609                        form_handle(), annot.get(), FPDF_ANNOT_AACTION_FORMAT,
3610                        buf.data(), length_bytes));
3611     EXPECT_EQ(L"AFDate_FormatEx(\"yyyy-mm-dd\");",
3612               GetPlatformWString(buf.data()));
3613 
3614     // FPDFAnnot_GetFormAdditionalActionJavaScript() negative testing.
3615     EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
3616                       form_handle(), nullptr, 0, nullptr, 0));
3617     EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
3618                       nullptr, annot.get(), 0, nullptr, 0));
3619     EXPECT_EQ(0u, FPDFAnnot_GetFormAdditionalActionJavaScript(
3620                       form_handle(), annot.get(), 0, nullptr, 0));
3621     EXPECT_EQ(2u, FPDFAnnot_GetFormAdditionalActionJavaScript(
3622                       form_handle(), annot.get(), FPDF_ANNOT_AACTION_KEY_STROKE,
3623                       nullptr, 0));
3624   }
3625 
3626   UnloadPage(page);
3627 }
3628 
TEST_F(FPDFAnnotEmbedderTest,FormFieldAlternateName)3629 TEST_F(FPDFAnnotEmbedderTest, FormFieldAlternateName) {
3630   ASSERT_TRUE(OpenDocument("click_form.pdf"));
3631   FPDF_PAGE page = LoadPage(0);
3632   ASSERT_TRUE(page);
3633   EXPECT_EQ(8, FPDFPage_GetAnnotCount(page));
3634 
3635   {
3636     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
3637     ASSERT_TRUE(annot);
3638 
3639     // FPDFAnnot_GetFormFieldAlternateName() positive testing.
3640     unsigned long length_bytes = FPDFAnnot_GetFormFieldAlternateName(
3641         form_handle(), annot.get(), nullptr, 0);
3642     ASSERT_EQ(34u, length_bytes);
3643     std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
3644     EXPECT_EQ(34u, FPDFAnnot_GetFormFieldAlternateName(
3645                        form_handle(), annot.get(), buf.data(), length_bytes));
3646     EXPECT_EQ(L"readOnlyCheckbox", GetPlatformWString(buf.data()));
3647 
3648     // FPDFAnnot_GetFormFieldAlternateName() negative testing.
3649     EXPECT_EQ(0u, FPDFAnnot_GetFormFieldAlternateName(form_handle(), nullptr,
3650                                                       nullptr, 0));
3651     EXPECT_EQ(0u, FPDFAnnot_GetFormFieldAlternateName(nullptr, annot.get(),
3652                                                       nullptr, 0));
3653   }
3654 
3655   UnloadPage(page);
3656 }
3657 
3658 // Due to https://crbug.com/pdfium/570, the AnnotationBorder test above cannot
3659 // actually render the line annotations inside line_annot.pdf. For now, use a
3660 // square annotation in annots.pdf for testing.
TEST_F(FPDFAnnotEmbedderTest,AnnotationBorderRendering)3661 TEST_F(FPDFAnnotEmbedderTest, AnnotationBorderRendering) {
3662   ASSERT_TRUE(OpenDocument("annots.pdf"));
3663   FPDF_PAGE page = LoadPage(1);
3664   ASSERT_TRUE(page);
3665   EXPECT_EQ(3, FPDFPage_GetAnnotCount(page));
3666 
3667   const char* original_checksum = []() {
3668     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
3669       return "238dccc7df0ac61ac580c28e1109da3c";
3670     }
3671 #if BUILDFLAG(IS_APPLE)
3672     return "522a4a6b6c7eab5bf95ded1f21ea372e";
3673 #else
3674     return "12127303aecd80c6288460f7c0d79f3f";
3675 #endif
3676   }();
3677   const char* modified_checksum = []() {
3678     if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
3679       return "0f326acb3eb583125ca584d703ccb13b";
3680     }
3681 #if BUILDFLAG(IS_APPLE)
3682     return "6844019e07b83cc01723415f58218d06";
3683 #else
3684     return "73d06ff4c665fe85029acef30240dcca";
3685 #endif
3686   }();
3687 
3688   {
3689     ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 2));
3690     ASSERT_TRUE(annot);
3691     EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(annot.get()));
3692 
3693     {
3694       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
3695       CompareBitmap(bitmap.get(), 612, 792, original_checksum);
3696     }
3697 
3698     EXPECT_TRUE(FPDFAnnot_SetBorder(annot.get(), /*horizontal_radius=*/2.0f,
3699                                     /*vertical_radius=*/3.5f,
3700                                     /*border_width=*/4.0f));
3701 
3702     {
3703       ScopedFPDFBitmap bitmap = RenderLoadedPageWithFlags(page, FPDF_ANNOT);
3704       CompareBitmap(bitmap.get(), 612, 792, modified_checksum);
3705     }
3706   }
3707 
3708   // Save the document and close the page.
3709   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3710   UnloadPage(page);
3711 
3712   ASSERT_TRUE(OpenSavedDocument());
3713   page = LoadSavedPage(1);
3714   ASSERT_TRUE(page);
3715   VerifySavedRendering(page, 612, 792, modified_checksum);
3716 
3717   CloseSavedPage(page);
3718   CloseSavedDocument();
3719 }
3720