aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-06 00:05:13 +0000
committerGravatar epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-06 00:05:13 +0000
commit1cad898916c9e4a21429b5325c834a51fa6f361e (patch)
tree721346bc48447def4cf0601331f3209fa586e3be
parentee38c08b0769fbe06887b50469dc97b8ebc6d868 (diff)
PDF: add support for named destinations
Imported from https://codereview.appspot.com/7374052/ on behalf of dml@google.com Review URL: https://codereview.chromium.org/12466008 git-svn-id: http://skia.googlecode.com/svn/trunk@8000 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gm/internal_links.cpp77
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/core/SkAnnotation.h39
-rw-r--r--include/pdf/SkPDFDevice.h22
-rw-r--r--src/core/SkAnnotation.cpp42
-rw-r--r--src/core/SkDevice.cpp1
-rw-r--r--src/gpu/SkGpuDevice.cpp1
-rw-r--r--src/pdf/SkPDFDevice.cpp111
-rw-r--r--src/pdf/SkPDFDocument.cpp7
-rw-r--r--src/pdf/SkPDFPage.cpp4
-rw-r--r--src/pdf/SkPDFPage.h5
-rw-r--r--src/pipe/SkGPipePriv.h3
-rw-r--r--src/pipe/SkGPipeRead.cpp3
-rw-r--r--src/pipe/SkGPipeWrite.cpp2
-rw-r--r--src/ports/SkGlobalInitialization_default.cpp6
-rw-r--r--tests/AnnotationTest.cpp53
16 files changed, 338 insertions, 39 deletions
diff --git a/gm/internal_links.cpp b/gm/internal_links.cpp
new file mode 100644
index 0000000000..f9846e4c67
--- /dev/null
+++ b/gm/internal_links.cpp
@@ -0,0 +1,77 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+
+#include "SkAnnotation.h"
+#include "SkData.h"
+
+namespace skiagm {
+
+/** Draws two rectangles. In output formats that support internal links (PDF),
+ * clicking the one labeled "Link to A" should take you to the one labeled
+ * "Target A". Note that you'll need to zoom your PDF viewer in a fair bit in
+ * order for the scrolling to not be blocked by the edge of the document.
+ */
+class InternalLinksGM : public GM {
+public:
+ InternalLinksGM() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ virtual SkString onShortName() {
+ return SkString("internal_links");
+ }
+
+ virtual SkISize onISize() {
+ return make_isize(700, 500);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ SkAutoTUnref<SkData> name(SkData::NewWithCString("target-a"));
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
+ drawLabeledRect(canvas, "Link to A", 0, 0);
+ SkRect rect = SkRect::MakeXYWH(0, 0, SkIntToScalar(50), SkIntToScalar(20));
+ SkAnnotateLinkToDestination(canvas, rect, name.get());
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(200), SkIntToScalar(200));
+ SkPoint point = SkPoint::Make(SkIntToScalar(100), SkIntToScalar(50));
+ drawLabeledRect(canvas, "Target A", point.x(), point.y());
+ SkAnnotateNamedDestination(canvas, point, name.get());
+ canvas->restore();
+ }
+
+private:
+ /** Draw an arbitrary rectangle at a given location and label it with some
+ * text. */
+ void drawLabeledRect(SkCanvas* canvas, const char* text, int x, int y) {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ SkRect rect = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(50), SkIntToScalar(20));
+ canvas->drawRect(rect, paint);
+
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(25));
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawText(text, strlen(text), x, y, paint);
+ }
+
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return SkNEW(InternalLinksGM); }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 0b42eed07a..ae36de09b4 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -50,6 +50,7 @@
'../gm/image.cpp',
'../gm/imagefiltersbase.cpp',
'../gm/imagefiltersgraph.cpp',
+ '../gm/internal_links.cpp',
'../gm/lcdtext.cpp',
'../gm/linepaths.cpp',
'../gm/matrixconvolution.cpp',
diff --git a/include/core/SkAnnotation.h b/include/core/SkAnnotation.h
index e1ecf6caf1..bb7f6c443e 100644
--- a/include/core/SkAnnotation.h
+++ b/include/core/SkAnnotation.h
@@ -12,6 +12,7 @@
class SkData;
class SkDataSet;
+class SkPoint;
class SkStream;
class SkWStream;
@@ -64,6 +65,18 @@ public:
* Returns the canonical key whose payload is a URL
*/
static const char* URL_Key();
+
+ /**
+ * Returns the canonical key whose payload is the name of a destination to
+ * be defined.
+ */
+ static const char* Define_Named_Dest_Key();
+
+ /**
+ * Returns the canonical key whose payload is the name of a destination to
+ * be linked to.
+ */
+ static const char* Link_Named_Dest_Key();
};
///////////////////////////////////////////////////////////////////////////////
@@ -86,4 +99,30 @@ class SkCanvas;
*/
SK_API void SkAnnotateRectWithURL(SkCanvas*, const SkRect&, SkData*);
+/**
+ * Experimental!
+ *
+ * Annotate the canvas by associating a name with the specified point.
+ *
+ * If the backend of this canvas does not support annotations, this call is
+ * safely ignored.
+ *
+ * The caller is responsible for managing its ownership of the SkData.
+ */
+SK_API void SkAnnotateNamedDestination(SkCanvas*, const SkPoint&, SkData*);
+
+/**
+ * Experimental!
+ *
+ * Annotate the canvas by making the specified rectangle link to a named
+ * destination.
+ *
+ * If the backend of this canvas does not support annotations, this call is
+ * safely ignored.
+ *
+ * The caller is responsible for managing its ownership of the SkData.
+ */
+SK_API void SkAnnotateLinkToDestination(SkCanvas*, const SkRect&, SkData*);
+
+
#endif
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index a13d6175c7..90379f9f6b 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -17,6 +17,7 @@
#include "SkRect.h"
#include "SkRefCnt.h"
#include "SkStream.h"
+#include "SkTDArray.h"
#include "SkTScopedPtr.h"
class SkPDFArray;
@@ -33,6 +34,7 @@ class SkPDFStream;
// Private classes.
struct ContentEntry;
struct GraphicStateEntry;
+struct NamedDestination;
/** \class SkPDFDevice
@@ -142,6 +144,12 @@ public:
*/
SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
+ /** Add our named destinations to the supplied dictionary.
+ * @param dict Dictionary to add destinations to.
+ * @param page The PDF object representing the page for this device.
+ */
+ void appendDestinations(SkPDFDict* dict, SkPDFObject* page);
+
/** Returns a copy of the media box for this device. The caller is required
* to unref() this when it is finished.
*/
@@ -191,6 +199,7 @@ private:
SkRegion fExistingClipRegion;
SkPDFArray* fAnnotations;
SkPDFDict* fResourceDict;
+ SkTDArray<NamedDestination*> fNamedDestinations;
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
SkTDArray<SkPDFObject*> fXObjectResources;
@@ -273,8 +282,17 @@ private:
*/
void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
- bool handleAnnotations(const SkRect& r, const SkMatrix& matrix,
- const SkPaint& paint);
+ bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
+ const SkPaint& paint);
+ bool handlePointAnnotation(const SkPoint* points, size_t count,
+ const SkMatrix& matrix, const SkPaint& paint);
+ SkPDFDict* createLinkAnnotation(const SkRect& r, const SkMatrix& matrix);
+ void handleLinkToURL(SkData* urlData, const SkRect& r,
+ const SkMatrix& matrix);
+ void handleLinkToNamedDest(SkData* nameData, const SkRect& r,
+ const SkMatrix& matrix);
+ void defineNamedDestination(SkData* nameData, const SkPoint& point,
+ const SkMatrix& matrix);
typedef SkDevice INHERITED;
};
diff --git a/src/core/SkAnnotation.cpp b/src/core/SkAnnotation.cpp
index 5e4363e728..52fa9b79f2 100644
--- a/src/core/SkAnnotation.cpp
+++ b/src/core/SkAnnotation.cpp
@@ -8,6 +8,7 @@
#include "SkAnnotation.h"
#include "SkDataSet.h"
#include "SkFlattenableBuffers.h"
+#include "SkPoint.h"
#include "SkStream.h"
SkAnnotation::SkAnnotation(SkDataSet* data, uint32_t flags) {
@@ -42,23 +43,50 @@ const char* SkAnnotationKeys::URL_Key() {
return "SkAnnotationKey_URL";
};
+const char* SkAnnotationKeys::Define_Named_Dest_Key() {
+ return "SkAnnotationKey_Define_Named_Dest";
+};
+
+const char* SkAnnotationKeys::Link_Named_Dest_Key() {
+ return "SkAnnotationKey_Link_Named_Dest";
+};
+
///////////////////////////////////////////////////////////////////////////////
#include "SkCanvas.h"
-void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) {
- if (NULL == value) {
- return;
- }
-
- const char* key = SkAnnotationKeys::URL_Key();
+static void annotate_paint(SkPaint& paint, const char* key, SkData* value) {
SkAutoTUnref<SkDataSet> dataset(SkNEW_ARGS(SkDataSet, (key, value)));
SkAnnotation* ann = SkNEW_ARGS(SkAnnotation, (dataset,
SkAnnotation::kNoDraw_Flag));
- SkPaint paint;
paint.setAnnotation(ann)->unref();
SkASSERT(paint.isNoDrawAnnotation());
+}
+void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) {
+ if (NULL == value) {
+ return;
+ }
+ SkPaint paint;
+ annotate_paint(paint, SkAnnotationKeys::URL_Key(), value);
+ canvas->drawRect(rect, paint);
+}
+
+void SkAnnotateNamedDestination(SkCanvas* canvas, const SkPoint& point, SkData* name) {
+ if (NULL == name) {
+ return;
+ }
+ SkPaint paint;
+ annotate_paint(paint, SkAnnotationKeys::Define_Named_Dest_Key(), name);
+ canvas->drawPoint(point.x(), point.y(), paint);
+}
+
+void SkAnnotateLinkToDestination(SkCanvas* canvas, const SkRect& rect, SkData* name) {
+ if (NULL == name) {
+ return;
+ }
+ SkPaint paint;
+ annotate_paint(paint, SkAnnotationKeys::Link_Named_Dest_Key(), name);
canvas->drawRect(rect, paint);
}
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 78ce0f635d..90d41862de 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -348,6 +348,7 @@ void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
const SkPoint pts[], const SkPaint& paint) {
+ CHECK_FOR_NODRAW_ANNOTATION(paint);
draw.drawPoints(mode, count, pts, paint);
}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 82c26912a3..77c4bd1115 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -597,6 +597,7 @@ static const GrPrimitiveType gPointMode2PrimtiveType[] = {
void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
size_t count, const SkPoint pts[], const SkPaint& paint) {
+ CHECK_FOR_NODRAW_ANNOTATION(paint);
CHECK_SHOULD_DRAW(draw, false);
SkScalar width = paint.getStrokeWidth();
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index eae7ab73f0..245299639e 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -645,6 +645,7 @@ void SkPDFDevice::cleanUp(bool clearFontUsage) {
fShaderResources.unrefAll();
SkSafeUnref(fAnnotations);
SkSafeUnref(fResourceDict);
+ fNamedDestinations.deleteAll();
if (clearFontUsage) {
fFontGlyphUsage->reset();
@@ -703,6 +704,10 @@ void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
return;
}
+ if (handlePointAnnotation(points, count, *d.fMatrix, passedPaint)) {
+ return;
+ }
+
// SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
// We only use this when there's a path effect because of the overhead
// of multiple calls to setUpContentEntry it causes.
@@ -791,7 +796,7 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
return;
}
- if (handleAnnotations(r, *d.fMatrix, paint)) {
+ if (handleRectAnnotation(r, *d.fMatrix, paint)) {
return;
}
@@ -847,7 +852,7 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
return;
}
- if (handleAnnotations(pathPtr->getBounds(), *d.fMatrix, paint)) {
+ if (handleRectAnnotation(pathPtr->getBounds(), *d.fMatrix, paint)) {
return;
}
@@ -1286,19 +1291,43 @@ SkData* SkPDFDevice::copyContentToData() const {
return data.copyToData();
}
-bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
- const SkPaint& p) {
+bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
+ const SkPaint& p) {
SkAnnotation* annotationInfo = p.getAnnotation();
if (!annotationInfo) {
return false;
}
SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key());
- if (!urlData) {
+ if (urlData) {
+ handleLinkToURL(urlData, r, matrix);
+ return p.isNoDrawAnnotation();
+ }
+ SkData* linkToName = annotationInfo->find(SkAnnotationKeys::Link_Named_Dest_Key());
+ if (linkToName) {
+ handleLinkToNamedDest(linkToName, r, matrix);
+ return p.isNoDrawAnnotation();
+ }
+ return false;
+}
+
+bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count,
+ const SkMatrix& matrix,
+ const SkPaint& paint) {
+ SkAnnotation* annotationInfo = paint.getAnnotation();
+ if (!annotationInfo) {
return false;
}
+ SkData* nameData = annotationInfo->find(SkAnnotationKeys::Define_Named_Dest_Key());
+ if (nameData) {
+ for (size_t i = 0; i < count; i++) {
+ defineNamedDestination(nameData, points[i], matrix);
+ }
+ return paint.isNoDrawAnnotation();
+ }
+ return false;
+}
- SkString url(static_cast<const char *>(urlData->data()),
- urlData->size() - 1);
+SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r, const SkMatrix& matrix) {
SkMatrix transform = matrix;
transform.postConcat(fInitialTransform);
SkRect translatedRect;
@@ -1307,18 +1336,18 @@ bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
if (NULL == fAnnotations) {
fAnnotations = SkNEW(SkPDFArray);
}
- SkAutoTUnref<SkPDFDict> annotation(new SkPDFDict("Annot"));
+ SkPDFDict* annotation(SkNEW_ARGS(SkPDFDict, ("Annot")));
annotation->insertName("Subtype", "Link");
- fAnnotations->append(annotation.get());
+ fAnnotations->append(annotation);
- SkAutoTUnref<SkPDFArray> border(new SkPDFArray);
+ SkAutoTUnref<SkPDFArray> border(SkNEW(SkPDFArray));
border->reserve(3);
border->appendInt(0); // Horizontal corner radius.
border->appendInt(0); // Vertical corner radius.
border->appendInt(0); // Width, 0 = no border.
annotation->insert("Border", border.get());
- SkAutoTUnref<SkPDFArray> rect(new SkPDFArray);
+ SkAutoTUnref<SkPDFArray> rect(SkNEW(SkPDFArray));
rect->reserve(4);
rect->appendScalar(translatedRect.fLeft);
rect->appendScalar(translatedRect.fTop);
@@ -1326,12 +1355,66 @@ bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
rect->appendScalar(translatedRect.fBottom);
annotation->insert("Rect", rect.get());
- SkAutoTUnref<SkPDFDict> action(new SkPDFDict("Action"));
+ return annotation;
+}
+
+void SkPDFDevice::handleLinkToURL(SkData* urlData, const SkRect& r,
+ const SkMatrix& matrix) {
+ SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix));
+
+ SkString url(static_cast<const char *>(urlData->data()),
+ urlData->size() - 1);
+ SkAutoTUnref<SkPDFDict> action(SkNEW_ARGS(SkPDFDict, ("Action")));
action->insertName("S", "URI");
- action->insert("URI", new SkPDFString(url))->unref();
+ action->insert("URI", SkNEW_ARGS(SkPDFString, (url)))->unref();
annotation->insert("A", action.get());
+}
+
+void SkPDFDevice::handleLinkToNamedDest(SkData* nameData, const SkRect& r,
+ const SkMatrix& matrix) {
+ SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix));
+ SkString name(static_cast<const char *>(nameData->data()),
+ nameData->size() - 1);
+ annotation->insert("Dest", SkNEW_ARGS(SkPDFString, (name)))->unref();
+}
+
+struct NamedDestination {
+ const SkData* nameData;
+ SkPoint point;
- return p.isNoDrawAnnotation();
+ NamedDestination(const SkData* nameData, const SkPoint& point)
+ : nameData(nameData), point(point) {
+ nameData->ref();
+ }
+
+ ~NamedDestination() {
+ nameData->unref();
+ }
+};
+
+void SkPDFDevice::defineNamedDestination(SkData* nameData, const SkPoint& point,
+ const SkMatrix& matrix) {
+ SkMatrix transform = matrix;
+ transform.postConcat(fInitialTransform);
+ SkPoint translatedPoint;
+ transform.mapXY(point.x(), point.y(), &translatedPoint);
+ fNamedDestinations.push(
+ SkNEW_ARGS(NamedDestination, (nameData, translatedPoint)));
+}
+
+void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) {
+ int nDest = fNamedDestinations.count();
+ for (int i = 0; i < nDest; i++) {
+ NamedDestination* dest = fNamedDestinations[i];
+ SkAutoTUnref<SkPDFArray> pdfDest(SkNEW(SkPDFArray));
+ pdfDest->reserve(5);
+ pdfDest->append(SkNEW_ARGS(SkPDFObjRef, (page)))->unref();
+ pdfDest->appendName("XYZ");
+ pdfDest->appendInt(dest->point.x());
+ pdfDest->appendInt(dest->point.y());
+ pdfDest->appendInt(0); // Leave zoom unchanged
+ dict->insert(static_cast<const char *>(dest->nameData->data()), pdfDest);
+ }
}
SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 4c66c6bb90..7ee2778326 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -107,18 +107,25 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) {
fDocCatalog->insert("OutputIntent", intentArray.get());
*/
+ SkPDFDict* dests = SkNEW(SkPDFDict); // fPageResources owns reference
+ fCatalog->addObject(dests, true /* onFirstPage */);
+ fPageResources.push(dests);
+
bool firstPage = true;
for (int i = 0; i < fPages.count(); i++) {
int resourceCount = fPageResources.count();
fPages[i]->finalizePage(fCatalog.get(), firstPage, &fPageResources);
addResourcesToCatalog(resourceCount, firstPage, &fPageResources,
fCatalog.get());
+ fPages[i]->appendDestinations(dests);
if (i == 0) {
firstPage = false;
fSecondPageFirstResourceIndex = fPageResources.count();
}
}
+ fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests)))->unref();
+
// Build font subsetting info before proceeding.
perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
index f47f8ffd46..4cdc00003b 100644
--- a/src/pdf/SkPDFPage.cpp
+++ b/src/pdf/SkPDFPage.cpp
@@ -147,3 +147,7 @@ const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
return fDevice->getFontGlyphUsage();
}
+
+void SkPDFPage::appendDestinations(SkPDFDict* dict) {
+ fDevice->appendDestinations(dict, this);
+}
diff --git a/src/pdf/SkPDFPage.h b/src/pdf/SkPDFPage.h
index 72ba335e66..285a2f565e 100644
--- a/src/pdf/SkPDFPage.h
+++ b/src/pdf/SkPDFPage.h
@@ -48,6 +48,11 @@ public:
void finalizePage(SkPDFCatalog* catalog, bool firstPage,
SkTDArray<SkPDFObject*>* resourceObjects);
+ /** Add destinations for this page to the supplied dictionary.
+ * @param dict Dictionary to add destinations to.
+ */
+ void appendDestinations(SkPDFDict* dict);
+
/** Determine the size of the page content and store to the catalog
* the offsets of all nonresource-indirect objects that make up the page
* content. This must be called before emitPage(), but after finalizePage.
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
index b563652cb9..f5f98f2bdf 100644
--- a/src/pipe/SkGPipePriv.h
+++ b/src/pipe/SkGPipePriv.h
@@ -25,8 +25,9 @@ enum PaintFlats {
kShader_PaintFlat,
kImageFilter_PaintFlat,
kXfermode_PaintFlat,
+ kAnnotation_PaintFlat,
- kLast_PaintFlat = kXfermode_PaintFlat
+ kLast_PaintFlat = kAnnotation_PaintFlat
};
#define kCount_PaintFlats (kLast_PaintFlat + 1)
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index 6cffb1b343..f47f3bfe65 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -53,6 +53,9 @@ static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat
case kXfermode_PaintFlat:
paint->setXfermode((SkXfermode*)obj);
break;
+ case kAnnotation_PaintFlat:
+ paint->setAnnotation((SkAnnotation*)obj);
+ break;
default:
SkDEBUGFAIL("never gets here");
}
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index cfd1e7ceef..34020dfe7f 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -6,6 +6,7 @@
* found in the LICENSE file.
*/
+#include "SkAnnotation.h"
#include "SkBitmapHeap.h"
#include "SkCanvas.h"
#include "SkColorFilter.h"
@@ -47,6 +48,7 @@ static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
case kShader_PaintFlat: return paint.getShader();
case kImageFilter_PaintFlat: return paint.getImageFilter();
case kXfermode_PaintFlat: return paint.getXfermode();
+ case kAnnotation_PaintFlat: return paint.getAnnotation();
}
SkDEBUGFAIL("never gets here");
return NULL;
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index 26a61ca394..2d35cefc7d 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -15,6 +15,7 @@
#include "Sk1DPathEffect.h"
#include "Sk2DPathEffect.h"
+#include "SkAnnotation.h"
#include "SkAvoidXfermode.h"
#include "SkBicubicImageFilter.h"
#include "SkBitmapSource.h"
@@ -30,6 +31,8 @@
#include "SkComposeShader.h"
#include "SkCornerPathEffect.h"
#include "SkDashPathEffect.h"
+#include "SkData.h"
+#include "SkDataSet.h"
#include "SkDiscretePathEffect.h"
#include "SkDisplacementMapEffect.h"
#include "SkEmptyShader.h"
@@ -52,6 +55,7 @@
void SkFlattenable::InitializeFlattenables() {
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAnnotation)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader)
@@ -66,6 +70,8 @@ void SkFlattenable::InitializeFlattenables() {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkData)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDataSet)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect)
diff --git a/tests/AnnotationTest.cpp b/tests/AnnotationTest.cpp
index 586525a9e8..5429b58912 100644
--- a/tests/AnnotationTest.cpp
+++ b/tests/AnnotationTest.cpp
@@ -12,6 +12,18 @@
#include "SkPDFDevice.h"
#include "SkPDFDocument.h"
+/** Returns true if data (may contain null characters) contains needle (null
+ * terminated). */
+static bool ContainsString(const char* data, size_t dataSize, const char* needle) {
+ size_t nSize = strlen(needle);
+ for (size_t i = 0; i < dataSize - nSize; i++) {
+ if (strncmp(&data[i], needle, nSize) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void test_nodraw(skiatest::Reporter* reporter) {
SkBitmap bm;
bm.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
@@ -55,27 +67,38 @@ static void test_pdf_link_annotations(skiatest::Reporter* reporter) {
SkAutoDataUnref out(outStream.copyToData());
const char* rawOutput = (const char*)out->data();
- bool found = false;
- for (size_t i = 0; i < out->size() - 8; i++) {
- if (rawOutput[i + 0] == '/' &&
- rawOutput[i + 1] == 'A' &&
- rawOutput[i + 2] == 'n' &&
- rawOutput[i + 3] == 'n' &&
- rawOutput[i + 4] == 'o' &&
- rawOutput[i + 5] == 't' &&
- rawOutput[i + 6] == 's' &&
- rawOutput[i + 7] == ' ') {
- found = true;
- break;
- }
- }
- REPORTER_ASSERT(reporter, found == tests[testNum].expectAnnotations);
+ REPORTER_ASSERT(reporter,
+ ContainsString(rawOutput, out->size(), "/Annots ")
+ == tests[testNum].expectAnnotations);
}
}
+static void test_named_destination_annotations(skiatest::Reporter* reporter) {
+ SkISize size = SkISize::Make(612, 792);
+ SkMatrix initialTransform;
+ initialTransform.reset();
+ SkPDFDevice device(size, size, initialTransform);
+ SkCanvas canvas(&device);
+
+ SkPoint p = SkPoint::Make(SkIntToScalar(72), SkIntToScalar(72));
+ SkAutoDataUnref data(SkData::NewWithCString("example"));
+ SkAnnotateNamedDestination(&canvas, p, data.get());
+
+ SkPDFDocument doc;
+ doc.appendPage(&device);
+ SkDynamicMemoryWStream outStream;
+ doc.emitPDF(&outStream);
+ SkAutoDataUnref out(outStream.copyToData());
+ const char* rawOutput = (const char*)out->data();
+
+ REPORTER_ASSERT(reporter,
+ ContainsString(rawOutput, out->size(), "/example "));
+}
+
static void TestAnnotation(skiatest::Reporter* reporter) {
test_nodraw(reporter);
test_pdf_link_annotations(reporter);
+ test_named_destination_annotations(reporter);
}
#include "TestClassDef.h"