skia2/tests/PDFTaggedTableTest.cpp
Dominic Mazzoni 6ffabbb4c4 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>
2020-02-20 21:16:48 +00:00

137 lines
4.4 KiB
C++

/*
* Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/Test.h"
#ifdef SK_SUPPORT_PDF
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkStream.h"
#include "include/docs/SkPDFDocument.h"
using PDFTag = SkPDF::StructureElementNode;
// Test building a tagged PDF containing a table.
// Add this to args.gn to output the PDF to a file:
// extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/table.pdf\"" ]
DEF_TEST(SkPDF_tagged_table, r) {
REQUIRE_PDF_DOCUMENT(SkPDF_tagged, r);
#ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH
SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH);
#else
SkDynamicMemoryWStream outputStream;
#endif
SkSize pageSize = SkSize::Make(612, 792); // U.S. Letter
SkPDF::Metadata metadata;
metadata.fTitle = "Example Tagged Table PDF";
metadata.fCreator = "Skia";
SkTime::DateTime now;
SkTime::GetDateTime(&now);
metadata.fCreation = now;
metadata.fModified = now;
constexpr int kRowCount = 5;
constexpr int kColCount = 4;
const char* cellData[kRowCount * kColCount] = {
"Car", "Engine", "City MPG", "Highway MPG",
"Mitsubishi Mirage ES", "Gas", "28", "47",
"Toyota Prius Three", "Hybrid", "43", "59",
"Nissan Leaf SL", "Electric", "N/A", nullptr,
"Tesla Model 3", nullptr, "N/A", nullptr
};
// The document tag.
auto root = std::make_unique<PDFTag>();
root->fNodeId = 1;
root->fTypeString = "Document";
root->fLang = "en-US";
// Heading.
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.
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});
for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) {
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;
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);
} else if (cellIndex == 14 || cellIndex == 18) {
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));
}
rows.push_back(std::move(row));
}
root->fChildVector.push_back(std::move(table));
metadata.fStructureElementTreeRoot = root.get();
sk_sp<SkDocument> document = SkPDF::MakeDocument(
&outputStream, metadata);
SkPaint paint;
paint.setColor(SK_ColorBLACK);
SkCanvas* canvas =
document->beginPage(pageSize.width(),
pageSize.height());
SkPDF::SetNodeId(canvas, 2);
SkFont font(nullptr, 36);
canvas->drawString("Tagged PDF Table", 72, 72, font, paint);
font.setSize(14);
for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) {
for (int colIndex = 0; colIndex < kColCount; colIndex++) {
int cellIndex = rowIndex * kColCount + colIndex;
const char* str = cellData[cellIndex];
if (!str)
continue;
int x = 72 + colIndex * 108 + (colIndex > 0 ? 72 : 0);
int y = 144 + rowIndex * 48;
SkPDF::SetNodeId(canvas, 10 + cellIndex);
canvas->drawString(str, x, y, font, paint);
}
}
document->endPage();
document->close();
outputStream.flush();
}
#endif