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
This commit is contained in:
parent
ee38c08b07
commit
1cad898916
77
gm/internal_links.cpp
Normal file
77
gm/internal_links.cpp
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -50,6 +50,7 @@
|
|||||||
'../gm/image.cpp',
|
'../gm/image.cpp',
|
||||||
'../gm/imagefiltersbase.cpp',
|
'../gm/imagefiltersbase.cpp',
|
||||||
'../gm/imagefiltersgraph.cpp',
|
'../gm/imagefiltersgraph.cpp',
|
||||||
|
'../gm/internal_links.cpp',
|
||||||
'../gm/lcdtext.cpp',
|
'../gm/lcdtext.cpp',
|
||||||
'../gm/linepaths.cpp',
|
'../gm/linepaths.cpp',
|
||||||
'../gm/matrixconvolution.cpp',
|
'../gm/matrixconvolution.cpp',
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
class SkData;
|
class SkData;
|
||||||
class SkDataSet;
|
class SkDataSet;
|
||||||
|
class SkPoint;
|
||||||
class SkStream;
|
class SkStream;
|
||||||
class SkWStream;
|
class SkWStream;
|
||||||
|
|
||||||
@ -64,6 +65,18 @@ public:
|
|||||||
* Returns the canonical key whose payload is a URL
|
* Returns the canonical key whose payload is a URL
|
||||||
*/
|
*/
|
||||||
static const char* URL_Key();
|
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*);
|
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
|
#endif
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "SkRect.h"
|
#include "SkRect.h"
|
||||||
#include "SkRefCnt.h"
|
#include "SkRefCnt.h"
|
||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
|
#include "SkTDArray.h"
|
||||||
#include "SkTScopedPtr.h"
|
#include "SkTScopedPtr.h"
|
||||||
|
|
||||||
class SkPDFArray;
|
class SkPDFArray;
|
||||||
@ -33,6 +34,7 @@ class SkPDFStream;
|
|||||||
// Private classes.
|
// Private classes.
|
||||||
struct ContentEntry;
|
struct ContentEntry;
|
||||||
struct GraphicStateEntry;
|
struct GraphicStateEntry;
|
||||||
|
struct NamedDestination;
|
||||||
|
|
||||||
/** \class SkPDFDevice
|
/** \class SkPDFDevice
|
||||||
|
|
||||||
@ -142,6 +144,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
|
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
|
/** Returns a copy of the media box for this device. The caller is required
|
||||||
* to unref() this when it is finished.
|
* to unref() this when it is finished.
|
||||||
*/
|
*/
|
||||||
@ -191,6 +199,7 @@ private:
|
|||||||
SkRegion fExistingClipRegion;
|
SkRegion fExistingClipRegion;
|
||||||
SkPDFArray* fAnnotations;
|
SkPDFArray* fAnnotations;
|
||||||
SkPDFDict* fResourceDict;
|
SkPDFDict* fResourceDict;
|
||||||
|
SkTDArray<NamedDestination*> fNamedDestinations;
|
||||||
|
|
||||||
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
|
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
|
||||||
SkTDArray<SkPDFObject*> fXObjectResources;
|
SkTDArray<SkPDFObject*> fXObjectResources;
|
||||||
@ -273,8 +282,17 @@ private:
|
|||||||
*/
|
*/
|
||||||
void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
|
void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
|
||||||
|
|
||||||
bool handleAnnotations(const SkRect& r, const SkMatrix& matrix,
|
bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
||||||
const SkPaint& paint);
|
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;
|
typedef SkDevice INHERITED;
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "SkAnnotation.h"
|
#include "SkAnnotation.h"
|
||||||
#include "SkDataSet.h"
|
#include "SkDataSet.h"
|
||||||
#include "SkFlattenableBuffers.h"
|
#include "SkFlattenableBuffers.h"
|
||||||
|
#include "SkPoint.h"
|
||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
|
|
||||||
SkAnnotation::SkAnnotation(SkDataSet* data, uint32_t flags) {
|
SkAnnotation::SkAnnotation(SkDataSet* data, uint32_t flags) {
|
||||||
@ -42,23 +43,50 @@ const char* SkAnnotationKeys::URL_Key() {
|
|||||||
return "SkAnnotationKey_URL";
|
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"
|
#include "SkCanvas.h"
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
paint.setAnnotation(ann)->unref();
|
||||||
|
SkASSERT(paint.isNoDrawAnnotation());
|
||||||
|
}
|
||||||
|
|
||||||
void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) {
|
void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) {
|
||||||
if (NULL == value) {
|
if (NULL == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* key = SkAnnotationKeys::URL_Key();
|
|
||||||
SkAutoTUnref<SkDataSet> dataset(SkNEW_ARGS(SkDataSet, (key, value)));
|
|
||||||
SkAnnotation* ann = SkNEW_ARGS(SkAnnotation, (dataset,
|
|
||||||
SkAnnotation::kNoDraw_Flag));
|
|
||||||
|
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
paint.setAnnotation(ann)->unref();
|
annotate_paint(paint, SkAnnotationKeys::URL_Key(), value);
|
||||||
SkASSERT(paint.isNoDrawAnnotation());
|
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);
|
canvas->drawRect(rect, paint);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
|
||||||
const SkPoint pts[], const SkPaint& paint) {
|
const SkPoint pts[], const SkPaint& paint) {
|
||||||
|
CHECK_FOR_NODRAW_ANNOTATION(paint);
|
||||||
draw.drawPoints(mode, count, pts, paint);
|
draw.drawPoints(mode, count, pts, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,6 +597,7 @@ static const GrPrimitiveType gPointMode2PrimtiveType[] = {
|
|||||||
|
|
||||||
void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
|
void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
|
||||||
size_t count, const SkPoint pts[], const SkPaint& paint) {
|
size_t count, const SkPoint pts[], const SkPaint& paint) {
|
||||||
|
CHECK_FOR_NODRAW_ANNOTATION(paint);
|
||||||
CHECK_SHOULD_DRAW(draw, false);
|
CHECK_SHOULD_DRAW(draw, false);
|
||||||
|
|
||||||
SkScalar width = paint.getStrokeWidth();
|
SkScalar width = paint.getStrokeWidth();
|
||||||
|
@ -645,6 +645,7 @@ void SkPDFDevice::cleanUp(bool clearFontUsage) {
|
|||||||
fShaderResources.unrefAll();
|
fShaderResources.unrefAll();
|
||||||
SkSafeUnref(fAnnotations);
|
SkSafeUnref(fAnnotations);
|
||||||
SkSafeUnref(fResourceDict);
|
SkSafeUnref(fResourceDict);
|
||||||
|
fNamedDestinations.deleteAll();
|
||||||
|
|
||||||
if (clearFontUsage) {
|
if (clearFontUsage) {
|
||||||
fFontGlyphUsage->reset();
|
fFontGlyphUsage->reset();
|
||||||
@ -703,6 +704,10 @@ void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (handlePointAnnotation(points, count, *d.fMatrix, passedPaint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
|
// SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
|
||||||
// We only use this when there's a path effect because of the overhead
|
// We only use this when there's a path effect because of the overhead
|
||||||
// of multiple calls to setUpContentEntry it causes.
|
// of multiple calls to setUpContentEntry it causes.
|
||||||
@ -791,7 +796,7 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleAnnotations(r, *d.fMatrix, paint)) {
|
if (handleRectAnnotation(r, *d.fMatrix, paint)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,7 +852,7 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleAnnotations(pathPtr->getBounds(), *d.fMatrix, paint)) {
|
if (handleRectAnnotation(pathPtr->getBounds(), *d.fMatrix, paint)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1286,19 +1291,43 @@ SkData* SkPDFDevice::copyContentToData() const {
|
|||||||
return data.copyToData();
|
return data.copyToData();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
|
bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
||||||
const SkPaint& p) {
|
const SkPaint& p) {
|
||||||
SkAnnotation* annotationInfo = p.getAnnotation();
|
SkAnnotation* annotationInfo = p.getAnnotation();
|
||||||
if (!annotationInfo) {
|
if (!annotationInfo) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key());
|
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;
|
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()),
|
SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r, const SkMatrix& matrix) {
|
||||||
urlData->size() - 1);
|
|
||||||
SkMatrix transform = matrix;
|
SkMatrix transform = matrix;
|
||||||
transform.postConcat(fInitialTransform);
|
transform.postConcat(fInitialTransform);
|
||||||
SkRect translatedRect;
|
SkRect translatedRect;
|
||||||
@ -1307,18 +1336,18 @@ bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
|
|||||||
if (NULL == fAnnotations) {
|
if (NULL == fAnnotations) {
|
||||||
fAnnotations = SkNEW(SkPDFArray);
|
fAnnotations = SkNEW(SkPDFArray);
|
||||||
}
|
}
|
||||||
SkAutoTUnref<SkPDFDict> annotation(new SkPDFDict("Annot"));
|
SkPDFDict* annotation(SkNEW_ARGS(SkPDFDict, ("Annot")));
|
||||||
annotation->insertName("Subtype", "Link");
|
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->reserve(3);
|
||||||
border->appendInt(0); // Horizontal corner radius.
|
border->appendInt(0); // Horizontal corner radius.
|
||||||
border->appendInt(0); // Vertical corner radius.
|
border->appendInt(0); // Vertical corner radius.
|
||||||
border->appendInt(0); // Width, 0 = no border.
|
border->appendInt(0); // Width, 0 = no border.
|
||||||
annotation->insert("Border", border.get());
|
annotation->insert("Border", border.get());
|
||||||
|
|
||||||
SkAutoTUnref<SkPDFArray> rect(new SkPDFArray);
|
SkAutoTUnref<SkPDFArray> rect(SkNEW(SkPDFArray));
|
||||||
rect->reserve(4);
|
rect->reserve(4);
|
||||||
rect->appendScalar(translatedRect.fLeft);
|
rect->appendScalar(translatedRect.fLeft);
|
||||||
rect->appendScalar(translatedRect.fTop);
|
rect->appendScalar(translatedRect.fTop);
|
||||||
@ -1326,12 +1355,66 @@ bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
|
|||||||
rect->appendScalar(translatedRect.fBottom);
|
rect->appendScalar(translatedRect.fBottom);
|
||||||
annotation->insert("Rect", rect.get());
|
annotation->insert("Rect", rect.get());
|
||||||
|
|
||||||
SkAutoTUnref<SkPDFDict> action(new SkPDFDict("Action"));
|
return annotation;
|
||||||
action->insertName("S", "URI");
|
}
|
||||||
action->insert("URI", new SkPDFString(url))->unref();
|
|
||||||
annotation->insert("A", action.get());
|
|
||||||
|
|
||||||
return p.isNoDrawAnnotation();
|
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", 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;
|
||||||
|
|
||||||
|
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() {
|
SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
|
||||||
|
@ -107,18 +107,25 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) {
|
|||||||
fDocCatalog->insert("OutputIntent", intentArray.get());
|
fDocCatalog->insert("OutputIntent", intentArray.get());
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
SkPDFDict* dests = SkNEW(SkPDFDict); // fPageResources owns reference
|
||||||
|
fCatalog->addObject(dests, true /* onFirstPage */);
|
||||||
|
fPageResources.push(dests);
|
||||||
|
|
||||||
bool firstPage = true;
|
bool firstPage = true;
|
||||||
for (int i = 0; i < fPages.count(); i++) {
|
for (int i = 0; i < fPages.count(); i++) {
|
||||||
int resourceCount = fPageResources.count();
|
int resourceCount = fPageResources.count();
|
||||||
fPages[i]->finalizePage(fCatalog.get(), firstPage, &fPageResources);
|
fPages[i]->finalizePage(fCatalog.get(), firstPage, &fPageResources);
|
||||||
addResourcesToCatalog(resourceCount, firstPage, &fPageResources,
|
addResourcesToCatalog(resourceCount, firstPage, &fPageResources,
|
||||||
fCatalog.get());
|
fCatalog.get());
|
||||||
|
fPages[i]->appendDestinations(dests);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
firstPage = false;
|
firstPage = false;
|
||||||
fSecondPageFirstResourceIndex = fPageResources.count();
|
fSecondPageFirstResourceIndex = fPageResources.count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (dests)))->unref();
|
||||||
|
|
||||||
// Build font subsetting info before proceeding.
|
// Build font subsetting info before proceeding.
|
||||||
perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
|
perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
|
||||||
|
|
||||||
|
@ -147,3 +147,7 @@ const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
|
|||||||
const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
|
const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
|
||||||
return fDevice->getFontGlyphUsage();
|
return fDevice->getFontGlyphUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkPDFPage::appendDestinations(SkPDFDict* dict) {
|
||||||
|
fDevice->appendDestinations(dict, this);
|
||||||
|
}
|
||||||
|
@ -48,6 +48,11 @@ public:
|
|||||||
void finalizePage(SkPDFCatalog* catalog, bool firstPage,
|
void finalizePage(SkPDFCatalog* catalog, bool firstPage,
|
||||||
SkTDArray<SkPDFObject*>* resourceObjects);
|
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
|
/** Determine the size of the page content and store to the catalog
|
||||||
* the offsets of all nonresource-indirect objects that make up the page
|
* the offsets of all nonresource-indirect objects that make up the page
|
||||||
* content. This must be called before emitPage(), but after finalizePage.
|
* content. This must be called before emitPage(), but after finalizePage.
|
||||||
|
@ -25,8 +25,9 @@ enum PaintFlats {
|
|||||||
kShader_PaintFlat,
|
kShader_PaintFlat,
|
||||||
kImageFilter_PaintFlat,
|
kImageFilter_PaintFlat,
|
||||||
kXfermode_PaintFlat,
|
kXfermode_PaintFlat,
|
||||||
|
kAnnotation_PaintFlat,
|
||||||
|
|
||||||
kLast_PaintFlat = kXfermode_PaintFlat
|
kLast_PaintFlat = kAnnotation_PaintFlat
|
||||||
};
|
};
|
||||||
#define kCount_PaintFlats (kLast_PaintFlat + 1)
|
#define kCount_PaintFlats (kLast_PaintFlat + 1)
|
||||||
|
|
||||||
|
@ -53,6 +53,9 @@ static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat
|
|||||||
case kXfermode_PaintFlat:
|
case kXfermode_PaintFlat:
|
||||||
paint->setXfermode((SkXfermode*)obj);
|
paint->setXfermode((SkXfermode*)obj);
|
||||||
break;
|
break;
|
||||||
|
case kAnnotation_PaintFlat:
|
||||||
|
paint->setAnnotation((SkAnnotation*)obj);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
SkDEBUGFAIL("never gets here");
|
SkDEBUGFAIL("never gets here");
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "SkAnnotation.h"
|
||||||
#include "SkBitmapHeap.h"
|
#include "SkBitmapHeap.h"
|
||||||
#include "SkCanvas.h"
|
#include "SkCanvas.h"
|
||||||
#include "SkColorFilter.h"
|
#include "SkColorFilter.h"
|
||||||
@ -47,6 +48,7 @@ static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
|
|||||||
case kShader_PaintFlat: return paint.getShader();
|
case kShader_PaintFlat: return paint.getShader();
|
||||||
case kImageFilter_PaintFlat: return paint.getImageFilter();
|
case kImageFilter_PaintFlat: return paint.getImageFilter();
|
||||||
case kXfermode_PaintFlat: return paint.getXfermode();
|
case kXfermode_PaintFlat: return paint.getXfermode();
|
||||||
|
case kAnnotation_PaintFlat: return paint.getAnnotation();
|
||||||
}
|
}
|
||||||
SkDEBUGFAIL("never gets here");
|
SkDEBUGFAIL("never gets here");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "Sk1DPathEffect.h"
|
#include "Sk1DPathEffect.h"
|
||||||
#include "Sk2DPathEffect.h"
|
#include "Sk2DPathEffect.h"
|
||||||
|
#include "SkAnnotation.h"
|
||||||
#include "SkAvoidXfermode.h"
|
#include "SkAvoidXfermode.h"
|
||||||
#include "SkBicubicImageFilter.h"
|
#include "SkBicubicImageFilter.h"
|
||||||
#include "SkBitmapSource.h"
|
#include "SkBitmapSource.h"
|
||||||
@ -30,6 +31,8 @@
|
|||||||
#include "SkComposeShader.h"
|
#include "SkComposeShader.h"
|
||||||
#include "SkCornerPathEffect.h"
|
#include "SkCornerPathEffect.h"
|
||||||
#include "SkDashPathEffect.h"
|
#include "SkDashPathEffect.h"
|
||||||
|
#include "SkData.h"
|
||||||
|
#include "SkDataSet.h"
|
||||||
#include "SkDiscretePathEffect.h"
|
#include "SkDiscretePathEffect.h"
|
||||||
#include "SkDisplacementMapEffect.h"
|
#include "SkDisplacementMapEffect.h"
|
||||||
#include "SkEmptyShader.h"
|
#include "SkEmptyShader.h"
|
||||||
@ -52,6 +55,7 @@
|
|||||||
|
|
||||||
void SkFlattenable::InitializeFlattenables() {
|
void SkFlattenable::InitializeFlattenables() {
|
||||||
|
|
||||||
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAnnotation)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader)
|
||||||
@ -66,6 +70,8 @@ void SkFlattenable::InitializeFlattenables() {
|
|||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
|
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(SkDilateImageFilter)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
|
||||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect)
|
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect)
|
||||||
|
@ -12,6 +12,18 @@
|
|||||||
#include "SkPDFDevice.h"
|
#include "SkPDFDevice.h"
|
||||||
#include "SkPDFDocument.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) {
|
static void test_nodraw(skiatest::Reporter* reporter) {
|
||||||
SkBitmap bm;
|
SkBitmap bm;
|
||||||
bm.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
|
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());
|
SkAutoDataUnref out(outStream.copyToData());
|
||||||
const char* rawOutput = (const char*)out->data();
|
const char* rawOutput = (const char*)out->data();
|
||||||
|
|
||||||
bool found = false;
|
REPORTER_ASSERT(reporter,
|
||||||
for (size_t i = 0; i < out->size() - 8; i++) {
|
ContainsString(rawOutput, out->size(), "/Annots ")
|
||||||
if (rawOutput[i + 0] == '/' &&
|
== tests[testNum].expectAnnotations);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
static void TestAnnotation(skiatest::Reporter* reporter) {
|
||||||
test_nodraw(reporter);
|
test_nodraw(reporter);
|
||||||
test_pdf_link_annotations(reporter);
|
test_pdf_link_annotations(reporter);
|
||||||
|
test_named_destination_annotations(reporter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "TestClassDef.h"
|
#include "TestClassDef.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user