[fpdf_structtree] Support references for attribute list/number

PDF software such as Adobe Acrobat Pro sometimes uses references for attribute lists (instead of array or dictionary). Similarly, attributes themselves (e.g. "ColSpan" for a TD or TH cell) sometimes use references as well.

Change-Id: I1bd8f56348ba48d84b72179c86db688304660682
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/110850
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Ilaï Deutel <idtl@google.com>
diff --git a/fpdfsdk/fpdf_structtree.cpp b/fpdfsdk/fpdf_structtree.cpp
index dc68239..4817f47 100644
--- a/fpdfsdk/fpdf_structtree.cpp
+++ b/fpdfsdk/fpdf_structtree.cpp
@@ -135,6 +135,10 @@
   if (!elem)
     return -1;
   RetainPtr<const CPDF_Object> attr_obj = elem->GetA();
+  if (!attr_obj) {
+    return -1;
+  }
+  attr_obj = attr_obj->GetDirect();
   if (!attr_obj)
     return -1;
   if (attr_obj->IsArray())
@@ -154,6 +158,10 @@
   if (!attr_obj)
     return nullptr;
 
+  attr_obj = attr_obj->GetDirect();
+  if (!attr_obj) {
+    return nullptr;
+  }
   if (attr_obj->IsDictionary()) {
     return index == 0 ? FPDFStructElementAttrFromCPDFDictionary(
                             attr_obj->AsDictionary())
@@ -353,7 +361,7 @@
   if (!dict)
     return false;
 
-  RetainPtr<const CPDF_Object> obj = dict->GetObjectFor(name);
+  RetainPtr<const CPDF_Object> obj = dict->GetDirectObjectFor(name);
   if (!obj || !obj->IsNumber())
     return false;
 
diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp
index 1ee1612..2704887 100644
--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp
+++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp
@@ -648,11 +648,12 @@
       FPDF_STRUCTELEMENT td = FPDF_StructElement_GetChildAtIndex(tr, 1);
       ASSERT_TRUE(td);
       {
+        // Test counting and obtaining attributes via reference
         ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(td));
         FPDF_STRUCTELEMENT_ATTR attr =
             FPDF_StructElement_GetAttributeAtIndex(td, 0);
         ASSERT_TRUE(attr);
-        ASSERT_EQ(3, FPDF_StructElement_Attr_GetCount(attr));
+        ASSERT_EQ(4, FPDF_StructElement_Attr_GetCount(attr));
         // Test string and blob type
         {
           char buffer[16] = {};
@@ -696,6 +697,23 @@
               FPDF_StructElement_Attr_GetBooleanValue(attr, buffer, &val));
           EXPECT_TRUE(val);
         }
+
+        // Test reference to number
+        {
+          char buffer[16] = {};
+          unsigned long out_len = ULONG_MAX;
+          ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
+              attr, 3, buffer, sizeof(buffer), &out_len));
+          EXPECT_EQ(8U, out_len);
+          EXPECT_STREQ("RowSpan", buffer);
+
+          EXPECT_EQ(FPDF_OBJECT_REFERENCE,
+                    FPDF_StructElement_Attr_GetType(attr, buffer));
+          float val;
+          ASSERT_TRUE(
+              FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &val));
+          EXPECT_FLOAT_EQ(3, val);
+        }
       }
     }
   }
diff --git a/testing/resources/tagged_table.in b/testing/resources/tagged_table.in
index ee298c5..6e2ddc0 100644
--- a/testing/resources/tagged_table.in
+++ b/testing/resources/tagged_table.in
@@ -203,14 +203,22 @@
   /S /TD
   /P 13 0 R
   /Pg 3 0 R
-  /A [<<
-        /O /Table
-        /ColProp (Sum)
-        /CurUSD true
-     >>]
+  /A 18 0 R
   /ID (node18)
 >>
 endobj
+{{object 18 0}} [
+  <<
+    /O /Table
+    /ColProp (Sum)
+    /CurUSD true
+    /RowSpan 19 0 R
+  >>
+]
+endobj
+{{object 19 0}}
+  3
+endobj
 {{xref}}
 {{trailer}}
 {{startxref}}
diff --git a/testing/resources/tagged_table.pdf b/testing/resources/tagged_table.pdf
index 4428682..6e2bf9e 100644
--- a/testing/resources/tagged_table.pdf
+++ b/testing/resources/tagged_table.pdf
@@ -204,16 +204,24 @@
   /S /TD
   /P 13 0 R
   /Pg 3 0 R
-  /A [<<
-        /O /Table
-        /ColProp (Sum)
-        /CurUSD true
-     >>]
+  /A 18 0 R
   /ID (node18)
 >>
 endobj
+18 0 obj [
+  <<
+    /O /Table
+    /ColProp (Sum)
+    /CurUSD true
+    /RowSpan 19 0 R
+  >>
+]
+endobj
+19 0 obj
+  3
+endobj
 xref
-0 18
+0 20
 0000000000 65535 f 
 0000000015 00000 n 
 0000000145 00000 n 
@@ -232,10 +240,12 @@
 0000002276 00000 n 
 0000002366 00000 n 
 0000002513 00000 n 
+0000002615 00000 n 
+0000002715 00000 n 
 trailer <<
   /Root 1 0 R
-  /Size 18
+  /Size 20
 >>
 startxref
-2683
+2735
 %%EOF