• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
7 #include "include/core/SkBitmap.h"
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkColor.h"
10 #include "include/core/SkDocument.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkImage.h" // IWYU pragma: keep
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkStream.h"
17 #include "include/core/SkString.h"
18 #include "include/core/SkTime.h"
19 #include "include/core/SkTypeface.h"
20 #include "include/docs/SkPDFDocument.h"
21 #include "tests/Test.h"
22 
23 #include <memory>
24 #include <utility>
25 #include <vector>
26 
27 using PDFTag = SkPDF::StructureElementNode;
28 
29 // Test building a tagged PDF.
30 // Add this to args.gn to output the PDF to a file:
31 //   extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/foo.pdf\"" ]
DEF_TEST(SkPDF_tagged_doc,r)32 DEF_TEST(SkPDF_tagged_doc, r) {
33     REQUIRE_PDF_DOCUMENT(SkPDF_tagged_doc, r);
34 #ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH
35     SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH);
36 #else
37     SkDynamicMemoryWStream outputStream;
38 #endif
39 
40     SkSize pageSize = SkSize::Make(612, 792);  // U.S. Letter
41 
42     SkPDF::Metadata metadata;
43     metadata.fTitle = "Example Tagged PDF";
44     metadata.fCreator = "Skia";
45     SkTime::DateTime now;
46     SkTime::GetDateTime(&now);
47     metadata.fCreation = now;
48     metadata.fModified = now;
49 
50     // The document tag.
51     auto root = std::make_unique<PDFTag>();
52     root->fNodeId = 1;
53     root->fTypeString = "Document";
54 
55     // Heading.
56     auto h1 = std::make_unique<PDFTag>();
57     h1->fNodeId = 2;
58     h1->fTypeString = "H1";
59     root->fChildVector.push_back(std::move(h1));
60 
61     // Initial paragraph.
62     auto p = std::make_unique<PDFTag>();
63     p->fNodeId = 3;
64     p->fTypeString = "P";
65     root->fChildVector.push_back(std::move(p));
66 
67     // Hidden div. This is never referenced by marked content
68     // so it should not appear in the resulting PDF.
69     auto div = std::make_unique<PDFTag>();
70     div->fNodeId = 4;
71     div->fTypeString = "Div";
72     root->fChildVector.push_back(std::move(div));
73 
74     // A bulleted list of two items.
75     auto l = std::make_unique<PDFTag>();
76     l->fNodeId = 5;
77     l->fTypeString = "L";
78 
79     auto lm1 = std::make_unique<PDFTag>();
80     lm1->fNodeId = 6;
81     lm1->fTypeString = "Lbl";
82     l->fChildVector.push_back(std::move(lm1));
83 
84     auto li1 = std::make_unique<PDFTag>();
85     li1->fNodeId = 7;
86     li1->fTypeString = "LI";
87     l->fChildVector.push_back(std::move(li1));
88 
89     auto lm2 = std::make_unique<PDFTag>();
90     lm2->fNodeId = 8;
91     lm2->fTypeString = "Lbl";
92     l->fChildVector.push_back(std::move(lm2));
93     auto li2 = std::make_unique<PDFTag>();
94     li2->fNodeId = 9;
95     li2->fTypeString = "LI";
96     l->fChildVector.push_back(std::move(li2));
97 
98     root->fChildVector.push_back(std::move(l));
99 
100     // Paragraph spanning two pages.
101     auto p2 = std::make_unique<PDFTag>();
102     p2->fNodeId = 10;
103     p2->fTypeString = "P";
104     root->fChildVector.push_back(std::move(p2));
105 
106     // Image with alt text.
107     auto img = std::make_unique<PDFTag>();
108     img->fNodeId = 11;
109     img->fTypeString = "Figure";
110     img->fAlt = "Red box";
111     root->fChildVector.push_back(std::move(img));
112 
113     metadata.fStructureElementTreeRoot = root.get();
114     sk_sp<SkDocument> document = SkPDF::MakeDocument(
115         &outputStream, metadata);
116 
117     SkPaint paint;
118     paint.setColor(SK_ColorBLACK);
119 
120     // First page.
121     SkCanvas* canvas =
122             document->beginPage(pageSize.width(),
123                                 pageSize.height());
124     SkPDF::SetNodeId(canvas, 2);
125     SkFont font(nullptr, 36);
126     const char* message = "This is the title";
127     canvas->translate(72, 72);
128     canvas->drawString(message, 0, 0, font, paint);
129 
130     SkPDF::SetNodeId(canvas, 3);
131     font.setSize(14);
132     message = "This is a simple paragraph.";
133     canvas->translate(0, 72);
134     canvas->drawString(message, 0, 0, font, paint);
135 
136     SkPDF::SetNodeId(canvas, 6);
137     font.setSize(14);
138     message = "*";
139     canvas->translate(0, 72);
140     canvas->drawString(message, 0, 0, font, paint);
141 
142     SkPDF::SetNodeId(canvas, 7);
143     message = "List item 1";
144     canvas->translate(36, 0);
145     canvas->drawString(message, 0, 0, font, paint);
146 
147     SkPDF::SetNodeId(canvas, 8);
148     message = "*";
149     canvas->translate(-36, 36);
150     canvas->drawString(message, 0, 0, font, paint);
151 
152     SkPDF::SetNodeId(canvas, 9);
153     message = "List item 2";
154     canvas->translate(36, 0);
155     canvas->drawString(message, 0, 0, font, paint);
156 
157     SkPDF::SetNodeId(canvas, 10);
158     message = "This is a paragraph that starts on one page";
159     canvas->translate(-36, 6 * 72);
160     canvas->drawString(message, 0, 0, font, paint);
161 
162     document->endPage();
163 
164     // Second page.
165     canvas = document->beginPage(pageSize.width(),
166                                  pageSize.height());
167     SkPDF::SetNodeId(canvas, 10);
168     message = "and finishes on the second page.";
169     canvas->translate(72, 72);
170     canvas->drawString(message, 0, 0, font, paint);
171 
172     // Test a tagged image with alt text.
173     SkPDF::SetNodeId(canvas, 11);
174     SkBitmap testBitmap;
175     testBitmap.allocN32Pixels(72, 72);
176     testBitmap.eraseColor(SK_ColorRED);
177     canvas->translate(72, 72);
178     canvas->drawImage(testBitmap.asImage(), 0, 0);
179 
180     // This has a node ID but never shows up in the tag tree so it
181     // won't be tagged.
182     SkPDF::SetNodeId(canvas, 999);
183     message = "Page 2";
184     canvas->translate(468, -36);
185     canvas->drawString(message, 0, 0, font, paint);
186 
187     document->endPage();
188 
189     document->close();
190 
191     outputStream.flush();
192 }
193