2018-09-26 03:29:15 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2018 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "tests/Test.h"
|
2018-09-26 03:29:15 +00:00
|
|
|
|
2020-12-23 15:11:33 +00:00
|
|
|
#include "include/core/SkBitmap.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkFont.h"
|
|
|
|
#include "include/core/SkStream.h"
|
|
|
|
#include "include/docs/SkPDFDocument.h"
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
using PDFTag = SkPDF::StructureElementNode;
|
|
|
|
|
|
|
|
// Test building a tagged PDF.
|
|
|
|
// Add this to args.gn to output the PDF to a file:
|
|
|
|
// extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/foo.pdf\"" ]
|
2020-03-06 19:20:45 +00:00
|
|
|
DEF_TEST(SkPDF_tagged_doc, r) {
|
|
|
|
REQUIRE_PDF_DOCUMENT(SkPDF_tagged_doc, r);
|
2018-09-26 03:29:15 +00:00
|
|
|
#ifdef SK_PDF_TEST_TAGS_OUTPUT_PATH
|
|
|
|
SkFILEWStream outputStream(SK_PDF_TEST_TAGS_OUTPUT_PATH);
|
|
|
|
#else
|
|
|
|
SkDynamicMemoryWStream outputStream;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SkSize pageSize = SkSize::Make(612, 792); // U.S. Letter
|
|
|
|
|
|
|
|
SkPDF::Metadata metadata;
|
|
|
|
metadata.fTitle = "Example Tagged PDF";
|
|
|
|
metadata.fCreator = "Skia";
|
|
|
|
SkTime::DateTime now;
|
|
|
|
SkTime::GetDateTime(&now);
|
|
|
|
metadata.fCreation = now;
|
|
|
|
metadata.fModified = now;
|
|
|
|
|
|
|
|
// The document tag.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto root = std::make_unique<PDFTag>();
|
|
|
|
root->fNodeId = 1;
|
2020-09-11 06:00:45 +00:00
|
|
|
root->fTypeString = "Document";
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
// Heading.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto h1 = std::make_unique<PDFTag>();
|
|
|
|
h1->fNodeId = 2;
|
2020-09-11 06:00:45 +00:00
|
|
|
h1->fTypeString = "H1";
|
2020-06-17 23:46:03 +00:00
|
|
|
root->fChildVector.push_back(std::move(h1));
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
// Initial paragraph.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto p = std::make_unique<PDFTag>();
|
|
|
|
p->fNodeId = 3;
|
2020-09-11 06:00:45 +00:00
|
|
|
p->fTypeString = "P";
|
2020-06-17 23:46:03 +00:00
|
|
|
root->fChildVector.push_back(std::move(p));
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
// Hidden div. This is never referenced by marked content
|
|
|
|
// so it should not appear in the resulting PDF.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto div = std::make_unique<PDFTag>();
|
|
|
|
div->fNodeId = 4;
|
2020-09-11 06:00:45 +00:00
|
|
|
div->fTypeString = "Div";
|
2020-06-17 23:46:03 +00:00
|
|
|
root->fChildVector.push_back(std::move(div));
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
// A bulleted list of two items.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto l = std::make_unique<PDFTag>();
|
|
|
|
l->fNodeId = 5;
|
2020-09-11 06:00:45 +00:00
|
|
|
l->fTypeString = "L";
|
2020-06-17 23:46:03 +00:00
|
|
|
|
|
|
|
auto lm1 = std::make_unique<PDFTag>();
|
|
|
|
lm1->fNodeId = 6;
|
2020-09-11 06:00:45 +00:00
|
|
|
lm1->fTypeString = "Lbl";
|
2020-06-17 23:46:03 +00:00
|
|
|
l->fChildVector.push_back(std::move(lm1));
|
|
|
|
|
|
|
|
auto li1 = std::make_unique<PDFTag>();
|
|
|
|
li1->fNodeId = 7;
|
2020-09-11 06:00:45 +00:00
|
|
|
li1->fTypeString = "LI";
|
2020-06-17 23:46:03 +00:00
|
|
|
l->fChildVector.push_back(std::move(li1));
|
|
|
|
|
|
|
|
auto lm2 = std::make_unique<PDFTag>();
|
|
|
|
lm2->fNodeId = 8;
|
2020-09-11 06:00:45 +00:00
|
|
|
lm2->fTypeString = "Lbl";
|
2020-06-17 23:46:03 +00:00
|
|
|
l->fChildVector.push_back(std::move(lm2));
|
|
|
|
auto li2 = std::make_unique<PDFTag>();
|
|
|
|
li2->fNodeId = 9;
|
2020-09-11 06:00:45 +00:00
|
|
|
li2->fTypeString = "LI";
|
2020-06-17 23:46:03 +00:00
|
|
|
l->fChildVector.push_back(std::move(li2));
|
|
|
|
|
|
|
|
root->fChildVector.push_back(std::move(l));
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
// Paragraph spanning two pages.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto p2 = std::make_unique<PDFTag>();
|
|
|
|
p2->fNodeId = 10;
|
2020-09-11 06:00:45 +00:00
|
|
|
p2->fTypeString = "P";
|
2020-06-17 23:46:03 +00:00
|
|
|
root->fChildVector.push_back(std::move(p2));
|
2018-09-26 03:29:15 +00:00
|
|
|
|
2020-03-06 19:20:45 +00:00
|
|
|
// Image with alt text.
|
2020-06-17 23:46:03 +00:00
|
|
|
auto img = std::make_unique<PDFTag>();
|
|
|
|
img->fNodeId = 11;
|
2020-09-11 06:00:45 +00:00
|
|
|
img->fTypeString = "Figure";
|
2020-06-17 23:46:03 +00:00
|
|
|
img->fAlt = "Red box";
|
|
|
|
root->fChildVector.push_back(std::move(img));
|
2020-03-06 19:20:45 +00:00
|
|
|
|
2020-06-17 23:46:03 +00:00
|
|
|
metadata.fStructureElementTreeRoot = root.get();
|
2018-09-26 03:29:15 +00:00
|
|
|
sk_sp<SkDocument> document = SkPDF::MakeDocument(
|
|
|
|
&outputStream, metadata);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLACK);
|
|
|
|
|
|
|
|
// First page.
|
|
|
|
SkCanvas* canvas =
|
|
|
|
document->beginPage(pageSize.width(),
|
|
|
|
pageSize.height());
|
|
|
|
SkPDF::SetNodeId(canvas, 2);
|
2019-01-08 18:01:58 +00:00
|
|
|
SkFont font(nullptr, 36);
|
2018-09-26 03:29:15 +00:00
|
|
|
const char* message = "This is the title";
|
|
|
|
canvas->translate(72, 72);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
SkPDF::SetNodeId(canvas, 3);
|
2019-01-08 18:01:58 +00:00
|
|
|
font.setSize(14);
|
2018-09-26 03:29:15 +00:00
|
|
|
message = "This is a simple paragraph.";
|
|
|
|
canvas->translate(0, 72);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
SkPDF::SetNodeId(canvas, 6);
|
2019-01-08 18:01:58 +00:00
|
|
|
font.setSize(14);
|
2018-09-26 03:29:15 +00:00
|
|
|
message = "*";
|
|
|
|
canvas->translate(0, 72);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
SkPDF::SetNodeId(canvas, 7);
|
|
|
|
message = "List item 1";
|
|
|
|
canvas->translate(36, 0);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
SkPDF::SetNodeId(canvas, 8);
|
|
|
|
message = "*";
|
|
|
|
canvas->translate(-36, 36);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
SkPDF::SetNodeId(canvas, 9);
|
|
|
|
message = "List item 2";
|
|
|
|
canvas->translate(36, 0);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
SkPDF::SetNodeId(canvas, 10);
|
|
|
|
message = "This is a paragraph that starts on one page";
|
|
|
|
canvas->translate(-36, 6 * 72);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
document->endPage();
|
|
|
|
|
|
|
|
// Second page.
|
|
|
|
canvas = document->beginPage(pageSize.width(),
|
|
|
|
pageSize.height());
|
|
|
|
SkPDF::SetNodeId(canvas, 10);
|
|
|
|
message = "and finishes on the second page.";
|
|
|
|
canvas->translate(72, 72);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
2020-03-06 19:20:45 +00:00
|
|
|
// Test a tagged image with alt text.
|
|
|
|
SkPDF::SetNodeId(canvas, 11);
|
|
|
|
SkBitmap testBitmap;
|
|
|
|
testBitmap.allocN32Pixels(72, 72);
|
|
|
|
testBitmap.eraseColor(SK_ColorRED);
|
|
|
|
canvas->translate(72, 72);
|
2021-01-25 00:49:21 +00:00
|
|
|
canvas->drawImage(testBitmap.asImage(), 0, 0);
|
2020-03-06 19:20:45 +00:00
|
|
|
|
2018-09-26 03:29:15 +00:00
|
|
|
// This has a node ID but never shows up in the tag tree so it
|
|
|
|
// won't be tagged.
|
|
|
|
SkPDF::SetNodeId(canvas, 999);
|
|
|
|
message = "Page 2";
|
|
|
|
canvas->translate(468, -36);
|
2019-01-08 18:01:58 +00:00
|
|
|
canvas->drawString(message, 0, 0, font, paint);
|
2018-09-26 03:29:15 +00:00
|
|
|
|
|
|
|
document->endPage();
|
|
|
|
|
|
|
|
document->close();
|
|
|
|
|
|
|
|
outputStream.flush();
|
|
|
|
}
|