Merge sub-device annotations in SkPDFDevice::drawDevice()

Previously annotations added between saveLayer/restore were lost.

Merge annotations in SkPDFDevice::drawDevice(). Also modified code to
apply correct transformation and clipping on annotations added between
saveLayer/restore:
- Apply the initial transform only when adding the annotations into the
  doc, otherwise we need to unapply sub-device's initial transform
  before merging the annotations into parent-device.
- Apply only device-local clipping. fClipStack is in global coordinates,
  which is not suitable to clip rects in sub-devices.

BUG=skia:4080
BUG=503515

Review URL: https://codereview.chromium.org/1257533004
diff --git a/gm/annotated_text.cpp b/gm/annotated_text.cpp
index 7126206..30f67b0 100644
--- a/gm/annotated_text.cpp
+++ b/gm/annotated_text.cpp
@@ -34,7 +34,8 @@
     const char text[] = "Click this link!";
     const char url[] = "https://www.google.com/";
     draw_url_annotated_text_with_box(canvas, text, 200.0f, 80.0f, p, url);
-    SkAutoCanvasRestore autoCanvasRestore2(canvas, true);
+    canvas->saveLayer(nullptr, nullptr);
     canvas->rotate(90);
     draw_url_annotated_text_with_box(canvas, text, 150.0f, -55.0f, p, url);
+    canvas->restore();
 }
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp
index 6c47379..4ea9d89 100644
--- a/src/doc/SkDocument_PDF.cpp
+++ b/src/doc/SkDocument_PDF.cpp
@@ -70,9 +70,10 @@
     SkAutoTUnref<SkPDFDict> page(new SkPDFDict("Page"));
     page->insertObject("Resources", pageDevice->createResourceDict());
     page->insertObject("MediaBox", pageDevice->copyMediaBox());
-    if (SkPDFArray* annots = pageDevice->getAnnotations()) {
-        SkASSERT(annots->size() > 0);
-        page->insertObject("Annots", SkRef(annots));
+    SkAutoTUnref<SkPDFArray> annotations(new SkPDFArray);
+    pageDevice->appendAnnotations(annotations);
+    if (annotations->size() > 0) {
+        page->insertObject("Annots", annotations.detach());
     }
     page->insertObjRef("Contents", create_pdf_page_content(pageDevice));
     return page.detach();
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index a5c9557..52c4c65 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -25,6 +25,7 @@
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
 #include "SkPDFUtils.h"
+#include "SkRasterClip.h"
 #include "SkRect.h"
 #include "SkRRect.h"
 #include "SkString.h"
@@ -702,7 +703,6 @@
     , fPageSize(pageSize)
     , fContentSize(pageSize)
     , fExistingClipRegion(SkIRect::MakeSize(pageSize))
-    , fAnnotations(nullptr)
     , fLastContentEntry(nullptr)
     , fLastMarginContentEntry(nullptr)
     , fDrawingArea(kContent_DrawingArea)
@@ -731,7 +731,6 @@
 }
 
 void SkPDFDevice::init() {
-    fAnnotations = nullptr;
     fContentEntries.free();
     fLastContentEntry = nullptr;
     fMarginContentEntries.free();
@@ -747,7 +746,8 @@
     fXObjectResources.unrefAll();
     fFontResources.unrefAll();
     fShaderResources.unrefAll();
-    SkSafeUnref(fAnnotations);
+    fLinkToURLs.deleteAll();
+    fLinkToDestinations.deleteAll();
     fNamedDestinations.deleteAll();
 
     if (clearFontUsage) {
@@ -876,22 +876,6 @@
     }
 }
 
-static SkPath transform_and_clip_path(const SkDraw& d,
-                                      const SkPath& region,
-                                      const SkMatrix& initialTransform) {
-    SkPath path = region;
-    SkMatrix transform = *d.fMatrix;
-    transform.postConcat(initialTransform);
-    path.transform(transform);
-    if (const SkClipStack* clipStack = d.fClipStack) {
-        SkPath clip;
-        (void)clipStack->asPath(&clip);
-        clip.transform(initialTransform);
-        Op(clip, path, SkPathOp::kIntersect_SkPathOp, &path);
-    }
-    return path;
-}
-
 static SkPDFDict* create_link_annotation(const SkRect& translatedRect) {
     SkAutoTUnref<SkPDFDict> annotation(new SkPDFDict("Annot"));
     annotation->insertName("Subtype", "Link");
@@ -914,7 +898,7 @@
     return annotation.detach();
 }
 
-static SkPDFDict* create_link_to_url(SkData* urlData, const SkRect& r) {
+static SkPDFDict* create_link_to_url(const SkData* urlData, const SkRect& r) {
     SkAutoTUnref<SkPDFDict> annotation(create_link_annotation(r));
 
     SkString url(static_cast<const char *>(urlData->data()),
@@ -926,7 +910,8 @@
     return annotation.detach();
 }
 
-static SkPDFDict* create_link_named_dest(SkData* nameData, const SkRect& r) {
+static SkPDFDict* create_link_named_dest(const SkData* nameData,
+                                         const SkRect& r) {
     SkAutoTUnref<SkPDFDict> annotation(create_link_annotation(r));
     SkString name(static_cast<const char *>(nameData->data()),
                   nameData->size() - 1);
@@ -934,21 +919,6 @@
     return annotation.detach();
 }
 
-static SkPDFDict* create_rect_annotation(const SkRect& r,
-                                         SkAnnotation* annotation) {
-    SkASSERT(annotation);
-    SkData* urlData = annotation->find(SkAnnotationKeys::URL_Key());
-    if (urlData) {
-        return create_link_to_url(urlData, r);
-    }
-    SkData* linkToName =
-            annotation->find(SkAnnotationKeys::Link_Named_Dest_Key());
-    if (linkToName) {
-        return create_link_named_dest(linkToName, r);
-    }
-    return nullptr;
-}
-
 void SkPDFDevice::drawRect(const SkDraw& d,
                            const SkRect& rect,
                            const SkPaint& srcPaint) {
@@ -970,12 +940,7 @@
     if (SkAnnotation* annotation = paint.getAnnotation()) {
         SkPath path;
         path.addRect(rect);
-        SkRect transformedRect =
-                transform_and_clip_path(d, path, fInitialTransform).getBounds();
-        SkAutoTUnref<SkPDFDict> annot(
-                create_rect_annotation(transformedRect, annotation));
-        if (annot) {
-            this->addAnnotation(annot.detach());
+        if (handlePathAnnotation(path, d, annotation)) {
             return;
         }
     }
@@ -1059,13 +1024,7 @@
     }
 
     if (SkAnnotation* annotation = paint.getAnnotation()) {
-        SkRect transformedRect =
-                transform_and_clip_path(d, *pathPtr, fInitialTransform)
-                        .getBounds();
-        SkAutoTUnref<SkPDFDict> annot(
-                create_rect_annotation(transformedRect, annotation));
-        if (annot) {
-            this->addAnnotation(annot.detach());
+        if (handlePathAnnotation(*pathPtr, d, annotation)) {
             return;
         }
     }
@@ -1327,10 +1286,42 @@
     // TODO: implement drawVertices
 }
 
+struct RectWithData {
+    SkRect rect;
+    SkAutoTUnref<const SkData> data;
+
+    RectWithData(const SkRect& rect, const SkData* data)
+        : rect(rect), data(SkRef(data)) {}
+};
+
+struct NamedDestination {
+    SkAutoTUnref<const SkData> nameData;
+    SkPoint point;
+
+    NamedDestination(const SkData* nameData, const SkPoint& point)
+        : nameData(SkRef(nameData)), point(point) {}
+};
+
 void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device,
                              int x, int y, const SkPaint& paint) {
     // our onCreateCompatibleDevice() always creates SkPDFDevice subclasses.
     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
+
+    SkScalar scalarX = SkIntToScalar(x);
+    SkScalar scalarY = SkIntToScalar(y);
+    for (RectWithData* link : pdfDevice->fLinkToURLs) {
+        fLinkToURLs.push(new RectWithData(
+                link->rect.makeOffset(scalarX, scalarY), link->data));
+    }
+    for (RectWithData* link : pdfDevice->fLinkToDestinations) {
+        fLinkToDestinations.push(new RectWithData(
+                link->rect.makeOffset(scalarX, scalarY), link->data));
+    }
+    for (NamedDestination* d : pdfDevice->fNamedDestinations) {
+        fNamedDestinations.push(new NamedDestination(
+                d->nameData, d->point + SkPoint::Make(scalarX, scalarY)));
+    }
+
     if (pdfDevice->isContentEmpty()) {
         return;
     }
@@ -1557,50 +1548,76 @@
     return true;
 }
 
-struct NamedDestination {
-    SkAutoTUnref<const SkData> nameData;
-    SkPoint point;
-
-    NamedDestination(const SkData* nameData, const SkPoint& point)
-        : nameData(SkRef(nameData)), point(point) {}
-};
-
 bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count,
                                         const SkMatrix& matrix,
                                         SkAnnotation* annotationInfo) {
     SkData* nameData = annotationInfo->find(
             SkAnnotationKeys::Define_Named_Dest_Key());
     if (nameData) {
-        SkMatrix transform = matrix;
-        transform.postConcat(fInitialTransform);
         for (size_t i = 0; i < count; i++) {
-            SkPoint translatedPoint;
-            transform.mapXY(points[i].x(), points[i].y(), &translatedPoint);
-            fNamedDestinations.push(new NamedDestination(nameData, translatedPoint));
+            SkPoint transformedPoint;
+            matrix.mapXY(points[i].x(), points[i].y(), &transformedPoint);
+            fNamedDestinations.push(new NamedDestination(nameData, transformedPoint));
         }
         return true;
     }
     return false;
 }
 
-void SkPDFDevice::addAnnotation(SkPDFDict* annotation) {
-    if (nullptr == fAnnotations) {
-        fAnnotations = new SkPDFArray;
+bool SkPDFDevice::handlePathAnnotation(const SkPath& path,
+                                       const SkDraw& d,
+                                       SkAnnotation* annotation) {
+    SkASSERT(annotation);
+
+    SkPath transformedPath = path;
+    transformedPath.transform(*d.fMatrix);
+    SkRasterClip clip = *d.fRC;
+    clip.op(transformedPath, SkISize::Make(width(), height()), SkRegion::kIntersect_Op, false);
+    SkRect transformedRect = SkRect::Make(clip.getBounds());
+
+    SkData* urlData = annotation->find(SkAnnotationKeys::URL_Key());
+    if (urlData) {
+        if (!transformedRect.isEmpty()) {
+            fLinkToURLs.push(new RectWithData(transformedRect, urlData));
+        }
+        return true;
     }
-    fAnnotations->appendObject(annotation);
+
+    SkData* linkToDestination =
+            annotation->find(SkAnnotationKeys::Link_Named_Dest_Key());
+    if (linkToDestination) {
+        if (!transformedRect.isEmpty()) {
+            fLinkToDestinations.push(new RectWithData(transformedRect, linkToDestination));
+        }
+        return true;
+    }
+
+    return false;
 }
 
+void SkPDFDevice::appendAnnotations(SkPDFArray* array) const {
+    array->reserve(fLinkToURLs.count() + fLinkToDestinations.count());
+    for (RectWithData* rectWithURL : fLinkToURLs) {
+        SkRect r;
+        fInitialTransform.mapRect(&r, rectWithURL->rect);
+        array->appendObject(create_link_to_url(rectWithURL->data, r));
+    }
+    for (RectWithData* linkToDestination : fLinkToDestinations) {
+        SkRect r;
+        fInitialTransform.mapRect(&r, linkToDestination->rect);
+        array->appendObject(create_link_named_dest(linkToDestination->data, r));
+    }
+}
 
 void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
-    int nDest = fNamedDestinations.count();
-    for (int i = 0; i < nDest; i++) {
-        NamedDestination* dest = fNamedDestinations[i];
+    for (NamedDestination* dest : fNamedDestinations) {
         SkAutoTUnref<SkPDFArray> pdfDest(new SkPDFArray);
         pdfDest->reserve(5);
         pdfDest->appendObjRef(SkRef(page));
         pdfDest->appendName("XYZ");
-        pdfDest->appendScalar(dest->point.x());
-        pdfDest->appendScalar(dest->point.y());
+        SkPoint p = fInitialTransform.mapXY(dest->point.x(), dest->point.y());
+        pdfDest->appendScalar(p.x());
+        pdfDest->appendScalar(p.y());
         pdfDest->appendInt(0);  // Leave zoom unchanged
         SkString name(static_cast<const char*>(dest->nameData->data()));
         dict->insertObject(name, pdfDest.detach());
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index fbd5fb3..45aba29 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -38,6 +38,7 @@
 struct ContentEntry;
 struct GraphicStateEntry;
 struct NamedDestination;
+struct RectWithData;
 
 /** \class SkPDFDevice
 
@@ -141,6 +142,12 @@
      */
     const SkTDArray<SkPDFFont*>& getFontResources() const;
 
+    /** Add our annotations (link to urls and destinations) to the supplied
+     *  array.
+     *  @param array Array to add annotations to.
+     */
+    void appendAnnotations(SkPDFArray* array) 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.
@@ -152,10 +159,6 @@
      */
     SkPDFArray* copyMediaBox() const;
 
-    /** Get the annotations from this page, or nullptr if there are none.
-     */
-    SkPDFArray* getAnnotations() const { return fAnnotations; }
-
     /** Returns a SkStream with the page contents.  The caller is responsible
      *  for a deleting the returned value.
      */
@@ -196,7 +199,9 @@
     SkMatrix fInitialTransform;
     SkClipStack fExistingClipStack;
     SkRegion fExistingClipRegion;
-    SkPDFArray* fAnnotations;
+
+    SkTDArray<RectWithData*> fLinkToURLs;
+    SkTDArray<RectWithData*> fLinkToDestinations;
     SkTDArray<NamedDestination*> fNamedDestinations;
 
     SkTDArray<SkPDFObject*> fGraphicStateResources;
@@ -291,7 +296,8 @@
                            const SkMatrix* prePathMatrix = nullptr);
     bool handlePointAnnotation(const SkPoint* points, size_t count,
                                const SkMatrix& matrix, SkAnnotation* annot);
-    void addAnnotation(SkPDFDict*);
+    bool handlePathAnnotation(const SkPath& path, const SkDraw& d,
+                              SkAnnotation* annot);
 
     typedef SkBaseDevice INHERITED;