PDF: add support for named destinations.

Landing patchset 7 from https://codereview.appspot.com/7374052 for dml@google.com
Review URL: https://codereview.chromium.org/12533009

git-svn-id: http://skia.googlecode.com/svn/trunk@8034 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index eae7ab7..be1bca5 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -645,6 +645,7 @@
     fShaderResources.unrefAll();
     SkSafeUnref(fAnnotations);
     SkSafeUnref(fResourceDict);
+    fNamedDestinations.deleteAll();
 
     if (clearFontUsage) {
         fFontGlyphUsage->reset();
@@ -703,6 +704,10 @@
         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 @@
         return;
     }
 
-    if (handleAnnotations(r, *d.fMatrix, paint)) {
+    if (handleRectAnnotation(r, *d.fMatrix, paint)) {
         return;
     }
 
@@ -847,7 +852,7 @@
         return;
     }
 
-    if (handleAnnotations(pathPtr->getBounds(), *d.fMatrix, paint)) {
+    if (handleRectAnnotation(pathPtr->getBounds(), *d.fMatrix, paint)) {
         return;
     }
 
@@ -1286,19 +1291,43 @@
     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 @@
     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 @@
     rect->appendScalar(translatedRect.fBottom);
     annotation->insert("Rect", rect.get());
 
-    SkAutoTUnref<SkPDFDict> action(new SkPDFDict("Action"));
-    action->insertName("S", "URI");
-    action->insert("URI", new SkPDFString(url))->unref();
-    annotation->insert("A", action.get());
+    return annotation;
+}
 
-    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(SkPDFName, (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->appendScalar(dest->point.x());
+        pdfDest->appendScalar(dest->point.y());
+        pdfDest->appendInt(0);  // Leave zoom unchanged
+        dict->insert(static_cast<const char *>(dest->nameData->data()), pdfDest);
+    }
 }
 
 SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {