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