Allow passing multiple node IDs per PDF structure node.
At the time Chromium is painting, we're passing node IDs
along with painting commands to enable tagging. However,
this assumes that all nodes will end up in the structure
tree, which we might not want.
Instead, allow the client to prune the structure tree
later before telling Skia to generate the PDF, but
keep all of the node IDs to be matched up with.
As an example, suppose the doc looks like this:
root id=1
paragraph id=2
div id=3
text1 id=4
link id=5
text2 id=6
The pruned tree passed to Skia would look like this:
root id=1
paragraph id=2 extra_ids=3,4
link id=5 extra_ids=6
We need to pass the extra node IDs into Skia so
that when content is tagged with id=4, we know to
map that to the paragraph node with id=2 instead.
Note that the resulting PDF document will *not*
have any of these extra IDs, they're all remapped
and consolidated.
While it's not strictly necessary that this is done
in Skia, it's easiest to implement it here. Doing the
same upstream would require replaying an SkPicture
and rewriting all of the node IDs.
Bug: chromium:607777
Change-Id: I0ecb62651e60b84cc5b9d053d7f7d3b9efda1470
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/272462
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
2020-02-24 16:45:39 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2020 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"
|
|
|
|
|
|
|
|
#ifdef SK_SUPPORT_PDF
|
|
|
|
|
|
|
|
#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 where nodes are pruned.
|
|
|
|
// Add this to args.gn to output the PDF to a file:
|
|
|
|
// extra_cflags = [ "-DSK_PDF_TEST_TAGS_OUTPUT_PATH=\"/tmp/pruning.pdf\"" ]
|
|
|
|
DEF_TEST(SkPDF_tagged_pruning, r) {
|
|
|
|
REQUIRE_PDF_DOCUMENT(SkPDF_tagged, 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.
|
|
|
|
auto root = std::make_unique<PDFTag>();
|
|
|
|
root->fNodeId = 1;
|
|
|
|
root->fTypeString = "Document";
|
|
|
|
root->fLang = "en-US";
|
|
|
|
|
|
|
|
// First paragraph.
|
|
|
|
auto p1 = std::make_unique<PDFTag>();
|
|
|
|
p1->fNodeId = 2;
|
|
|
|
p1->fAdditionalNodeIds = {3, 4};
|
|
|
|
p1->fTypeString = "P";
|
|
|
|
root->fChildVector.push_back(std::move(p1));
|
|
|
|
|
|
|
|
// Second paragraph.
|
|
|
|
auto p2 = std::make_unique<PDFTag>();
|
|
|
|
p2->fNodeId = 5;
|
|
|
|
p2->fAdditionalNodeIds = {6, 7};
|
|
|
|
p2->fTypeString = "P";
|
|
|
|
root->fChildVector.push_back(std::move(p2));
|
|
|
|
|
|
|
|
metadata.fStructureElementTreeRoot = root.get();
|
|
|
|
sk_sp<SkDocument> document = SkPDF::MakeDocument(
|
|
|
|
&outputStream, metadata);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLACK);
|
|
|
|
|
|
|
|
SkCanvas* canvas =
|
|
|
|
document->beginPage(pageSize.width(),
|
|
|
|
pageSize.height());
|
|
|
|
SkFont font(nullptr, 20);
|
|
|
|
SkPDF::SetNodeId(canvas, 3);
|
|
|
|
canvas->drawString("First paragraph line 1", 72, 72, font, paint);
|
|
|
|
SkPDF::SetNodeId(canvas, 4);
|
|
|
|
canvas->drawString("First paragraph line 2", 72, 108, font, paint);
|
|
|
|
SkPDF::SetNodeId(canvas, 6);
|
|
|
|
canvas->drawString("Second paragraph line 1", 72, 180, font, paint);
|
|
|
|
SkPDF::SetNodeId(canvas, 7);
|
|
|
|
canvas->drawString("Second paragraph line 2", 72, 216, font, paint);
|
|
|
|
|
|
|
|
document->endPage();
|
|
|
|
document->close();
|
|
|
|
outputStream.flush();
|
|
|
|
}
|
|
|
|
|
2020-07-08 21:57:45 +00:00
|
|
|
// Similar to SkPDF_tagged_pruning but never actually writes out anything annotated.
|
|
|
|
// Ensures that nothing goes wring when there are no annotations on an annotated PDF.
|
|
|
|
DEF_TEST(SkPDF_tagged_pruning_empty, r) {
|
|
|
|
REQUIRE_PDF_DOCUMENT(SkPDF_tagged, 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.
|
|
|
|
auto root = std::make_unique<PDFTag>();
|
|
|
|
root->fNodeId = 1;
|
|
|
|
root->fTypeString = "Document";
|
|
|
|
root->fLang = "en-US";
|
|
|
|
|
|
|
|
// First paragraph.
|
|
|
|
auto p1 = std::make_unique<PDFTag>();
|
|
|
|
p1->fNodeId = 2;
|
|
|
|
p1->fAdditionalNodeIds = {3, 4};
|
|
|
|
p1->fTypeString = "P";
|
|
|
|
root->fChildVector.push_back(std::move(p1));
|
|
|
|
|
|
|
|
// Second paragraph.
|
|
|
|
auto p2 = std::make_unique<PDFTag>();
|
|
|
|
p2->fNodeId = 5;
|
|
|
|
p2->fAdditionalNodeIds = {6, 7};
|
|
|
|
p2->fTypeString = "P";
|
|
|
|
root->fChildVector.push_back(std::move(p2));
|
|
|
|
|
|
|
|
metadata.fStructureElementTreeRoot = root.get();
|
|
|
|
sk_sp<SkDocument> document = SkPDF::MakeDocument(
|
|
|
|
&outputStream, metadata);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLACK);
|
|
|
|
|
|
|
|
SkCanvas* canvas =
|
|
|
|
document->beginPage(pageSize.width(),
|
|
|
|
pageSize.height());
|
|
|
|
|
|
|
|
canvas->drawRect(SkRect::MakeXYWH(10, 10, 100, 100), paint);
|
|
|
|
|
|
|
|
document->endPage();
|
|
|
|
document->close();
|
|
|
|
outputStream.flush();
|
|
|
|
}
|
|
|
|
|
Allow passing multiple node IDs per PDF structure node.
At the time Chromium is painting, we're passing node IDs
along with painting commands to enable tagging. However,
this assumes that all nodes will end up in the structure
tree, which we might not want.
Instead, allow the client to prune the structure tree
later before telling Skia to generate the PDF, but
keep all of the node IDs to be matched up with.
As an example, suppose the doc looks like this:
root id=1
paragraph id=2
div id=3
text1 id=4
link id=5
text2 id=6
The pruned tree passed to Skia would look like this:
root id=1
paragraph id=2 extra_ids=3,4
link id=5 extra_ids=6
We need to pass the extra node IDs into Skia so
that when content is tagged with id=4, we know to
map that to the paragraph node with id=2 instead.
Note that the resulting PDF document will *not*
have any of these extra IDs, they're all remapped
and consolidated.
While it's not strictly necessary that this is done
in Skia, it's easiest to implement it here. Doing the
same upstream would require replaying an SkPicture
and rewriting all of the node IDs.
Bug: chromium:607777
Change-Id: I0ecb62651e60b84cc5b9d053d7f7d3b9efda1470
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/272462
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
2020-02-24 16:45:39 +00:00
|
|
|
#endif
|