1 // Copyright 2023 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 "core/fpdfdoc/cpdf_annotlist.h"
6
7 #include <stdint.h>
8
9 #include <initializer_list>
10 #include <memory>
11
12 #include "constants/annotation_common.h"
13 #include "core/fpdfapi/page/cpdf_page.h"
14 #include "core/fpdfapi/page/test_with_page_module.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_name.h"
18 #include "core/fpdfapi/parser/cpdf_string.h"
19 #include "core/fpdfapi/parser/cpdf_test_document.h"
20 #include "core/fpdfdoc/cpdf_annot.h"
21 #include "core/fxcrt/bytestring.h"
22 #include "core/fxcrt/retain_ptr.h"
23 #include "core/fxcrt/widestring.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace {
27
28 class CPDFAnnotListTest : public TestWithPageModule {
29 public:
SetUp()30 void SetUp() override {
31 TestWithPageModule::SetUp();
32
33 document_ = std::make_unique<CPDF_TestDocument>();
34 document_->SetRoot(pdfium::MakeRetain<CPDF_Dictionary>());
35 page_ = pdfium::MakeRetain<CPDF_Page>(
36 document_.get(), pdfium::MakeRetain<CPDF_Dictionary>());
37 }
38
TearDown()39 void TearDown() override {
40 page_.Reset();
41 document_.reset();
42
43 TestWithPageModule::TearDown();
44 }
45
46 protected:
AddTextAnnotation(const ByteString & contents)47 void AddTextAnnotation(const ByteString& contents) {
48 RetainPtr<CPDF_Dictionary> annotation =
49 page_->GetOrCreateAnnotsArray()->AppendNew<CPDF_Dictionary>();
50 annotation->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Text");
51 annotation->SetNewFor<CPDF_String>(pdfium::annotation::kContents, contents,
52 /*bHex=*/false);
53 }
54
55 std::unique_ptr<CPDF_TestDocument> document_;
56 RetainPtr<CPDF_Page> page_;
57 };
58
MakeByteString(std::initializer_list<uint8_t> bytes)59 ByteString MakeByteString(std::initializer_list<uint8_t> bytes) {
60 return ByteString(std::data(bytes), std::size(bytes));
61 }
62
GetRawContents(const CPDF_Annot * annotation)63 ByteString GetRawContents(const CPDF_Annot* annotation) {
64 return annotation->GetAnnotDict()->GetByteStringFor(
65 pdfium::annotation::kContents);
66 }
67
GetDecodedContents(const CPDF_Annot * annotation)68 WideString GetDecodedContents(const CPDF_Annot* annotation) {
69 return annotation->GetAnnotDict()->GetUnicodeTextFor(
70 pdfium::annotation::kContents);
71 }
72
73 } // namespace
74
TEST_F(CPDFAnnotListTest,CreatePopupAnnotFromPdfEncoded)75 TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromPdfEncoded) {
76 const ByteString kContents = MakeByteString({'A', 'a', 0xE4, 0xA0});
77 AddTextAnnotation(kContents);
78
79 CPDF_AnnotList list(page_);
80
81 ASSERT_EQ(2u, list.Count());
82 EXPECT_EQ(kContents, GetRawContents(list.GetAt(1)));
83 EXPECT_EQ(WideString::FromUTF8("Aaä€"), GetDecodedContents(list.GetAt(1)));
84 }
85
TEST_F(CPDFAnnotListTest,CreatePopupAnnotFromUnicode)86 TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromUnicode) {
87 const ByteString kContents =
88 MakeByteString({0xFE, 0xFF, 0x00, 'A', 0x00, 'a', 0x00, 0xE4, 0x20, 0xAC,
89 0xD8, 0x3C, 0xDF, 0xA8});
90 AddTextAnnotation(kContents);
91
92 CPDF_AnnotList list(page_);
93
94 ASSERT_EQ(2u, list.Count());
95 EXPECT_EQ(kContents, GetRawContents(list.GetAt(1)));
96
97 EXPECT_EQ(WideString::FromUTF8("Aaä€"), GetDecodedContents(list.GetAt(1)));
98 }
99
TEST_F(CPDFAnnotListTest,CreatePopupAnnotFromEmptyPdfEncoded)100 TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromEmptyPdfEncoded) {
101 AddTextAnnotation("");
102
103 CPDF_AnnotList list(page_);
104
105 EXPECT_EQ(1u, list.Count());
106 }
107
TEST_F(CPDFAnnotListTest,CreatePopupAnnotFromEmptyUnicode)108 TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromEmptyUnicode) {
109 const ByteString kContents = MakeByteString({0xFE, 0xFF});
110 AddTextAnnotation(kContents);
111
112 CPDF_AnnotList list(page_);
113
114 EXPECT_EQ(1u, list.Count());
115 }
116
TEST_F(CPDFAnnotListTest,CreatePopupAnnotFromEmptyUnicodedWithEscape)117 TEST_F(CPDFAnnotListTest, CreatePopupAnnotFromEmptyUnicodedWithEscape) {
118 const ByteString kContents =
119 MakeByteString({0xFE, 0xFF, 0x00, 0x1B, 'j', 'a', 0x00, 0x1B});
120 AddTextAnnotation(kContents);
121
122 CPDF_AnnotList list(page_);
123
124 EXPECT_EQ(1u, list.Count());
125 }
126