Simplify interface to StructureElementNode.

Use a std::vector of std::unique_ptr for the children, rather than an
error-prone raw pointer.

Use a string for the element type, rather than an enum.

In both cases, both the old and new format are supported and tested
within Skia until Chromium has migrated.

Also add support for Alt and Lang, which are like attributes
but go right on the structure node. There are only a small
handful of attributes that go on the structure node so this
seems like the simplest solution.

Bug: chromium:607777
Change-Id: I4f315685df35dd9dcd8e1bca6d275cbeb8f2c67a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/271816
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
This commit is contained in:
Dominic Mazzoni 2020-02-20 11:34:24 -08:00 committed by Skia Commit-Bot
parent 1d589a578c
commit 6ffabbb4c4
3 changed files with 82 additions and 48 deletions

View File

@ -108,11 +108,19 @@ private:
NodeIDs should be unique within each tree.
*/
struct StructureElementNode {
SkString fTypeString;
std::vector<std::unique_ptr<StructureElementNode>> fChildVector;
int fNodeId = 0;
AttributeList fAttributes;
SkString fAlt;
SkString fLang;
// Deprecated. Use fChildVector instead.
StructureElementNode* fChildren = nullptr;
size_t fChildCount = 0;
int fNodeId = 0;
// Deprecated. Use fTypeString instead.
DocumentStructureType fType = DocumentStructureType::kNonStruct;
AttributeList fAttributes;
};
/** Optional metadata to be passed into the PDF factory function.

View File

@ -140,6 +140,9 @@ struct SkPDFTagNode {
SkTArray<MarkedContentInfo> fMarkedContent;
int fNodeId;
SkPDF::DocumentStructureType fType;
SkString fTypeString;
SkString fAlt;
SkString fLang;
SkPDFIndirectReference fRef;
enum State {
kUnknown,
@ -159,15 +162,31 @@ void SkPDFTagTree::Copy(SkPDF::StructureElementNode& node,
SkArenaAlloc* arena,
SkTHashMap<int, SkPDFTagNode*>* nodeMap) {
nodeMap->set(node.fNodeId, dst);
size_t childCount = node.fChildCount;
SkPDFTagNode* children = arena->makeArray<SkPDFTagNode>(childCount);
dst->fChildCount = childCount;
dst->fNodeId = node.fNodeId;
dst->fType = node.fType;
dst->fChildren = children;
for (size_t i = 0; i < childCount; ++i) {
Copy(node.fChildren[i], &children[i], arena, nodeMap);
dst->fTypeString = node.fTypeString;
dst->fAlt = node.fAlt;
dst->fLang = node.fLang;
// Temporarily support both raw fChildren and fChildVector.
if (node.fChildren) {
size_t childCount = node.fChildCount;
SkPDFTagNode* children = arena->makeArray<SkPDFTagNode>(childCount);
dst->fChildCount = childCount;
dst->fChildren = children;
for (size_t i = 0; i < childCount; ++i) {
Copy(node.fChildren[i], &children[i], arena, nodeMap);
}
} else {
size_t childCount = node.fChildVector.size();
SkPDFTagNode* children = arena->makeArray<SkPDFTagNode>(childCount);
dst->fChildCount = childCount;
dst->fChildren = children;
for (size_t i = 0; i < childCount; ++i) {
Copy(*node.fChildVector[i], &children[i], arena, nodeMap);
}
}
dst->fAttributes = std::move(node.fAttributes.fAttrs);
}
@ -248,7 +267,17 @@ SkPDFIndirectReference prepare_tag_tree_to_emit(SkPDFIndirectReference parent,
}
node->fRef = ref;
SkPDFDict dict("StructElem");
dict.insertName("S", tag_name_from_type(node->fType));
if (!node->fTypeString.isEmpty()) {
dict.insertName("S", node->fTypeString.c_str());
} else {
dict.insertName("S", tag_name_from_type(node->fType));
}
if (!node->fAlt.isEmpty()) {
dict.insertName("Alt", node->fAlt);
}
if (!node->fLang.isEmpty()) {
dict.insertName("Lang", node->fLang);
}
dict.insertRef("P", parent);
dict.insertObject("K", std::move(kids));
SkString idString;

View File

@ -47,61 +47,58 @@ DEF_TEST(SkPDF_tagged_table, r) {
};
// The document tag.
PDFTag root;
root.fNodeId = 1;
root.fType = SkPDF::DocumentStructureType::kDocument;
root.fChildCount = 2;
PDFTag rootChildren[2];
auto root = std::make_unique<PDFTag>();
root->fNodeId = 1;
root->fTypeString = "Document";
root->fLang = "en-US";
// Heading.
PDFTag& h1 = rootChildren[0];
h1.fNodeId = 2;
h1.fType = SkPDF::DocumentStructureType::kH1;
h1.fChildCount = 0;
auto h1 = std::make_unique<PDFTag>();
h1->fNodeId = 2;
h1->fTypeString = "H1";
h1->fAlt = "Tagged PDF Table Alt Text";
root->fChildVector.push_back(std::move(h1));
// Table.
PDFTag& table = rootChildren[1];
table.fNodeId = 3;
table.fType = SkPDF::DocumentStructureType::kTable;
table.fChildCount = 5;
table.fAttributes.appendFloatArray("Layout", "BBox", {72, 72, 360, 360});
auto table = std::make_unique<PDFTag>();
table->fNodeId = 3;
table->fTypeString = "Table";
auto& rows = table->fChildVector;
table->fAttributes.appendFloatArray("Layout", "BBox", {72, 72, 360, 360});
PDFTag rows[kRowCount];
PDFTag all_cells[kRowCount * kColCount];
for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) {
PDFTag& row = rows[rowIndex];
row.fNodeId = 4 + rowIndex;
row.fType = SkPDF::DocumentStructureType::kTR;
row.fChildCount = kColCount;
PDFTag* cells = &all_cells[rowIndex * kColCount];
auto row = std::make_unique<PDFTag>();
row->fNodeId = 4 + rowIndex;
row->fTypeString = "TR";
auto& cells = row->fChildVector;
for (int colIndex = 0; colIndex < kColCount; colIndex++) {
auto cell = std::make_unique<PDFTag>();
int cellIndex = rowIndex * kColCount + colIndex;
PDFTag& cell = cells[colIndex];
cell.fNodeId = 10 + cellIndex;
if (!cellData[cellIndex])
cell.fType = SkPDF::DocumentStructureType::kNonStruct;
else if (rowIndex == 0 || colIndex == 0)
cell.fType = SkPDF::DocumentStructureType::kTH;
else
cell.fType = SkPDF::DocumentStructureType::kTD;
cell.fChildCount = 0;
cell->fNodeId = 10 + cellIndex;
if (!cellData[cellIndex]) {
cell->fTypeString = "NonStruct";
} else if (rowIndex == 0 || colIndex == 0) {
cell->fTypeString = "TH";
} else {
cell->fTypeString = "TD";
}
cell->fChildCount = 0;
if (cellIndex == 13) {
cell.fAttributes.appendInt("Table", "RowSpan", 2);
cell->fAttributes.appendInt("Table", "RowSpan", 2);
} else if (cellIndex == 14 || cellIndex == 18) {
cell.fAttributes.appendInt("Table", "ColSpan", 2);
} else if (cell.fType == SkPDF::DocumentStructureType::kTH) {
cell.fAttributes.appendString(
cell->fAttributes.appendInt("Table", "ColSpan", 2);
} else if (rowIndex == 0 || colIndex == 0) {
cell->fAttributes.appendString(
"Table", "Scope", rowIndex == 0 ? "Column" : "Row");
}
cells.push_back(std::move(cell));
}
row.fChildren = cells;
rows.push_back(std::move(row));
}
table.fChildren = rows;
root.fChildren = rootChildren;
root->fChildVector.push_back(std::move(table));
metadata.fStructureElementTreeRoot = &root;
metadata.fStructureElementTreeRoot = root.get();
sk_sp<SkDocument> document = SkPDF::MakeDocument(
&outputStream, metadata);