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