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