skia2/tests/PDFTaggedTest.cpp
Dominic Mazzoni 8f233fc795 Add marked-content ID to PDF images
Previously we only added marked-content IDs to text commands.
Extend this to support marking bitmaps drawn to a PDF document
too, so that images can be tagged with alt text.

Moves the code that outputs the marked content ID and closing
tag into a scoped helper class that outputs the closing sequence
on destruction.

Note: we'll probably want to do this to nearly all drawing
commands, since nearly everything could in theory be tagged.

This change is safe because it's still behind a flag in
Chromium and if the document doesn't have any structure
tree, it won't output anything.

Bug: chromium:607777
Change-Id: I4d7d655857fa7e76c6a6914a2a83097be917d835
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/275517
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
2020-03-06 19:48:50 +00:00

183 lines
5.1 KiB
C++

/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/Test.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkStream.h"
#include "include/docs/SkPDFDocument.h"
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\"" ]
DEF_TEST(SkPDF_tagged_doc, r) {
REQUIRE_PDF_DOCUMENT(SkPDF_tagged_doc, r);
#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.
PDFTag root;
root.fNodeId = 1;
root.fType = SkPDF::DocumentStructureType::kDocument;
root.fChildCount = 6;
PDFTag rootChildren[6];
root.fChildren = rootChildren;
// Heading.
PDFTag& h1 = rootChildren[0];
h1.fNodeId = 2;
h1.fType = SkPDF::DocumentStructureType::kH1;
h1.fChildCount = 0;
// Initial paragraph.
PDFTag& p = rootChildren[1];
p.fNodeId = 3;
p.fType = SkPDF::DocumentStructureType::kP;
p.fChildCount = 0;
// Hidden div. This is never referenced by marked content
// so it should not appear in the resulting PDF.
PDFTag& div = rootChildren[2];
div.fNodeId = 4;
div.fType = SkPDF::DocumentStructureType::kDiv;
div.fChildCount = 0;
// A bulleted list of two items.
PDFTag& l = rootChildren[3];
l.fNodeId = 5;
l.fType = SkPDF::DocumentStructureType::kL;
l.fChildCount = 4;
PDFTag listChildren[4];
l.fChildren = listChildren;
PDFTag& lm1 = listChildren[0];
lm1.fNodeId = 6;
lm1.fType = SkPDF::DocumentStructureType::kLbl;
lm1.fChildCount = 0;
PDFTag& li1 = listChildren[1];
li1.fNodeId = 7;
li1.fType = SkPDF::DocumentStructureType::kLI;
li1.fChildCount = 0;
PDFTag& lm2 = listChildren[2];
lm2.fNodeId = 8;
lm2.fType = SkPDF::DocumentStructureType::kLbl;
lm2.fChildCount = 0;
PDFTag& li2 = listChildren[3];
li2.fNodeId = 9;
li2.fType = SkPDF::DocumentStructureType::kLI;
li2.fChildCount = 0;
// Paragraph spanning two pages.
PDFTag& p2 = rootChildren[4];
p2.fNodeId = 10;
p2.fType = SkPDF::DocumentStructureType::kP;
p2.fChildCount = 0;
// Image with alt text.
PDFTag& img = rootChildren[5];
img.fNodeId = 11;
img.fType = SkPDF::DocumentStructureType::kFigure;
img.fAlt = "Red box";
img.fChildCount = 0;
metadata.fStructureElementTreeRoot = &root;
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);
SkFont font(nullptr, 36);
const char* message = "This is the title";
canvas->translate(72, 72);
canvas->drawString(message, 0, 0, font, paint);
SkPDF::SetNodeId(canvas, 3);
font.setSize(14);
message = "This is a simple paragraph.";
canvas->translate(0, 72);
canvas->drawString(message, 0, 0, font, paint);
SkPDF::SetNodeId(canvas, 6);
font.setSize(14);
message = "*";
canvas->translate(0, 72);
canvas->drawString(message, 0, 0, font, paint);
SkPDF::SetNodeId(canvas, 7);
message = "List item 1";
canvas->translate(36, 0);
canvas->drawString(message, 0, 0, font, paint);
SkPDF::SetNodeId(canvas, 8);
message = "*";
canvas->translate(-36, 36);
canvas->drawString(message, 0, 0, font, paint);
SkPDF::SetNodeId(canvas, 9);
message = "List item 2";
canvas->translate(36, 0);
canvas->drawString(message, 0, 0, font, paint);
SkPDF::SetNodeId(canvas, 10);
message = "This is a paragraph that starts on one page";
canvas->translate(-36, 6 * 72);
canvas->drawString(message, 0, 0, font, paint);
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);
canvas->drawString(message, 0, 0, font, paint);
// 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);
canvas->drawBitmap(testBitmap, 0, 0);
// 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);
canvas->drawString(message, 0, 0, font, paint);
document->endPage();
document->close();
outputStream.flush();
}