1 /*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7 #include "tests/Test.h"
8
9 #ifdef SK_SUPPORT_PDF
10
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkStream.h"
14 #include "include/docs/SkPDFDocument.h"
15
16 using PDFTag = SkPDF::StructureElementNode;
17
18 // Test building a tagged PDF containing a table.
19 // Add this to args.gn to output the PDF to a file:
20 // extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/table.pdf\"" ]
DEF_TEST(SkPDF_tagged_table,r)21 DEF_TEST(SkPDF_tagged_table, r) {
22 REQUIRE_PDF_DOCUMENT(SkPDF_tagged, r);
23 #ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH
24 SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH);
25 #else
26 SkDynamicMemoryWStream outputStream;
27 #endif
28
29 SkSize pageSize = SkSize::Make(612, 792); // U.S. Letter
30
31 SkPDF::Metadata metadata;
32 metadata.fTitle = "Example Tagged Table PDF";
33 metadata.fCreator = "Skia";
34 SkTime::DateTime now;
35 SkTime::GetDateTime(&now);
36 metadata.fCreation = now;
37 metadata.fModified = now;
38
39 constexpr int kRowCount = 5;
40 constexpr int kColCount = 4;
41 const char* cellData[kRowCount * kColCount] = {
42 "Car", "Engine", "City MPG", "Highway MPG",
43 "Mitsubishi Mirage ES", "Gas", "28", "47",
44 "Toyota Prius Three", "Hybrid", "43", "59",
45 "Nissan Leaf SL", "Electric", "N/A", nullptr,
46 "Tesla Model 3", nullptr, "N/A", nullptr
47 };
48
49 // The document tag.
50 auto root = std::make_unique<PDFTag>();
51 root->fNodeId = 1;
52 root->fTypeString = "Document";
53 root->fLang = "en-US";
54
55 // Heading.
56 auto h1 = std::make_unique<PDFTag>();
57 h1->fNodeId = 2;
58 h1->fTypeString = "H1";
59 h1->fAlt = "Tagged PDF Table Alt Text";
60 root->fChildVector.push_back(std::move(h1));
61
62 // Table.
63 auto table = std::make_unique<PDFTag>();
64 table->fNodeId = 3;
65 table->fTypeString = "Table";
66 auto& rows = table->fChildVector;
67 table->fAttributes.appendFloatArray("Layout", "BBox", {72, 72, 360, 360});
68
69 for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) {
70 auto row = std::make_unique<PDFTag>();
71 row->fNodeId = 4 + rowIndex;
72 row->fTypeString = "TR";
73 auto& cells = row->fChildVector;
74 for (int colIndex = 0; colIndex < kColCount; colIndex++) {
75 auto cell = std::make_unique<PDFTag>();
76 int cellIndex = rowIndex * kColCount + colIndex;
77 cell->fNodeId = 10 + cellIndex;
78 if (!cellData[cellIndex]) {
79 cell->fTypeString = "NonStruct";
80 } else if (rowIndex == 0 || colIndex == 0) {
81 cell->fTypeString = "TH";
82 } else {
83 cell->fTypeString = "TD";
84 std::vector<int> headerIds;
85 headerIds.push_back(10 + rowIndex * kColCount); // Row header
86 headerIds.push_back(10 + colIndex); // Col header.
87 cell->fAttributes.appendNodeIdArray(
88 "Table", "Headers", headerIds);
89 }
90
91 if (cellIndex == 13) {
92 cell->fAttributes.appendInt("Table", "RowSpan", 2);
93 } else if (cellIndex == 14 || cellIndex == 18) {
94 cell->fAttributes.appendInt("Table", "ColSpan", 2);
95 } else if (rowIndex == 0 || colIndex == 0) {
96 cell->fAttributes.appendName(
97 "Table", "Scope", rowIndex == 0 ? "Column" : "Row");
98 }
99 cells.push_back(std::move(cell));
100 }
101 rows.push_back(std::move(row));
102 }
103 root->fChildVector.push_back(std::move(table));
104
105 metadata.fStructureElementTreeRoot = root.get();
106 sk_sp<SkDocument> document = SkPDF::MakeDocument(
107 &outputStream, metadata);
108
109 SkPaint paint;
110 paint.setColor(SK_ColorBLACK);
111
112 SkCanvas* canvas =
113 document->beginPage(pageSize.width(),
114 pageSize.height());
115 SkPDF::SetNodeId(canvas, 2);
116 SkFont font(nullptr, 36);
117 canvas->drawString("Tagged PDF Table", 72, 72, font, paint);
118
119 font.setSize(14);
120 for (int rowIndex = 0; rowIndex < kRowCount; rowIndex++) {
121 for (int colIndex = 0; colIndex < kColCount; colIndex++) {
122 int cellIndex = rowIndex * kColCount + colIndex;
123 const char* str = cellData[cellIndex];
124 if (!str)
125 continue;
126
127 int x = 72 + colIndex * 108 + (colIndex > 0 ? 72 : 0);
128 int y = 144 + rowIndex * 48;
129
130 SkPDF::SetNodeId(canvas, 10 + cellIndex);
131 canvas->drawString(str, x, y, font, paint);
132 }
133 }
134
135 document->endPage();
136 document->close();
137 outputStream.flush();
138 }
139
140 #endif
141