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