1*3ac0a46fSAndroid Build Coastguard Worker // Copyright 2016 The PDFium Authors
2*3ac0a46fSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*3ac0a46fSAndroid Build Coastguard Worker // found in the LICENSE file.
4*3ac0a46fSAndroid Build Coastguard Worker
5*3ac0a46fSAndroid Build Coastguard Worker #include <iterator>
6*3ac0a46fSAndroid Build Coastguard Worker
7*3ac0a46fSAndroid Build Coastguard Worker #include "public/fpdf_structtree.h"
8*3ac0a46fSAndroid Build Coastguard Worker #include "testing/embedder_test.h"
9*3ac0a46fSAndroid Build Coastguard Worker #include "testing/fx_string_testhelpers.h"
10*3ac0a46fSAndroid Build Coastguard Worker #include "third_party/abseil-cpp/absl/types/optional.h"
11*3ac0a46fSAndroid Build Coastguard Worker
12*3ac0a46fSAndroid Build Coastguard Worker class FPDFStructTreeEmbedderTest : public EmbedderTest {};
13*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetAltText)14*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetAltText) {
15*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
16*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
17*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
18*3ac0a46fSAndroid Build Coastguard Worker
19*3ac0a46fSAndroid Build Coastguard Worker {
20*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
21*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
22*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
23*3ac0a46fSAndroid Build Coastguard Worker
24*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT element =
25*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), -1);
26*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(element);
27*3ac0a46fSAndroid Build Coastguard Worker element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1);
28*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(element);
29*3ac0a46fSAndroid Build Coastguard Worker element = FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
30*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(element);
31*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(element));
32*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetAltText(element, nullptr, 0));
33*3ac0a46fSAndroid Build Coastguard Worker
34*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
35*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child_element =
36*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(element, -1);
37*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(child_element);
38*3ac0a46fSAndroid Build Coastguard Worker child_element = FPDF_StructElement_GetChildAtIndex(element, 1);
39*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(child_element);
40*3ac0a46fSAndroid Build Coastguard Worker child_element = FPDF_StructElement_GetChildAtIndex(element, 0);
41*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child_element);
42*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(child_element));
43*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetAltText(child_element, nullptr, 0));
44*3ac0a46fSAndroid Build Coastguard Worker
45*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element));
46*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT gchild_element =
47*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(child_element, -1);
48*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(gchild_element);
49*3ac0a46fSAndroid Build Coastguard Worker gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 1);
50*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(gchild_element);
51*3ac0a46fSAndroid Build Coastguard Worker gchild_element = FPDF_StructElement_GetChildAtIndex(child_element, 0);
52*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(gchild_element);
53*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
54*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, nullptr, 0));
55*3ac0a46fSAndroid Build Coastguard Worker
56*3ac0a46fSAndroid Build Coastguard Worker unsigned short buffer[12];
57*3ac0a46fSAndroid Build Coastguard Worker memset(buffer, 0, sizeof(buffer));
58*3ac0a46fSAndroid Build Coastguard Worker // Deliberately pass in a small buffer size to make sure |buffer| remains
59*3ac0a46fSAndroid Build Coastguard Worker // untouched.
60*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer, 1));
61*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
62*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, buffer[i]);
63*3ac0a46fSAndroid Build Coastguard Worker
64*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(gchild_element));
65*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(24U, FPDF_StructElement_GetAltText(gchild_element, buffer,
66*3ac0a46fSAndroid Build Coastguard Worker sizeof(buffer)));
67*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"Black Image", GetPlatformWString(buffer));
68*3ac0a46fSAndroid Build Coastguard Worker
69*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild_element));
70*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT ggchild_element =
71*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(gchild_element, 0);
72*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(ggchild_element);
73*3ac0a46fSAndroid Build Coastguard Worker }
74*3ac0a46fSAndroid Build Coastguard Worker
75*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
76*3ac0a46fSAndroid Build Coastguard Worker }
77*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetActualText)78*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetActualText) {
79*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_actual_text.pdf"));
80*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
81*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
82*3ac0a46fSAndroid Build Coastguard Worker
83*3ac0a46fSAndroid Build Coastguard Worker {
84*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
85*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
86*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
87*3ac0a46fSAndroid Build Coastguard Worker
88*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetActualText(nullptr, nullptr, 0));
89*3ac0a46fSAndroid Build Coastguard Worker
90*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT element =
91*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
92*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(element);
93*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetActualText(element, nullptr, 0));
94*3ac0a46fSAndroid Build Coastguard Worker
95*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
96*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child_element =
97*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(element, 0);
98*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child_element);
99*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetActualText(child_element, nullptr, 0));
100*3ac0a46fSAndroid Build Coastguard Worker
101*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(child_element));
102*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT gchild_element =
103*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(child_element, 0);
104*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(gchild_element);
105*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(24U,
106*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetActualText(gchild_element, nullptr, 0));
107*3ac0a46fSAndroid Build Coastguard Worker
108*3ac0a46fSAndroid Build Coastguard Worker unsigned short buffer[12] = {};
109*3ac0a46fSAndroid Build Coastguard Worker // Deliberately pass in a small buffer size to make sure |buffer| remains
110*3ac0a46fSAndroid Build Coastguard Worker // untouched.
111*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(24U, FPDF_StructElement_GetActualText(gchild_element, buffer, 1));
112*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
113*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, buffer[i]);
114*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(24U, FPDF_StructElement_GetActualText(gchild_element, buffer,
115*3ac0a46fSAndroid Build Coastguard Worker sizeof(buffer)));
116*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"Actual Text", GetPlatformWString(buffer));
117*3ac0a46fSAndroid Build Coastguard Worker }
118*3ac0a46fSAndroid Build Coastguard Worker
119*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
120*3ac0a46fSAndroid Build Coastguard Worker }
121*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetStringAttribute)122*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetStringAttribute) {
123*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
124*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
125*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
126*3ac0a46fSAndroid Build Coastguard Worker
127*3ac0a46fSAndroid Build Coastguard Worker {
128*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
129*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
130*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
131*3ac0a46fSAndroid Build Coastguard Worker
132*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT document =
133*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
134*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(document);
135*3ac0a46fSAndroid Build Coastguard Worker
136*3ac0a46fSAndroid Build Coastguard Worker constexpr int kBufLen = 100;
137*3ac0a46fSAndroid Build Coastguard Worker uint16_t buffer[kBufLen] = {0};
138*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
139*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Document", GetPlatformString(buffer));
140*3ac0a46fSAndroid Build Coastguard Worker
141*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
142*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
143*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(table);
144*3ac0a46fSAndroid Build Coastguard Worker
145*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
146*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Table", GetPlatformString(buffer));
147*3ac0a46fSAndroid Build Coastguard Worker
148*3ac0a46fSAndroid Build Coastguard Worker // The table should have an attribute "Summary" set to the empty string.
149*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2U, FPDF_StructElement_GetStringAttribute(table, "Summary",
150*3ac0a46fSAndroid Build Coastguard Worker buffer, kBufLen));
151*3ac0a46fSAndroid Build Coastguard Worker
152*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
153*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
154*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(row);
155*3ac0a46fSAndroid Build Coastguard Worker
156*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(row));
157*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT header_cell = FPDF_StructElement_GetChildAtIndex(row, 0);
158*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(header_cell);
159*3ac0a46fSAndroid Build Coastguard Worker
160*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(6U, FPDF_StructElement_GetType(header_cell, buffer, kBufLen));
161*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("TH", GetPlatformString(buffer));
162*3ac0a46fSAndroid Build Coastguard Worker
163*3ac0a46fSAndroid Build Coastguard Worker // The header should have an attribute "Scope" with a scope of "Row".
164*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(8U, FPDF_StructElement_GetStringAttribute(header_cell, "Scope",
165*3ac0a46fSAndroid Build Coastguard Worker buffer, kBufLen));
166*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Row", GetPlatformString(buffer));
167*3ac0a46fSAndroid Build Coastguard Worker
168*3ac0a46fSAndroid Build Coastguard Worker // The header has an attribute "ColSpan", but it's not a string so it
169*3ac0a46fSAndroid Build Coastguard Worker // returns null.
170*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(header_cell, "ColSpan",
171*3ac0a46fSAndroid Build Coastguard Worker buffer, kBufLen));
172*3ac0a46fSAndroid Build Coastguard Worker
173*3ac0a46fSAndroid Build Coastguard Worker // An unsupported attribute should return 0.
174*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(header_cell, "Other",
175*3ac0a46fSAndroid Build Coastguard Worker buffer, kBufLen));
176*3ac0a46fSAndroid Build Coastguard Worker
177*3ac0a46fSAndroid Build Coastguard Worker // A null struct element should not crash.
178*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(nullptr, "Other",
179*3ac0a46fSAndroid Build Coastguard Worker buffer, kBufLen));
180*3ac0a46fSAndroid Build Coastguard Worker }
181*3ac0a46fSAndroid Build Coastguard Worker
182*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
183*3ac0a46fSAndroid Build Coastguard Worker }
184*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetStringAttributeBadStructElement)185*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetStringAttributeBadStructElement) {
186*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table_bad_elem.pdf"));
187*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
188*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
189*3ac0a46fSAndroid Build Coastguard Worker
190*3ac0a46fSAndroid Build Coastguard Worker {
191*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
192*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
193*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
194*3ac0a46fSAndroid Build Coastguard Worker
195*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT document =
196*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
197*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(document);
198*3ac0a46fSAndroid Build Coastguard Worker
199*3ac0a46fSAndroid Build Coastguard Worker constexpr int kBufLen = 100;
200*3ac0a46fSAndroid Build Coastguard Worker uint16_t buffer[kBufLen] = {0};
201*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
202*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Document", GetPlatformString(buffer));
203*3ac0a46fSAndroid Build Coastguard Worker
204*3ac0a46fSAndroid Build Coastguard Worker // The table can be retrieved, even though it does not have /Type.
205*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
206*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
207*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(table);
208*3ac0a46fSAndroid Build Coastguard Worker
209*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
210*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Table", GetPlatformString(buffer));
211*3ac0a46fSAndroid Build Coastguard Worker
212*3ac0a46fSAndroid Build Coastguard Worker // The table entry cannot be retrieved, as the element is malformed.
213*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetStringAttribute(table, "Summary",
214*3ac0a46fSAndroid Build Coastguard Worker buffer, kBufLen));
215*3ac0a46fSAndroid Build Coastguard Worker
216*3ac0a46fSAndroid Build Coastguard Worker // The row can be retrieved, even though it had an invalid /Type.
217*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(table));
218*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
219*3ac0a46fSAndroid Build Coastguard Worker EXPECT_TRUE(row);
220*3ac0a46fSAndroid Build Coastguard Worker }
221*3ac0a46fSAndroid Build Coastguard Worker
222*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
223*3ac0a46fSAndroid Build Coastguard Worker }
224*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetID)225*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetID) {
226*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
227*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
228*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
229*3ac0a46fSAndroid Build Coastguard Worker
230*3ac0a46fSAndroid Build Coastguard Worker {
231*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
232*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
233*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
234*3ac0a46fSAndroid Build Coastguard Worker
235*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT document =
236*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
237*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(document);
238*3ac0a46fSAndroid Build Coastguard Worker
239*3ac0a46fSAndroid Build Coastguard Worker constexpr int kBufLen = 100;
240*3ac0a46fSAndroid Build Coastguard Worker uint16_t buffer[kBufLen] = {0};
241*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
242*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Document", GetPlatformString(buffer));
243*3ac0a46fSAndroid Build Coastguard Worker
244*3ac0a46fSAndroid Build Coastguard Worker // The document has no ID.
245*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetID(document, buffer, kBufLen));
246*3ac0a46fSAndroid Build Coastguard Worker
247*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
248*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
249*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(table);
250*3ac0a46fSAndroid Build Coastguard Worker
251*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
252*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Table", GetPlatformString(buffer));
253*3ac0a46fSAndroid Build Coastguard Worker
254*3ac0a46fSAndroid Build Coastguard Worker // The table has an ID.
255*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(14U, FPDF_StructElement_GetID(table, buffer, kBufLen));
256*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("node12", GetPlatformString(buffer));
257*3ac0a46fSAndroid Build Coastguard Worker
258*3ac0a46fSAndroid Build Coastguard Worker // The first child of the table is a row, which has an empty ID.
259*3ac0a46fSAndroid Build Coastguard Worker // It returns 2U, the length of an empty string, instead of 0U,
260*3ac0a46fSAndroid Build Coastguard Worker // representing null.
261*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
262*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
263*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(row);
264*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2U, FPDF_StructElement_GetID(row, buffer, kBufLen));
265*3ac0a46fSAndroid Build Coastguard Worker }
266*3ac0a46fSAndroid Build Coastguard Worker
267*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
268*3ac0a46fSAndroid Build Coastguard Worker }
269*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetLang)270*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetLang) {
271*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
272*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
273*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
274*3ac0a46fSAndroid Build Coastguard Worker
275*3ac0a46fSAndroid Build Coastguard Worker {
276*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
277*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
278*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
279*3ac0a46fSAndroid Build Coastguard Worker
280*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT document =
281*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
282*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(document);
283*3ac0a46fSAndroid Build Coastguard Worker
284*3ac0a46fSAndroid Build Coastguard Worker constexpr int kBufLen = 100;
285*3ac0a46fSAndroid Build Coastguard Worker uint16_t buffer[kBufLen] = {0};
286*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(18U, FPDF_StructElement_GetType(document, buffer, kBufLen));
287*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Document", GetPlatformString(buffer));
288*3ac0a46fSAndroid Build Coastguard Worker
289*3ac0a46fSAndroid Build Coastguard Worker // Nullptr test
290*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetLang(nullptr, buffer, kBufLen));
291*3ac0a46fSAndroid Build Coastguard Worker
292*3ac0a46fSAndroid Build Coastguard Worker // The document has a language.
293*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(12U, FPDF_StructElement_GetLang(document, buffer, kBufLen));
294*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("en-US", GetPlatformString(buffer));
295*3ac0a46fSAndroid Build Coastguard Worker
296*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
297*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
298*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(table);
299*3ac0a46fSAndroid Build Coastguard Worker
300*3ac0a46fSAndroid Build Coastguard Worker // The first child is a table, with a language.
301*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(12U, FPDF_StructElement_GetType(table, buffer, kBufLen));
302*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("Table", GetPlatformString(buffer));
303*3ac0a46fSAndroid Build Coastguard Worker
304*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(6U, FPDF_StructElement_GetLang(table, buffer, kBufLen));
305*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ("hu", GetPlatformString(buffer));
306*3ac0a46fSAndroid Build Coastguard Worker
307*3ac0a46fSAndroid Build Coastguard Worker // The first child of the table is a row, which doesn't have a
308*3ac0a46fSAndroid Build Coastguard Worker // language explicitly set on it.
309*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
310*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT row = FPDF_StructElement_GetChildAtIndex(table, 0);
311*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(row);
312*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, FPDF_StructElement_GetLang(row, buffer, kBufLen));
313*3ac0a46fSAndroid Build Coastguard Worker }
314*3ac0a46fSAndroid Build Coastguard Worker
315*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
316*3ac0a46fSAndroid Build Coastguard Worker }
317*3ac0a46fSAndroid Build Coastguard Worker
318*3ac0a46fSAndroid Build Coastguard Worker // See also FPDFEditEmbedderTest.TraverseMarkedContentID, which traverses the
319*3ac0a46fSAndroid Build Coastguard Worker // marked contents using FPDFPageObj_GetMark() and related API.
TEST_F(FPDFStructTreeEmbedderTest,GetMarkedContentID)320*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentID) {
321*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("marked_content_id.pdf"));
322*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
323*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
324*3ac0a46fSAndroid Build Coastguard Worker
325*3ac0a46fSAndroid Build Coastguard Worker {
326*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
327*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
328*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
329*3ac0a46fSAndroid Build Coastguard Worker
330*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT element =
331*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
332*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentID(element));
333*3ac0a46fSAndroid Build Coastguard Worker }
334*3ac0a46fSAndroid Build Coastguard Worker
335*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
336*3ac0a46fSAndroid Build Coastguard Worker }
337*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetMarkedContentIdAtIndex)338*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetMarkedContentIdAtIndex) {
339*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_marked_content.pdf"));
340*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
341*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
342*3ac0a46fSAndroid Build Coastguard Worker
343*3ac0a46fSAndroid Build Coastguard Worker {
344*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
345*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
346*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(4, FPDF_StructTree_CountChildren(struct_tree.get()));
347*3ac0a46fSAndroid Build Coastguard Worker
348*3ac0a46fSAndroid Build Coastguard Worker // K is an integer MCID
349*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child1 =
350*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
351*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child1);
352*3ac0a46fSAndroid Build Coastguard Worker // Legacy API
353*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentID(child1));
354*3ac0a46fSAndroid Build Coastguard Worker
355*3ac0a46fSAndroid Build Coastguard Worker // K is a dict containing MCR object reference
356*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child2 =
357*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 1);
358*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child2);
359*3ac0a46fSAndroid Build Coastguard Worker
360*3ac0a46fSAndroid Build Coastguard Worker // K is an array containing dict MCR object reference and integer MCID
361*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child3 =
362*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 2);
363*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child3);
364*3ac0a46fSAndroid Build Coastguard Worker
365*3ac0a46fSAndroid Build Coastguard Worker // K does not exist
366*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child4 =
367*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 3);
368*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child4);
369*3ac0a46fSAndroid Build Coastguard Worker
370*3ac0a46fSAndroid Build Coastguard Worker // New APIs
371*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdCount(nullptr));
372*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(nullptr, 0));
373*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, -1));
374*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, 1));
375*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdCount(child1));
376*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0, FPDF_StructElement_GetMarkedContentIdAtIndex(child1, 0));
377*3ac0a46fSAndroid Build Coastguard Worker
378*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdCount(child2));
379*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(1, FPDF_StructElement_GetMarkedContentIdAtIndex(child2, 0));
380*3ac0a46fSAndroid Build Coastguard Worker
381*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2, FPDF_StructElement_GetMarkedContentIdCount(child3));
382*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2, FPDF_StructElement_GetMarkedContentIdAtIndex(child3, 0));
383*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(3, FPDF_StructElement_GetMarkedContentIdAtIndex(child3, 1));
384*3ac0a46fSAndroid Build Coastguard Worker
385*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdCount(child4));
386*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentIdAtIndex(child4, 0));
387*3ac0a46fSAndroid Build Coastguard Worker }
388*3ac0a46fSAndroid Build Coastguard Worker
389*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
390*3ac0a46fSAndroid Build Coastguard Worker }
391*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetType)392*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetType) {
393*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
394*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
395*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
396*3ac0a46fSAndroid Build Coastguard Worker
397*3ac0a46fSAndroid Build Coastguard Worker {
398*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
399*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
400*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
401*3ac0a46fSAndroid Build Coastguard Worker
402*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT element =
403*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
404*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(element);
405*3ac0a46fSAndroid Build Coastguard Worker
406*3ac0a46fSAndroid Build Coastguard Worker // test nullptr inputs
407*3ac0a46fSAndroid Build Coastguard Worker unsigned short buffer[12];
408*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U, FPDF_StructElement_GetType(nullptr, buffer, sizeof(buffer)));
409*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U, FPDF_StructElement_GetType(nullptr, nullptr, 0));
410*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(18U, FPDF_StructElement_GetType(element, nullptr, 0));
411*3ac0a46fSAndroid Build Coastguard Worker
412*3ac0a46fSAndroid Build Coastguard Worker memset(buffer, 0, sizeof(buffer));
413*3ac0a46fSAndroid Build Coastguard Worker // Deliberately pass in a small buffer size to make sure |buffer| remains
414*3ac0a46fSAndroid Build Coastguard Worker // untouched.
415*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, 1));
416*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
417*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, buffer[i]);
418*3ac0a46fSAndroid Build Coastguard Worker
419*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, sizeof(buffer)));
420*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"Document", GetPlatformWString(buffer));
421*3ac0a46fSAndroid Build Coastguard Worker }
422*3ac0a46fSAndroid Build Coastguard Worker
423*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
424*3ac0a46fSAndroid Build Coastguard Worker }
425*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetObjType)426*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetObjType) {
427*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table_bad_elem.pdf"));
428*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
429*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
430*3ac0a46fSAndroid Build Coastguard Worker
431*3ac0a46fSAndroid Build Coastguard Worker {
432*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
433*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
434*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
435*3ac0a46fSAndroid Build Coastguard Worker
436*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child =
437*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
438*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child);
439*3ac0a46fSAndroid Build Coastguard Worker
440*3ac0a46fSAndroid Build Coastguard Worker // test nullptr inputs
441*3ac0a46fSAndroid Build Coastguard Worker unsigned short buffer[28] = {};
442*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U,
443*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetObjType(nullptr, buffer, sizeof(buffer)));
444*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U, FPDF_StructElement_GetObjType(nullptr, nullptr, 0));
445*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(22U, FPDF_StructElement_GetObjType(child, nullptr, 0));
446*3ac0a46fSAndroid Build Coastguard Worker
447*3ac0a46fSAndroid Build Coastguard Worker // Deliberately pass in a small buffer size to make sure `buffer` remains
448*3ac0a46fSAndroid Build Coastguard Worker // untouched.
449*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(22U, FPDF_StructElement_GetObjType(child, buffer, 1));
450*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
451*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, buffer[i]);
452*3ac0a46fSAndroid Build Coastguard Worker
453*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(22U,
454*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetObjType(child, buffer, sizeof(buffer)));
455*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"StructElem", GetPlatformWString(buffer));
456*3ac0a46fSAndroid Build Coastguard Worker
457*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(child));
458*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT gchild = FPDF_StructElement_GetChildAtIndex(child, 0);
459*3ac0a46fSAndroid Build Coastguard Worker memset(buffer, 0, sizeof(buffer));
460*3ac0a46fSAndroid Build Coastguard Worker // Missing /Type in `gchild`
461*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U,
462*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetObjType(gchild, buffer, sizeof(buffer)));
463*3ac0a46fSAndroid Build Coastguard Worker // Buffer is untouched.
464*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
465*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, buffer[i]);
466*3ac0a46fSAndroid Build Coastguard Worker
467*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild));
468*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT ggchild = FPDF_StructElement_GetChildAtIndex(gchild, 0);
469*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(28U,
470*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetObjType(ggchild, buffer, sizeof(buffer)));
471*3ac0a46fSAndroid Build Coastguard Worker // Reading bad elem also works.
472*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"NotStructElem", GetPlatformWString(buffer));
473*3ac0a46fSAndroid Build Coastguard Worker }
474*3ac0a46fSAndroid Build Coastguard Worker
475*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
476*3ac0a46fSAndroid Build Coastguard Worker }
477*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetParent)478*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetParent) {
479*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
480*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
481*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
482*3ac0a46fSAndroid Build Coastguard Worker
483*3ac0a46fSAndroid Build Coastguard Worker {
484*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
485*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
486*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
487*3ac0a46fSAndroid Build Coastguard Worker
488*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT parent =
489*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
490*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(parent);
491*3ac0a46fSAndroid Build Coastguard Worker
492*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(parent));
493*3ac0a46fSAndroid Build Coastguard Worker
494*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child = FPDF_StructElement_GetChildAtIndex(parent, 0);
495*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(child);
496*3ac0a46fSAndroid Build Coastguard Worker
497*3ac0a46fSAndroid Build Coastguard Worker // test nullptr inputs
498*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(nullptr, FPDF_StructElement_GetParent(nullptr));
499*3ac0a46fSAndroid Build Coastguard Worker
500*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(parent, FPDF_StructElement_GetParent(child));
501*3ac0a46fSAndroid Build Coastguard Worker
502*3ac0a46fSAndroid Build Coastguard Worker // The parent of `parent` is StructTreeRoot and no longer a StructElement.
503*3ac0a46fSAndroid Build Coastguard Worker // We currently handle this case by returning a nullptr.
504*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(nullptr, FPDF_StructElement_GetParent(parent));
505*3ac0a46fSAndroid Build Coastguard Worker }
506*3ac0a46fSAndroid Build Coastguard Worker
507*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
508*3ac0a46fSAndroid Build Coastguard Worker }
509*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetTitle)510*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetTitle) {
511*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_alt_text.pdf"));
512*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
513*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
514*3ac0a46fSAndroid Build Coastguard Worker
515*3ac0a46fSAndroid Build Coastguard Worker {
516*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
517*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
518*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
519*3ac0a46fSAndroid Build Coastguard Worker
520*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT element =
521*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
522*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(element);
523*3ac0a46fSAndroid Build Coastguard Worker
524*3ac0a46fSAndroid Build Coastguard Worker // test nullptr inputs
525*3ac0a46fSAndroid Build Coastguard Worker unsigned short buffer[13];
526*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U, FPDF_StructElement_GetTitle(nullptr, buffer, sizeof(buffer)));
527*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(0U, FPDF_StructElement_GetTitle(nullptr, nullptr, 0));
528*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, nullptr, 0));
529*3ac0a46fSAndroid Build Coastguard Worker
530*3ac0a46fSAndroid Build Coastguard Worker memset(buffer, 0, sizeof(buffer));
531*3ac0a46fSAndroid Build Coastguard Worker // Deliberately pass in a small buffer size to make sure |buffer| remains
532*3ac0a46fSAndroid Build Coastguard Worker // untouched.
533*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, buffer, 1));
534*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
535*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0U, buffer[i]);
536*3ac0a46fSAndroid Build Coastguard Worker
537*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U,
538*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetTitle(element, buffer, sizeof(buffer)));
539*3ac0a46fSAndroid Build Coastguard Worker
540*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"TitleText", GetPlatformWString(buffer));
541*3ac0a46fSAndroid Build Coastguard Worker
542*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
543*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT child_element =
544*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(element, 0);
545*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(element);
546*3ac0a46fSAndroid Build Coastguard Worker
547*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(26U, FPDF_StructElement_GetTitle(child_element, buffer,
548*3ac0a46fSAndroid Build Coastguard Worker sizeof(buffer)));
549*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"symbol: 100k", GetPlatformWString(buffer));
550*3ac0a46fSAndroid Build Coastguard Worker }
551*3ac0a46fSAndroid Build Coastguard Worker
552*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
553*3ac0a46fSAndroid Build Coastguard Worker }
554*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetAttributes)555*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetAttributes) {
556*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
557*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
558*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
559*3ac0a46fSAndroid Build Coastguard Worker
560*3ac0a46fSAndroid Build Coastguard Worker {
561*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
562*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
563*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
564*3ac0a46fSAndroid Build Coastguard Worker
565*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT document =
566*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
567*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(document);
568*3ac0a46fSAndroid Build Coastguard Worker
569*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
570*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(-1, FPDF_StructElement_GetAttributeCount(document));
571*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
572*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(table);
573*3ac0a46fSAndroid Build Coastguard Worker
574*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
575*3ac0a46fSAndroid Build Coastguard Worker
576*3ac0a46fSAndroid Build Coastguard Worker {
577*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 0);
578*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(tr);
579*3ac0a46fSAndroid Build Coastguard Worker
580*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr));
581*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT th = FPDF_StructElement_GetChildAtIndex(tr, 0);
582*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(th);
583*3ac0a46fSAndroid Build Coastguard Worker
584*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_GetAttributeCount(th));
585*3ac0a46fSAndroid Build Coastguard Worker
586*3ac0a46fSAndroid Build Coastguard Worker // nullptr test
587*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, 0));
588*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, -1));
589*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(th, 2));
590*3ac0a46fSAndroid Build Coastguard Worker
591*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT_ATTR attr =
592*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetAttributeAtIndex(th, 1);
593*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(attr);
594*3ac0a46fSAndroid Build Coastguard Worker
595*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_Attr_GetCount(attr));
596*3ac0a46fSAndroid Build Coastguard Worker ASSERT_FALSE(
597*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0U, nullptr));
598*3ac0a46fSAndroid Build Coastguard Worker unsigned long buffer_len_needed = ULONG_MAX;
599*3ac0a46fSAndroid Build Coastguard Worker // Pass buffer = nullptr to obtain the size of the buffer needed,
600*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0,
601*3ac0a46fSAndroid Build Coastguard Worker &buffer_len_needed));
602*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2U, buffer_len_needed);
603*3ac0a46fSAndroid Build Coastguard Worker char buffer[8] = {};
604*3ac0a46fSAndroid Build Coastguard Worker unsigned long out_len = ULONG_MAX;
605*3ac0a46fSAndroid Build Coastguard Worker // Deliberately pass in a small buffer size to make sure `buffer` remains
606*3ac0a46fSAndroid Build Coastguard Worker // untouched.
607*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(
608*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetName(attr, 1, buffer, 1, &out_len));
609*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2U, out_len);
610*3ac0a46fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(buffer); ++i)
611*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(0, buffer[i]);
612*3ac0a46fSAndroid Build Coastguard Worker
613*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 1, buffer,
614*3ac0a46fSAndroid Build Coastguard Worker sizeof(buffer), &out_len));
615*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(2U, out_len);
616*3ac0a46fSAndroid Build Coastguard Worker EXPECT_STREQ("O", buffer);
617*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(FPDF_OBJECT_NAME,
618*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetType(attr, buffer));
619*3ac0a46fSAndroid Build Coastguard Worker
620*3ac0a46fSAndroid Build Coastguard Worker unsigned short str_val[12] = {};
621*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue(
622*3ac0a46fSAndroid Build Coastguard Worker attr, buffer, str_val, sizeof(str_val), &out_len));
623*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(12U, out_len);
624*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"Table", GetPlatformWString(str_val));
625*3ac0a46fSAndroid Build Coastguard Worker
626*3ac0a46fSAndroid Build Coastguard Worker memset(buffer, 0, sizeof(buffer));
627*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 0, buffer,
628*3ac0a46fSAndroid Build Coastguard Worker sizeof(buffer), &out_len));
629*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(8U, out_len);
630*3ac0a46fSAndroid Build Coastguard Worker EXPECT_STREQ("ColSpan", buffer);
631*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(FPDF_OBJECT_NUMBER,
632*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetType(attr, buffer));
633*3ac0a46fSAndroid Build Coastguard Worker float num_val;
634*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(
635*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &num_val));
636*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FLOAT_EQ(2.0f, num_val);
637*3ac0a46fSAndroid Build Coastguard Worker }
638*3ac0a46fSAndroid Build Coastguard Worker
639*3ac0a46fSAndroid Build Coastguard Worker {
640*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 1);
641*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(tr);
642*3ac0a46fSAndroid Build Coastguard Worker
643*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(tr));
644*3ac0a46fSAndroid Build Coastguard Worker // nullptr when index out of range
645*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(tr, 1));
646*3ac0a46fSAndroid Build Coastguard Worker
647*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr));
648*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT td = FPDF_StructElement_GetChildAtIndex(tr, 1);
649*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(td);
650*3ac0a46fSAndroid Build Coastguard Worker {
651*3ac0a46fSAndroid Build Coastguard Worker // Test counting and obtaining attributes via reference
652*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(td));
653*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT_ATTR attr =
654*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetAttributeAtIndex(td, 0);
655*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(attr);
656*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(4, FPDF_StructElement_Attr_GetCount(attr));
657*3ac0a46fSAndroid Build Coastguard Worker // Test string and blob type
658*3ac0a46fSAndroid Build Coastguard Worker {
659*3ac0a46fSAndroid Build Coastguard Worker char buffer[16] = {};
660*3ac0a46fSAndroid Build Coastguard Worker unsigned long out_len = ULONG_MAX;
661*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
662*3ac0a46fSAndroid Build Coastguard Worker attr, 0, buffer, sizeof(buffer), &out_len));
663*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(8U, out_len);
664*3ac0a46fSAndroid Build Coastguard Worker EXPECT_STREQ("ColProp", buffer);
665*3ac0a46fSAndroid Build Coastguard Worker
666*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(FPDF_OBJECT_STRING,
667*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetType(attr, buffer));
668*3ac0a46fSAndroid Build Coastguard Worker
669*3ac0a46fSAndroid Build Coastguard Worker unsigned short str_val[12] = {};
670*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue(
671*3ac0a46fSAndroid Build Coastguard Worker attr, buffer, str_val, sizeof(str_val), &out_len));
672*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(8U, out_len);
673*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"Sum", GetPlatformWString(str_val));
674*3ac0a46fSAndroid Build Coastguard Worker
675*3ac0a46fSAndroid Build Coastguard Worker char blob_val[3] = {};
676*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetBlobValue(
677*3ac0a46fSAndroid Build Coastguard Worker attr, buffer, blob_val, sizeof(blob_val), &out_len));
678*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(3U, out_len);
679*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ('S', blob_val[0]);
680*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ('u', blob_val[1]);
681*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ('m', blob_val[2]);
682*3ac0a46fSAndroid Build Coastguard Worker }
683*3ac0a46fSAndroid Build Coastguard Worker
684*3ac0a46fSAndroid Build Coastguard Worker // Test boolean type
685*3ac0a46fSAndroid Build Coastguard Worker {
686*3ac0a46fSAndroid Build Coastguard Worker char buffer[16] = {};
687*3ac0a46fSAndroid Build Coastguard Worker unsigned long out_len = ULONG_MAX;
688*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
689*3ac0a46fSAndroid Build Coastguard Worker attr, 1, buffer, sizeof(buffer), &out_len));
690*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(7U, out_len);
691*3ac0a46fSAndroid Build Coastguard Worker EXPECT_STREQ("CurUSD", buffer);
692*3ac0a46fSAndroid Build Coastguard Worker
693*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(FPDF_OBJECT_BOOLEAN,
694*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetType(attr, buffer));
695*3ac0a46fSAndroid Build Coastguard Worker FPDF_BOOL val;
696*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(
697*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetBooleanValue(attr, buffer, &val));
698*3ac0a46fSAndroid Build Coastguard Worker EXPECT_TRUE(val);
699*3ac0a46fSAndroid Build Coastguard Worker }
700*3ac0a46fSAndroid Build Coastguard Worker
701*3ac0a46fSAndroid Build Coastguard Worker // Test reference to number
702*3ac0a46fSAndroid Build Coastguard Worker {
703*3ac0a46fSAndroid Build Coastguard Worker char buffer[16] = {};
704*3ac0a46fSAndroid Build Coastguard Worker unsigned long out_len = ULONG_MAX;
705*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
706*3ac0a46fSAndroid Build Coastguard Worker attr, 3, buffer, sizeof(buffer), &out_len));
707*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(8U, out_len);
708*3ac0a46fSAndroid Build Coastguard Worker EXPECT_STREQ("RowSpan", buffer);
709*3ac0a46fSAndroid Build Coastguard Worker
710*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(FPDF_OBJECT_REFERENCE,
711*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetType(attr, buffer));
712*3ac0a46fSAndroid Build Coastguard Worker float val;
713*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(
714*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &val));
715*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FLOAT_EQ(3, val);
716*3ac0a46fSAndroid Build Coastguard Worker }
717*3ac0a46fSAndroid Build Coastguard Worker }
718*3ac0a46fSAndroid Build Coastguard Worker }
719*3ac0a46fSAndroid Build Coastguard Worker }
720*3ac0a46fSAndroid Build Coastguard Worker
721*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
722*3ac0a46fSAndroid Build Coastguard Worker }
723*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,GetStructTreeForNestedTaggedPDF)724*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, GetStructTreeForNestedTaggedPDF) {
725*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_nested.pdf"));
726*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
727*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
728*3ac0a46fSAndroid Build Coastguard Worker
729*3ac0a46fSAndroid Build Coastguard Worker {
730*3ac0a46fSAndroid Build Coastguard Worker // This call should not crash. https://crbug.com/pdfium/1480
731*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
732*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
733*3ac0a46fSAndroid Build Coastguard Worker }
734*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
735*3ac0a46fSAndroid Build Coastguard Worker }
736*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,MarkedContentReferenceAndObjectReference)737*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, MarkedContentReferenceAndObjectReference) {
738*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_mcr_objr.pdf"));
739*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
740*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
741*3ac0a46fSAndroid Build Coastguard Worker
742*3ac0a46fSAndroid Build Coastguard Worker {
743*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
744*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
745*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
746*3ac0a46fSAndroid Build Coastguard Worker
747*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object8 =
748*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
749*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object8);
750*3ac0a46fSAndroid Build Coastguard Worker unsigned short buffer[12];
751*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(18U, FPDF_StructElement_GetType(object8, buffer, sizeof(buffer)));
752*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"Document", GetPlatformWString(buffer));
753*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object8));
754*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(object8));
755*3ac0a46fSAndroid Build Coastguard Worker
756*3ac0a46fSAndroid Build Coastguard Worker // First branch. 10 -> 12 -> 13 -> Inline dict.
757*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object10 =
758*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(object8, 0);
759*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object10);
760*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U,
761*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetType(object10, buffer, sizeof(buffer)));
762*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
763*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object10));
764*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(object10));
765*3ac0a46fSAndroid Build Coastguard Worker
766*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object12 =
767*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(object10, 0);
768*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object12);
769*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(4U, FPDF_StructElement_GetType(object12, buffer, sizeof(buffer)));
770*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"P", GetPlatformWString(buffer));
771*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object12));
772*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(object12));
773*3ac0a46fSAndroid Build Coastguard Worker
774*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object13 =
775*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(object12, 0);
776*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object13);
777*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U,
778*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetType(object13, buffer, sizeof(buffer)));
779*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
780*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object13));
781*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(object13));
782*3ac0a46fSAndroid Build Coastguard Worker
783*3ac0a46fSAndroid Build Coastguard Worker // TODO(crbug.com/pdfium/672): Fetch this child element.
784*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object13, 0));
785*3ac0a46fSAndroid Build Coastguard Worker
786*3ac0a46fSAndroid Build Coastguard Worker // Second branch. 11 -> 14 -> Inline dict.
787*3ac0a46fSAndroid Build Coastguard Worker // -> 15 -> Inline dict.
788*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object11 =
789*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(object8, 1);
790*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object11);
791*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(4U, FPDF_StructElement_GetType(object11, buffer, sizeof(buffer)));
792*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"P", GetPlatformWString(buffer));
793*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object11));
794*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(object11));
795*3ac0a46fSAndroid Build Coastguard Worker
796*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object14 =
797*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(object11, 0);
798*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object14);
799*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U,
800*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetType(object14, buffer, sizeof(buffer)));
801*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
802*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object14));
803*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(2, FPDF_StructElement_CountChildren(object14));
804*3ac0a46fSAndroid Build Coastguard Worker
805*3ac0a46fSAndroid Build Coastguard Worker // TODO(crbug.com/pdfium/672): Object 15 should be at index 1.
806*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object14, 1));
807*3ac0a46fSAndroid Build Coastguard Worker FPDF_STRUCTELEMENT object15 =
808*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetChildAtIndex(object14, 0);
809*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(object15);
810*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(20U,
811*3ac0a46fSAndroid Build Coastguard Worker FPDF_StructElement_GetType(object15, buffer, sizeof(buffer)));
812*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(L"NonStruct", GetPlatformWString(buffer));
813*3ac0a46fSAndroid Build Coastguard Worker EXPECT_EQ(-1, FPDF_StructElement_GetMarkedContentID(object15));
814*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructElement_CountChildren(object15));
815*3ac0a46fSAndroid Build Coastguard Worker
816*3ac0a46fSAndroid Build Coastguard Worker // TODO(crbug.com/pdfium/672): Fetch this child element.
817*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(FPDF_StructElement_GetChildAtIndex(object15, 0));
818*3ac0a46fSAndroid Build Coastguard Worker }
819*3ac0a46fSAndroid Build Coastguard Worker
820*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
821*3ac0a46fSAndroid Build Coastguard Worker }
822*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,Bug1768)823*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, Bug1768) {
824*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("bug_1768.pdf"));
825*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
826*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
827*3ac0a46fSAndroid Build Coastguard Worker
828*3ac0a46fSAndroid Build Coastguard Worker {
829*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
830*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
831*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
832*3ac0a46fSAndroid Build Coastguard Worker
833*3ac0a46fSAndroid Build Coastguard Worker // TODO(crbug.com/pdfium/1768): Fetch this child element. Then consider
834*3ac0a46fSAndroid Build Coastguard Worker // writing more of the test to make sure other elements in the tree can be
835*3ac0a46fSAndroid Build Coastguard Worker // fetched correctly as well.
836*3ac0a46fSAndroid Build Coastguard Worker EXPECT_FALSE(FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0));
837*3ac0a46fSAndroid Build Coastguard Worker }
838*3ac0a46fSAndroid Build Coastguard Worker
839*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
840*3ac0a46fSAndroid Build Coastguard Worker }
841*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,Bug1296920)842*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, Bug1296920) {
843*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("bug_1296920.pdf"));
844*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
845*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
846*3ac0a46fSAndroid Build Coastguard Worker
847*3ac0a46fSAndroid Build Coastguard Worker {
848*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
849*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
850*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
851*3ac0a46fSAndroid Build Coastguard Worker
852*3ac0a46fSAndroid Build Coastguard Worker // Destroying this tree should not crash.
853*3ac0a46fSAndroid Build Coastguard Worker }
854*3ac0a46fSAndroid Build Coastguard Worker
855*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
856*3ac0a46fSAndroid Build Coastguard Worker }
857*3ac0a46fSAndroid Build Coastguard Worker
TEST_F(FPDFStructTreeEmbedderTest,Bug1443100)858*3ac0a46fSAndroid Build Coastguard Worker TEST_F(FPDFStructTreeEmbedderTest, Bug1443100) {
859*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(OpenDocument("tagged_table_bad_parent.pdf"));
860*3ac0a46fSAndroid Build Coastguard Worker FPDF_PAGE page = LoadPage(0);
861*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(page);
862*3ac0a46fSAndroid Build Coastguard Worker
863*3ac0a46fSAndroid Build Coastguard Worker {
864*3ac0a46fSAndroid Build Coastguard Worker // Calling these APIs should not trigger a dangling pointer.
865*3ac0a46fSAndroid Build Coastguard Worker ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
866*3ac0a46fSAndroid Build Coastguard Worker ASSERT_TRUE(struct_tree);
867*3ac0a46fSAndroid Build Coastguard Worker ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
868*3ac0a46fSAndroid Build Coastguard Worker }
869*3ac0a46fSAndroid Build Coastguard Worker
870*3ac0a46fSAndroid Build Coastguard Worker UnloadPage(page);
871*3ac0a46fSAndroid Build Coastguard Worker }
872