Add link annotation support to SkSVGDevice.

This CL implements |SkSVGDevice::drawAnnotation|, overridden from
|SKBaseDevice|.  |drawAnnotation| supports annotating rectangular areas
of a Skia device.  Previous to this change, annotations are being used
in |SkPDFDevice| to include hyperlinked rectangular areas in .pdf
documents.  This CL implements the SVG equivalent of this PDF feature.

BUG=skia:7581

Docs-Preview: https://skia.org/?cl=104680
Change-Id: I92ae01ceb7ae10cd2010bebab2a58dcfe48ef253
Reviewed-on: https://skia-review.googlesource.com/104680
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Bryce Thomas 2018-02-06 14:53:05 -08:00 committed by Skia Commit-Bot
parent 5dd202dc90
commit fd5a5081d5
5 changed files with 74 additions and 3 deletions

View File

@ -12,6 +12,7 @@
# Please keep the list sorted alphabetically.
ACCESS CO., LTD. <*@access-company.com>
Amazon, Inc <*@amazon.com>
Anthony Catel <paraboul@gmail.com>
ARM <*@arm.com>
Ehsan Akhgari <ehsan.akhgari@gmail.com>

View File

@ -16,5 +16,6 @@ Device backends for Skia currently include:
* OpenGL
* PDF
* XPS
* SVG
* Picture (for recording and then playing back into another Canvas)

View File

@ -7,9 +7,11 @@
#include "SkSVGDevice.h"
#include "SkAnnotationKeys.h"
#include "SkBase64.h"
#include "SkBitmap.h"
#include "SkChecksum.h"
#include "SkClipOpPriv.h"
#include "SkClipStack.h"
#include "SkData.h"
#include "SkDraw.h"
@ -22,7 +24,6 @@
#include "SkTypeface.h"
#include "SkUtils.h"
#include "SkXMLWriter.h"
#include "SkClipOpPriv.h"
namespace {
@ -621,6 +622,32 @@ void SkSVGDevice::drawPaint(const SkPaint& paint) {
SkIntToScalar(this->height())));
}
void SkSVGDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
if (!value) {
return;
}
if (!strcmp(SkAnnotationKeys::URL_Key(), key) ||
!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
this->cs().save();
this->cs().clipRect(rect, this->ctm(), kIntersect_SkClipOp, true);
SkRect transformedRect = this->cs().bounds(this->getGlobalBounds());
this->cs().restore();
if (transformedRect.isEmpty()) {
return;
}
SkString url(static_cast<const char*>(value->data()), value->size() - 1);
AutoElement a("a", fWriter);
a.addAttribute("xlink:href", url.c_str());
{
AutoElement r("rect", fWriter);
r.addAttribute("fill-opacity", "0.0");
r.addRectAttributes(transformedRect);
}
}
}
void SkSVGDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
const SkPoint pts[], const SkPaint& paint) {
SkPath path;

View File

@ -19,6 +19,7 @@ public:
protected:
void drawPaint(const SkPaint& paint) override;
void drawAnnotation(const SkRect& rect, const char key[], SkData* value) override;
void drawPoints(SkCanvas::PointMode mode, size_t count,
const SkPoint[], const SkPaint& paint) override;
void drawRect(const SkRect& r, const SkPaint& paint) override;

View File

@ -8,7 +8,9 @@
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDocument.h"
#include "SkSVGCanvas.h"
#include "SkStream.h"
#include "SkXMLWriter.h"
#include "Test.h"
/** Returns true if data (may contain null characters) contains needle (null
@ -57,8 +59,8 @@ DEF_TEST(Annotation_PdfLink, reporter) {
REPORTER_ASSERT(reporter, ContainsString(rawOutput, out->size(), "/Annots "));
}
DEF_TEST(Annotation_NamedDestination, reporter) {
REQUIRE_PDF_DOCUMENT(Annotation_NamedDestination, reporter);
DEF_TEST(Annotation_PdfDefineNamedDestination, reporter) {
REQUIRE_PDF_DOCUMENT(Annotation_PdfNamedDestination, reporter);
SkDynamicMemoryWStream outStream;
sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
SkCanvas* canvas = doc->beginPage(612.0f, 792.0f);
@ -75,3 +77,42 @@ DEF_TEST(Annotation_NamedDestination, reporter) {
REPORTER_ASSERT(reporter,
ContainsString(rawOutput, out->size(), "/example "));
}
DEF_TEST(Annotation_SvgLink, reporter) {
SkDynamicMemoryWStream outStream;
std::unique_ptr<SkXMLWriter> xmlWriter(new SkXMLStreamWriter(&outStream));
SkRect bounds = SkRect::MakeIWH(400, 400);
std::unique_ptr<SkCanvas> canvas = SkSVGCanvas::Make(bounds, xmlWriter.get());
SkRect r = SkRect::MakeXYWH(SkIntToScalar(72), SkIntToScalar(72), SkIntToScalar(288),
SkIntToScalar(72));
sk_sp<SkData> data(SkData::MakeWithCString("http://www.gooogle.com"));
SkAnnotateRectWithURL(canvas.get(), r, data.get());
canvas->flush();
sk_sp<SkData> out = outStream.detachAsData();
const char* rawOutput = (const char*)out->data();
REPORTER_ASSERT(reporter,
ContainsString(rawOutput, out->size(), "a xlink:href=\"http://www.gooogle.com\""));
}
DEF_TEST(Annotation_SvgLinkNamedDestination, reporter) {
SkDynamicMemoryWStream outStream;
std::unique_ptr<SkXMLWriter> xmlWriter(new SkXMLStreamWriter(&outStream));
SkRect bounds = SkRect::MakeIWH(400, 400);
std::unique_ptr<SkCanvas> canvas = SkSVGCanvas::Make(bounds, xmlWriter.get());
SkRect r = SkRect::MakeXYWH(SkIntToScalar(72), SkIntToScalar(72), SkIntToScalar(288),
SkIntToScalar(72));
sk_sp<SkData> data(SkData::MakeWithCString("http://www.gooogle.com/#NamedDestination"));
SkAnnotateLinkToDestination(canvas.get(), r, data.get());
canvas->flush();
sk_sp<SkData> out = outStream.detachAsData();
const char* rawOutput = (const char*)out->data();
REPORTER_ASSERT(reporter,
ContainsString(rawOutput, out->size(),
"a xlink:href=\"http://www.gooogle.com/#NamedDestination\""));
}