Add GetAttribute APIs for structure element
Adding a set of APIs to access "A" attribute map of structure element.
Change-Id: I5b071debdbcb23f9e409eaf7a5d335bc1fa703a1
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/90450
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index a037b7a..b585ad1 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -185,6 +185,15 @@
return reinterpret_cast<CPDF_StructElement*>(struct_element);
}
+inline FPDF_STRUCTELEMENT_ATTR FPDFStructElementAttrFromCPDFDictionary(
+ const CPDF_Dictionary* dictionary) {
+ return reinterpret_cast<FPDF_STRUCTELEMENT_ATTR>(dictionary);
+}
+inline const CPDF_Dictionary* CPDFDictionaryFromFPDFStructElementAttr(
+ FPDF_STRUCTELEMENT_ATTR struct_element_attr) {
+ return reinterpret_cast<const CPDF_Dictionary*>(struct_element_attr);
+}
+
inline FPDF_TEXTPAGE FPDFTextPageFromCPDFTextPage(CPDF_TextPage* page) {
return reinterpret_cast<FPDF_TEXTPAGE>(page);
}
diff --git a/fpdfsdk/fpdf_structtree.cpp b/fpdfsdk/fpdf_structtree.cpp
index e429e68..538ad2e 100644
--- a/fpdfsdk/fpdf_structtree.cpp
+++ b/fpdfsdk/fpdf_structtree.cpp
@@ -120,6 +120,46 @@
buflen);
}
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element) {
+ CPDF_StructElement* elem =
+ CPDFStructElementFromFPDFStructElement(struct_element);
+ const CPDF_Dictionary* dict = elem ? elem->GetDict() : nullptr;
+ const CPDF_Object* attr_obj = dict ? dict->GetObjectFor("A") : nullptr;
+ if (!attr_obj)
+ return -1;
+
+ if (attr_obj->IsArray())
+ return attr_obj->AsArray()->size();
+ return attr_obj->IsDictionary() ? 1 : -1;
+}
+
+FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV
+FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element,
+ int index) {
+ CPDF_StructElement* elem =
+ CPDFStructElementFromFPDFStructElement(struct_element);
+ const CPDF_Dictionary* dict = elem ? elem->GetDict() : nullptr;
+ const CPDF_Object* attr_obj = dict ? dict->GetObjectFor("A") : nullptr;
+ if (!attr_obj)
+ return nullptr;
+
+ if (attr_obj->IsDictionary()) {
+ return index == 0 ? FPDFStructElementAttrFromCPDFDictionary(
+ attr_obj->AsDictionary())
+ : nullptr;
+ }
+
+ if (attr_obj->IsArray()) {
+ const CPDF_Array* array = attr_obj->AsArray();
+ if (index < 0 || static_cast<size_t>(index) >= array->size())
+ return nullptr;
+ return FPDFStructElementAttrFromCPDFDictionary(array->GetDictAt(index));
+ }
+
+ return nullptr;
+}
+
FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element,
FPDF_BYTESTRING attr_name,
@@ -218,3 +258,141 @@
}
return FPDFStructElementFromCPDFStructElement(parent);
}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute) {
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return -1;
+ return fxcrt::CollectionSize<int>(*dict);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ int index,
+ void* buffer,
+ unsigned long buflen,
+ unsigned long* out_buflen) {
+ if (!out_buflen || !buffer)
+ return false;
+
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return false;
+
+ CPDF_DictionaryLocker locker(dict);
+ for (auto& it : locker) {
+ if (index == 0) {
+ *out_buflen =
+ NulTerminateMaybeCopyAndReturnLength(it.first, buffer, buflen);
+ return true;
+ }
+ --index;
+ }
+ return false;
+}
+
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name) {
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return FPDF_OBJECT_UNKNOWN;
+
+ const CPDF_Object* obj = dict->GetObjectFor(name);
+ return obj ? obj->GetType() : FPDF_OBJECT_UNKNOWN;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_StructElement_Attr_GetBooleanValue(
+ FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ FPDF_BOOL* out_value) {
+ if (!out_value)
+ return false;
+
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return false;
+
+ const CPDF_Object* obj = dict->GetObjectFor(name);
+ if (!obj || !obj->IsBoolean())
+ return false;
+
+ *out_value = obj->GetInteger();
+ return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ float* out_value) {
+ if (!out_value)
+ return false;
+
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return false;
+
+ const CPDF_Object* obj = dict->GetObjectFor(name);
+ if (!obj || !obj->IsNumber())
+ return false;
+
+ *out_value = obj->GetNumber();
+ return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ void* buffer,
+ unsigned long buflen,
+ unsigned long* out_buflen) {
+ if (!out_buflen)
+ return false;
+
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return false;
+
+ const CPDF_Object* obj = dict->GetObjectFor(name);
+ if (!obj || !(obj->IsString() || obj->IsName()))
+ return false;
+
+ *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
+ WideString::FromUTF8(obj->GetString().AsStringView()), buffer, buflen);
+ return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ void* buffer,
+ unsigned long buflen,
+ unsigned long* out_buflen) {
+ if (!out_buflen)
+ return false;
+
+ const CPDF_Dictionary* dict =
+ CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
+ if (!dict)
+ return false;
+
+ const CPDF_Object* obj = dict->GetObjectFor(name);
+ if (!obj || !obj->IsString())
+ return false;
+
+ ByteString result = obj->GetString();
+ unsigned long len = result.GetLength();
+
+ if (buffer && len <= buflen)
+ memcpy(buffer, result.c_str(), len);
+
+ *out_buflen = len;
+ return true;
+}
diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp
index 5ba2bfe..1d90b90 100644
--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp
+++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp
@@ -497,6 +497,152 @@
UnloadPage(page);
}
+TEST_F(FPDFStructTreeEmbedderTest, GetAttributes) {
+ ASSERT_TRUE(OpenDocument("tagged_table.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+
+ {
+ ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(page));
+ ASSERT_TRUE(struct_tree);
+ ASSERT_EQ(1, FPDF_StructTree_CountChildren(struct_tree.get()));
+
+ FPDF_STRUCTELEMENT document =
+ FPDF_StructTree_GetChildAtIndex(struct_tree.get(), 0);
+ ASSERT_TRUE(document);
+
+ ASSERT_EQ(1, FPDF_StructElement_CountChildren(document));
+ ASSERT_EQ(-1, FPDF_StructElement_GetAttributeCount(document));
+ FPDF_STRUCTELEMENT table = FPDF_StructElement_GetChildAtIndex(document, 0);
+ ASSERT_TRUE(table);
+
+ ASSERT_EQ(2, FPDF_StructElement_CountChildren(table));
+
+ {
+ FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 0);
+ ASSERT_TRUE(tr);
+
+ ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr));
+ FPDF_STRUCTELEMENT th = FPDF_StructElement_GetChildAtIndex(tr, 0);
+ ASSERT_TRUE(th);
+
+ ASSERT_EQ(2, FPDF_StructElement_GetAttributeCount(th));
+
+ // nullptr test
+ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, 0));
+ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(document, -1));
+ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(th, 2));
+
+ FPDF_STRUCTELEMENT_ATTR attr =
+ FPDF_StructElement_GetAttributeAtIndex(th, 1);
+ ASSERT_TRUE(attr);
+
+ ASSERT_EQ(2, FPDF_StructElement_Attr_GetCount(attr));
+ ASSERT_FALSE(
+ FPDF_StructElement_Attr_GetName(attr, 1, nullptr, 0U, nullptr));
+ char buffer[8] = {};
+ unsigned long out_len = ULONG_MAX;
+ // Deliberately pass in a small buffer size to make sure `buffer` remains
+ // untouched.
+ ASSERT_TRUE(
+ FPDF_StructElement_Attr_GetName(attr, 1, buffer, 1, &out_len));
+ EXPECT_EQ(2U, out_len);
+ for (size_t i = 0; i < pdfium::size(buffer); ++i)
+ EXPECT_EQ(0, buffer[i]);
+
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 1, buffer,
+ sizeof(buffer), &out_len));
+ EXPECT_EQ(2U, out_len);
+ EXPECT_STREQ("O", buffer);
+ EXPECT_EQ(FPDF_OBJECT_NAME,
+ FPDF_StructElement_Attr_GetType(attr, buffer));
+
+ unsigned short str_val[12] = {};
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue(
+ attr, buffer, str_val, sizeof(str_val), &out_len));
+ EXPECT_EQ(12U, out_len);
+ EXPECT_EQ(L"Table", GetPlatformWString(str_val));
+
+ memset(buffer, 0, sizeof(buffer));
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 0, buffer,
+ sizeof(buffer), &out_len));
+ EXPECT_EQ(8U, out_len);
+ EXPECT_STREQ("ColSpan", buffer);
+ EXPECT_EQ(FPDF_OBJECT_NUMBER,
+ FPDF_StructElement_Attr_GetType(attr, buffer));
+ float num_val;
+ ASSERT_TRUE(
+ FPDF_StructElement_Attr_GetNumberValue(attr, buffer, &num_val));
+ EXPECT_FLOAT_EQ(2.0f, num_val);
+ }
+
+ {
+ FPDF_STRUCTELEMENT tr = FPDF_StructElement_GetChildAtIndex(table, 1);
+ ASSERT_TRUE(tr);
+
+ ASSERT_EQ(1, FPDF_StructElement_GetAttributeCount(tr));
+ // nullptr when index out of range
+ ASSERT_EQ(nullptr, FPDF_StructElement_GetAttributeAtIndex(tr, 1));
+
+ ASSERT_EQ(2, FPDF_StructElement_CountChildren(tr));
+ FPDF_STRUCTELEMENT td = FPDF_StructElement_GetChildAtIndex(tr, 1);
+ ASSERT_TRUE(td);
+ {
+ 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));
+ // Test string and blob type
+ {
+ char buffer[16] = {};
+ unsigned long out_len = ULONG_MAX;
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
+ attr, 0, buffer, sizeof(buffer), &out_len));
+ EXPECT_EQ(8U, out_len);
+ EXPECT_STREQ("ColProp", buffer);
+
+ EXPECT_EQ(FPDF_OBJECT_STRING,
+ FPDF_StructElement_Attr_GetType(attr, buffer));
+
+ unsigned short str_val[12] = {};
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetStringValue(
+ attr, buffer, str_val, sizeof(str_val), &out_len));
+ EXPECT_EQ(8U, out_len);
+ EXPECT_EQ(L"Sum", GetPlatformWString(str_val));
+
+ char blob_val[3] = {};
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetBlobValue(
+ attr, buffer, blob_val, sizeof(blob_val), &out_len));
+ EXPECT_EQ(3U, out_len);
+ EXPECT_EQ('S', blob_val[0]);
+ EXPECT_EQ('u', blob_val[1]);
+ EXPECT_EQ('m', blob_val[2]);
+ }
+
+ // Test boolean type
+ {
+ char buffer[16] = {};
+ unsigned long out_len = ULONG_MAX;
+ ASSERT_TRUE(FPDF_StructElement_Attr_GetName(
+ attr, 1, buffer, sizeof(buffer), &out_len));
+ EXPECT_EQ(7U, out_len);
+ EXPECT_STREQ("CurUSD", buffer);
+
+ EXPECT_EQ(FPDF_OBJECT_BOOLEAN,
+ FPDF_StructElement_Attr_GetType(attr, buffer));
+ FPDF_BOOL val;
+ ASSERT_TRUE(
+ FPDF_StructElement_Attr_GetBooleanValue(attr, buffer, &val));
+ EXPECT_TRUE(val);
+ }
+ }
+ }
+ }
+
+ UnloadPage(page);
+}
+
TEST_F(FPDFStructTreeEmbedderTest, GetStructTreeForNestedTaggedPDF) {
ASSERT_TRUE(OpenDocument("tagged_nested.pdf"));
FPDF_PAGE page = LoadPage(0);
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index f078104..38fc525 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -352,9 +352,18 @@
CHK(FPDF_GetSignatureObject);
// fpdf_structtree.h
+ CHK(FPDF_StructElement_Attr_GetBlobValue);
+ CHK(FPDF_StructElement_Attr_GetBooleanValue);
+ CHK(FPDF_StructElement_Attr_GetCount);
+ CHK(FPDF_StructElement_Attr_GetName);
+ CHK(FPDF_StructElement_Attr_GetNumberValue);
+ CHK(FPDF_StructElement_Attr_GetStringValue);
+ CHK(FPDF_StructElement_Attr_GetType);
CHK(FPDF_StructElement_CountChildren);
CHK(FPDF_StructElement_GetActualText);
CHK(FPDF_StructElement_GetAltText);
+ CHK(FPDF_StructElement_GetAttributeAtIndex);
+ CHK(FPDF_StructElement_GetAttributeCount);
CHK(FPDF_StructElement_GetChildAtIndex);
CHK(FPDF_StructElement_GetID);
CHK(FPDF_StructElement_GetLang);
diff --git a/public/fpdf_structtree.h b/public/fpdf_structtree.h
index 1485bb0..2de41af 100644
--- a/public/fpdf_structtree.h
+++ b/public/fpdf_structtree.h
@@ -243,8 +243,8 @@
// Function: FPDF_StructElement_GetChildAtIndex
// Get a child in the structure element.
// Parameters:
-// struct_tree - Handle to the struct element.
-// index - The index for the child, 0-based.
+// struct_element - Handle to the struct element.
+// index - The index for the child, 0-based.
// Return value:
// The child at the n-th index or NULL on error.
// Comments:
@@ -258,7 +258,7 @@
// Function: FPDF_StructElement_GetParent
// Get the parent of the structure element.
// Parameters:
-// struct_tree - Handle to the struct element.
+// struct_element - Handle to the struct element.
// Return value:
// The parent structure element or NULL on error.
// Comments:
@@ -267,6 +267,163 @@
FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element);
+// Function: FPDF_StructElement_GetAttributeCount
+// Count the number of attributes for the structure element.
+// Parameters:
+// struct_element - Handle to the struct element.
+// Return value:
+// The number of attributes, or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element);
+
+// Experimental API.
+// Function: FPDF_StructElement_GetAttributeAtIndex
+// Get an attribute object in the structure element.
+// Parameters:
+// struct_element - Handle to the struct element.
+// index - The index for the attribute object, 0-based.
+// Return value:
+// The attribute object at the n-th index or NULL on error.
+// Comments:
+// If the attribute object exists but is not a dict, then this
+// function will return NULL. This will also return NULL for out of
+// bounds indices.
+FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV
+FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element, int index);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetCount
+// Count the number of attributes in a structure element attribute map.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// Return value:
+// The number of attributes, or -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute);
+
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetName
+// Get the name of an attribute in a structure element attribute map.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// index - The index of attribute in the map.
+// buffer - A buffer for output. May be NULL. This is only
+// modified if |buflen| is longer than the length
+// of the key. Optional, pass null to just
+// retrieve the size of the buffer needed.
+// buflen - The length of the buffer.
+// out_buflen - A pointer to variable that will receive the
+// minimum buffer size to contain the key. Not
+// filled if FALSE is returned.
+// Return value:
+// TRUE if the operation was successful, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ int index,
+ void* buffer,
+ unsigned long buflen,
+ unsigned long* out_buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetType
+// Get the type of an attribute in a structure element attribute map.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// name - The attribute name.
+// Return value:
+// Returns the type of the value, or FPDF_OBJECT_UNKNOWN in case of
+// failure.
+FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
+FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetBooleanValue
+// Get the value of a boolean attribute in an attribute map by name as
+// FPDF_BOOL. FPDF_StructElement_Attr_GetType() should have returned
+// FPDF_OBJECT_BOOLEAN for this property.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// name - The attribute name.
+// out_value - A pointer to variable that will receive the
+// value. Not filled if false is returned.
+// Return value:
+// Returns TRUE if the name maps to a boolean value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetBooleanValue(
+ FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ FPDF_BOOL* out_value);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetNumberValue
+// Get the value of a number attribute in an attribute map by name as
+// float. FPDF_StructElement_Attr_GetType() should have returned
+// FPDF_OBJECT_NUMBER for this property.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// name - The attribute name.
+// out_value - A pointer to variable that will receive the
+// value. Not filled if false is returned.
+// Return value:
+// Returns TRUE if the name maps to a number value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ float* out_value);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetStringValue
+// Get the value of a string attribute in an attribute map by name as
+// string. FPDF_StructElement_Attr_GetType() should have returned
+// FPDF_OBJECT_STRING or FPDF_OBJECT_NAME for this property.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// name - The attribute name.
+// buffer - A buffer for holding the returned key in
+// UTF-16LE. This is only modified if |buflen| is
+// longer than the length of the key. Optional,
+// pass null to just retrieve the size of the
+// buffer needed.
+// buflen - The length of the buffer.
+// out_buflen - A pointer to variable that will receive the
+// minimum buffer size to contain the key. Not
+// filled if FALSE is returned.
+// Return value:
+// Returns TRUE if the name maps to a string value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ void* buffer,
+ unsigned long buflen,
+ unsigned long* out_buflen);
+
+// Experimental API.
+// Function: FPDF_StructElement_Attr_GetBlobValue
+// Get the value of a blob attribute in an attribute map by name as
+// string.
+// Parameters:
+// struct_attribute - Handle to the struct element attribute.
+// name - The attribute name.
+// buffer - A buffer for holding the returned value. This
+// is only modified if |buflen| is at least as
+// long as the length of the value. Optional, pass
+// null to just retrieve the size of the buffer
+// needed.
+// buflen - The length of the buffer.
+// out_buflen - A pointer to variable that will receive the
+// minimum buffer size to contain the key. Not
+// filled if FALSE is returned.
+// Return value:
+// Returns TRUE if the name maps to a string value, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
+ FPDF_BYTESTRING name,
+ void* buffer,
+ unsigned long buflen,
+ unsigned long* out_buflen);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 779fa5f..a06f8f8 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -75,6 +75,7 @@
typedef struct fpdf_schhandle_t__* FPDF_SCHHANDLE;
typedef struct fpdf_signature_t__* FPDF_SIGNATURE;
typedef struct fpdf_structelement_t__* FPDF_STRUCTELEMENT;
+typedef const struct fpdf_structelement_attr_t__* FPDF_STRUCTELEMENT_ATTR;
typedef struct fpdf_structtree_t__* FPDF_STRUCTTREE;
typedef struct fpdf_textpage_t__* FPDF_TEXTPAGE;
typedef struct fpdf_widget_t__* FPDF_WIDGET;
diff --git a/testing/resources/tagged_table.in b/testing/resources/tagged_table.in
index 70df01c..ee298c5 100644
--- a/testing/resources/tagged_table.in
+++ b/testing/resources/tagged_table.in
@@ -156,6 +156,9 @@
/K [16 0 R 17 0 R]
/P 11 0 R
/Pg 3 0 R
+ /A <<
+ /O /Table
+ >>
/ID (node14)
>>
endobj
@@ -200,6 +203,11 @@
/S /TD
/P 13 0 R
/Pg 3 0 R
+ /A [<<
+ /O /Table
+ /ColProp (Sum)
+ /CurUSD true
+ >>]
/ID (node18)
>>
endobj
diff --git a/testing/resources/tagged_table.pdf b/testing/resources/tagged_table.pdf
index b11437c..4428682 100644
--- a/testing/resources/tagged_table.pdf
+++ b/testing/resources/tagged_table.pdf
@@ -157,6 +157,9 @@
/K [16 0 R 17 0 R]
/P 11 0 R
/Pg 3 0 R
+ /A <<
+ /O /Table
+ >>
/ID (node14)
>>
endobj
@@ -201,6 +204,11 @@
/S /TD
/P 13 0 R
/Pg 3 0 R
+ /A [<<
+ /O /Table
+ /ColProp (Sum)
+ /CurUSD true
+ >>]
/ID (node18)
>>
endobj
@@ -220,14 +228,14 @@
0000001642 00000 n
0000001826 00000 n
0000001931 00000 n
-0000002042 00000 n
-0000002244 00000 n
-0000002334 00000 n
-0000002481 00000 n
+0000002074 00000 n
+0000002276 00000 n
+0000002366 00000 n
+0000002513 00000 n
trailer <<
/Root 1 0 R
/Size 18
>>
startxref
-2571
+2683
%%EOF