• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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