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