blob: 385e6275b60e384d0cfb881cf8dcacc183b4dc8a [file] [log] [blame]
Dominic Mazzoni656cefe2018-09-25 20:29:15 -07001/*
2 * Copyright 2018 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 */
Mike Kleinc0bd9f92019-04-23 12:05:21 -05007#include "tests/Test.h"
Dominic Mazzoni656cefe2018-09-25 20:29:15 -07008
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "include/core/SkCanvas.h"
10#include "include/core/SkFont.h"
11#include "include/core/SkStream.h"
12#include "include/docs/SkPDFDocument.h"
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070013
14using PDFTag = SkPDF::StructureElementNode;
15
16// Test building a tagged PDF.
17// Add this to args.gn to output the PDF to a file:
18// extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/foo.pdf\"" ]
Dominic Mazzoni8f233fc2020-03-06 11:20:45 -080019DEF_TEST(SkPDF_tagged_doc, r) {
20 REQUIRE_PDF_DOCUMENT(SkPDF_tagged_doc, r);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070021#ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH
22 SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH);
23#else
24 SkDynamicMemoryWStream outputStream;
25#endif
26
27 SkSize pageSize = SkSize::Make(612, 792); // U.S. Letter
28
29 SkPDF::Metadata metadata;
30 metadata.fTitle = "Example Tagged PDF";
31 metadata.fCreator = "Skia";
32 SkTime::DateTime now;
33 SkTime::GetDateTime(&now);
34 metadata.fCreation = now;
35 metadata.fModified = now;
36
37 // The document tag.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070038 auto root = std::make_unique<PDFTag>();
39 root->fNodeId = 1;
40 root->fType = SkPDF::DocumentStructureType::kDocument;
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070041
42 // Heading.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070043 auto h1 = std::make_unique<PDFTag>();
44 h1->fNodeId = 2;
45 h1->fType = SkPDF::DocumentStructureType::kH1;
46 root->fChildVector.push_back(std::move(h1));
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070047
48 // Initial paragraph.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070049 auto p = std::make_unique<PDFTag>();
50 p->fNodeId = 3;
51 p->fType = SkPDF::DocumentStructureType::kP;
52 root->fChildVector.push_back(std::move(p));
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070053
54 // Hidden div. This is never referenced by marked content
55 // so it should not appear in the resulting PDF.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070056 auto div = std::make_unique<PDFTag>();
57 div->fNodeId = 4;
58 div->fType = SkPDF::DocumentStructureType::kDiv;
59 root->fChildVector.push_back(std::move(div));
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070060
61 // A bulleted list of two items.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070062 auto l = std::make_unique<PDFTag>();
63 l->fNodeId = 5;
64 l->fType = SkPDF::DocumentStructureType::kL;
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070065
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070066 auto lm1 = std::make_unique<PDFTag>();
67 lm1->fNodeId = 6;
68 lm1->fType = SkPDF::DocumentStructureType::kLbl;
69 l->fChildVector.push_back(std::move(lm1));
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070070
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070071 auto li1 = std::make_unique<PDFTag>();
72 li1->fNodeId = 7;
73 li1->fType = SkPDF::DocumentStructureType::kLI;
74 l->fChildVector.push_back(std::move(li1));
75
76 auto lm2 = std::make_unique<PDFTag>();
77 lm2->fNodeId = 8;
78 lm2->fType = SkPDF::DocumentStructureType::kLbl;
79 l->fChildVector.push_back(std::move(lm2));
80 auto li2 = std::make_unique<PDFTag>();
81 li2->fNodeId = 9;
82 li2->fType = SkPDF::DocumentStructureType::kLI;
83 l->fChildVector.push_back(std::move(li2));
84
85 root->fChildVector.push_back(std::move(l));
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070086
87 // Paragraph spanning two pages.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070088 auto p2 = std::make_unique<PDFTag>();
89 p2->fNodeId = 10;
90 p2->fType = SkPDF::DocumentStructureType::kP;
91 root->fChildVector.push_back(std::move(p2));
Dominic Mazzoni656cefe2018-09-25 20:29:15 -070092
Dominic Mazzoni8f233fc2020-03-06 11:20:45 -080093 // Image with alt text.
Dominic Mazzoni8c662a72020-06-17 16:46:03 -070094 auto img = std::make_unique<PDFTag>();
95 img->fNodeId = 11;
96 img->fType = SkPDF::DocumentStructureType::kFigure;
97 img->fAlt = "Red box";
98 root->fChildVector.push_back(std::move(img));
Dominic Mazzoni8f233fc2020-03-06 11:20:45 -080099
Dominic Mazzoni8c662a72020-06-17 16:46:03 -0700100 metadata.fStructureElementTreeRoot = root.get();
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700101 sk_sp<SkDocument> document = SkPDF::MakeDocument(
102 &outputStream, metadata);
103
104 SkPaint paint;
105 paint.setColor(SK_ColorBLACK);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700106
107 // First page.
108 SkCanvas* canvas =
109 document->beginPage(pageSize.width(),
110 pageSize.height());
111 SkPDF::SetNodeId(canvas, 2);
Hal Canary3560ea72019-01-08 13:01:58 -0500112 SkFont font(nullptr, 36);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700113 const char* message = "This is the title";
114 canvas->translate(72, 72);
Hal Canary3560ea72019-01-08 13:01:58 -0500115 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700116
117 SkPDF::SetNodeId(canvas, 3);
Hal Canary3560ea72019-01-08 13:01:58 -0500118 font.setSize(14);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700119 message = "This is a simple paragraph.";
120 canvas->translate(0, 72);
Hal Canary3560ea72019-01-08 13:01:58 -0500121 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700122
123 SkPDF::SetNodeId(canvas, 6);
Hal Canary3560ea72019-01-08 13:01:58 -0500124 font.setSize(14);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700125 message = "*";
126 canvas->translate(0, 72);
Hal Canary3560ea72019-01-08 13:01:58 -0500127 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700128
129 SkPDF::SetNodeId(canvas, 7);
130 message = "List item 1";
131 canvas->translate(36, 0);
Hal Canary3560ea72019-01-08 13:01:58 -0500132 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700133
134 SkPDF::SetNodeId(canvas, 8);
135 message = "*";
136 canvas->translate(-36, 36);
Hal Canary3560ea72019-01-08 13:01:58 -0500137 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700138
139 SkPDF::SetNodeId(canvas, 9);
140 message = "List item 2";
141 canvas->translate(36, 0);
Hal Canary3560ea72019-01-08 13:01:58 -0500142 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700143
144 SkPDF::SetNodeId(canvas, 10);
145 message = "This is a paragraph that starts on one page";
146 canvas->translate(-36, 6 * 72);
Hal Canary3560ea72019-01-08 13:01:58 -0500147 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700148
149 document->endPage();
150
151 // Second page.
152 canvas = document->beginPage(pageSize.width(),
153 pageSize.height());
154 SkPDF::SetNodeId(canvas, 10);
155 message = "and finishes on the second page.";
156 canvas->translate(72, 72);
Hal Canary3560ea72019-01-08 13:01:58 -0500157 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700158
Dominic Mazzoni8f233fc2020-03-06 11:20:45 -0800159 // Test a tagged image with alt text.
160 SkPDF::SetNodeId(canvas, 11);
161 SkBitmap testBitmap;
162 testBitmap.allocN32Pixels(72, 72);
163 testBitmap.eraseColor(SK_ColorRED);
164 canvas->translate(72, 72);
165 canvas->drawBitmap(testBitmap, 0, 0);
166
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700167 // This has a node ID but never shows up in the tag tree so it
168 // won't be tagged.
169 SkPDF::SetNodeId(canvas, 999);
170 message = "Page 2";
171 canvas->translate(468, -36);
Hal Canary3560ea72019-01-08 13:01:58 -0500172 canvas->drawString(message, 0, 0, font, paint);
Dominic Mazzoni656cefe2018-09-25 20:29:15 -0700173
174 document->endPage();
175
176 document->close();
177
178 outputStream.flush();
179}