diff options
author | epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-03-06 00:05:13 +0000 |
---|---|---|
committer | epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-03-06 00:05:13 +0000 |
commit | 1cad898916c9e4a21429b5325c834a51fa6f361e (patch) | |
tree | 721346bc48447def4cf0601331f3209fa586e3be | |
parent | ee38c08b0769fbe06887b50469dc97b8ebc6d868 (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.cpp | 77 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/core/SkAnnotation.h | 39 | ||||
-rw-r--r-- | include/pdf/SkPDFDevice.h | 22 | ||||
-rw-r--r-- | src/core/SkAnnotation.cpp | 42 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 1 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 1 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 111 | ||||
-rw-r--r-- | src/pdf/SkPDFDocument.cpp | 7 | ||||
-rw-r--r-- | src/pdf/SkPDFPage.cpp | 4 | ||||
-rw-r--r-- | src/pdf/SkPDFPage.h | 5 | ||||
-rw-r--r-- | src/pipe/SkGPipePriv.h | 3 | ||||
-rw-r--r-- | src/pipe/SkGPipeRead.cpp | 3 | ||||
-rw-r--r-- | src/pipe/SkGPipeWrite.cpp | 2 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 6 | ||||
-rw-r--r-- | tests/AnnotationTest.cpp | 53 |
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" |