Add SkPDFDeviceFlatenner which extends SkPDFDevice to add support to flatten the path and the text when we have perspective.
prepare to deprecate SkPDFDevice constructor, and route gm and render_pdfs to use SkDocument::Create pdf interface instead. - controlled by a flag
add comments where we are supposed to flatten other features (paint, shaders, ... )

R=reed@google.com, bungeman@google.com, scroggo@google.com, vandebo@chromium.org, bsalomon@google.com

Author: edisonn@google.com

Review URL: https://codereview.chromium.org/24811002

git-svn-id: http://skia.googlecode.com/svn/trunk@11751 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gm_error.h b/gm/gm_error.h
index e442f5d..e2274c4 100644
--- a/gm/gm_error.h
+++ b/gm/gm_error.h
@@ -28,6 +28,7 @@
 
         kIntentionallySkipped_ErrorType,
         kRenderModeMismatch_ErrorType,
+        kGeneratePdfFailed_ErrorType,
         kExpectationsMismatch_ErrorType,
         kMissingExpectations_ErrorType,
         kWritingReferenceImage_ErrorType,
@@ -45,6 +46,8 @@
             return "IntentionallySkipped";
         case kRenderModeMismatch_ErrorType:
             return "RenderModeMismatch";
+        case kGeneratePdfFailed_ErrorType:
+            return "GeneratePdfFailed";
         case kExpectationsMismatch_ErrorType:
             return "ExpectationsMismatch";
         case kMissingExpectations_ErrorType:
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 3ee90fe..00fcb64 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -23,6 +23,7 @@
 #include "SkData.h"
 #include "SkDeferredCanvas.h"
 #include "SkDevice.h"
+#include "SkDocument.h"
 #include "SkDrawFilter.h"
 #include "SkForceLinking.h"
 #include "SkGPipe.h"
@@ -33,6 +34,7 @@
 #include "SkPDFRasterizer.h"
 #include "SkPicture.h"
 #include "SkRefCnt.h"
+#include "SkScalar.h"
 #include "SkStream.h"
 #include "SkTArray.h"
 #include "SkTDict.h"
@@ -75,6 +77,7 @@
 #define DEBUGFAIL_SEE_STDERR SkDEBUGFAIL("see stderr for message")
 
 extern bool gSkSuppressFontCachePurgeSpew;
+DECLARE_bool(useDocumentInsteadOfDevice);
 
 #ifdef SK_SUPPORT_PDF
     #include "SkPDFDevice.h"
@@ -627,34 +630,51 @@
         }
     }
 
-    static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
+    static bool generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
 #ifdef SK_SUPPORT_PDF
         SkMatrix initialTransform = gm->getInitialTransform();
-        SkISize pageSize = gm->getISize();
-        SkPDFDevice* dev = NULL;
-        if (initialTransform.isIdentity()) {
-            dev = new SkPDFDevice(pageSize, pageSize, initialTransform);
+        if (FLAGS_useDocumentInsteadOfDevice) {
+            SkISize pageISize = gm->getISize();
+            SkAutoTUnref<SkDocument> pdfDoc(SkDocument::CreatePDF(&pdf, NULL, encode_to_dct_data));
+
+            if (!pdfDoc.get()) {
+                return false;
+            }
+
+            SkCanvas* canvas = NULL;
+            canvas = pdfDoc->beginPage(SkIntToScalar(pageISize.width()),
+                                       SkIntToScalar(pageISize.height()));
+            canvas->concat(initialTransform);
+
+            invokeGM(gm, canvas, true, false);
+
+            return pdfDoc->close();
         } else {
-            SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()),
-                                            SkIntToScalar(pageSize.height()));
-            initialTransform.mapRect(&content);
-            content.intersect(0, 0, SkIntToScalar(pageSize.width()),
-                              SkIntToScalar(pageSize.height()));
-            SkISize contentSize =
-                SkISize::Make(SkScalarRoundToInt(content.width()),
-                              SkScalarRoundToInt(content.height()));
-            dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
+            SkISize pageSize = gm->getISize();
+            SkPDFDevice* dev = NULL;
+            if (initialTransform.isIdentity()) {
+                dev = new SkPDFDevice(pageSize, pageSize, initialTransform);
+            } else {
+                SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()),
+                                                SkIntToScalar(pageSize.height()));
+                initialTransform.mapRect(&content);
+                content.intersect(0, 0, SkIntToScalar(pageSize.width()),
+                                  SkIntToScalar(pageSize.height()));
+                SkISize contentSize =
+                    SkISize::Make(SkScalarRoundToInt(content.width()),
+                                  SkScalarRoundToInt(content.height()));
+                dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
+            }
+            dev->setDCTEncoder(encode_to_dct_data);
+            SkAutoUnref aur(dev);
+            SkCanvas c(dev);
+            invokeGM(gm, &c, true, false);
+            SkPDFDocument doc;
+            doc.appendPage(dev);
+            doc.emitPDF(&pdf);
         }
-        dev->setDCTEncoder(encode_to_dct_data);
-        SkAutoUnref aur(dev);
-
-        SkCanvas c(dev);
-        invokeGM(gm, &c, true, false);
-
-        SkPDFDocument doc;
-        doc.appendPage(dev);
-        doc.emitPDF(&pdf);
-#endif
+#endif  // SK_SUPPORT_PDF
+        return true; // Do not report failure if pdf is not supported.
     }
 
     static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
@@ -1048,43 +1068,46 @@
                 errors.add(write_bitmap(path, bitmapAndDigest.fBitmap));
             }
         } else if (gRec.fBackend == kPDF_Backend) {
-            generate_pdf(gm, document);
-
-            SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream());
-            if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
-                path = make_filename(writePath, gm->shortName(), gRec.fName, "", "pdf");
-                errors.add(write_document(path, documentStream));
-            }
-
-            if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) {
-                for (int i = 0; i < pdfRasterizers.count(); i++) {
-                    SkBitmap pdfBitmap;
-                    SkASSERT(documentStream->rewind());
-                    bool success = (*pdfRasterizers[i]->fRasterizerFunction)(
-                            documentStream.get(), &pdfBitmap);
-                    if (!success) {
-                        gm_fprintf(stderr, "FAILED to render PDF for %s using renderer %s\n",
-                                   gm->shortName(),
-                                   pdfRasterizers[i]->fName);
-                        continue;
-                    }
-
-                    SkString configName(gRec.fName);
-                    configName.append("-");
-                    configName.append(pdfRasterizers[i]->fName);
-
-                    BitmapAndDigest bitmapAndDigest(pdfBitmap);
-                    errors.add(compare_test_results_to_stored_expectations(
-                               gm, gRec, configName.c_str(), &bitmapAndDigest));
-
-                    if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
-                        path = make_bitmap_filename(writePath, gm->shortName(), configName.c_str(),
-                                                    "", bitmapAndDigest.fDigest);
-                        errors.add(write_bitmap(path, bitmapAndDigest.fBitmap));
-                    }
-                }
+            if (!generate_pdf(gm, document)) {
+                errors.add(kGeneratePdfFailed_ErrorType);
             } else {
-                errors.add(kIntentionallySkipped_ErrorType);
+                SkAutoTUnref<SkStreamAsset> documentStream(document.detachAsStream());
+                if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
+                    path = make_filename(writePath, gm->shortName(), gRec.fName, "", "pdf");
+                    errors.add(write_document(path, documentStream));
+                }
+
+                if (!(gm->getFlags() & GM::kSkipPDFRasterization_Flag)) {
+                    for (int i = 0; i < pdfRasterizers.count(); i++) {
+                        SkBitmap pdfBitmap;
+                        SkASSERT(documentStream->rewind());
+                        bool success = (*pdfRasterizers[i]->fRasterizerFunction)(
+                                documentStream.get(), &pdfBitmap);
+                        if (!success) {
+                            gm_fprintf(stderr, "FAILED to render PDF for %s using renderer %s\n",
+                                       gm->shortName(),
+                                       pdfRasterizers[i]->fName);
+                            continue;
+                        }
+
+                        SkString configName(gRec.fName);
+                        configName.append("-");
+                        configName.append(pdfRasterizers[i]->fName);
+
+                        BitmapAndDigest bitmapAndDigest(pdfBitmap);
+                        errors.add(compare_test_results_to_stored_expectations(
+                                   gm, gRec, configName.c_str(), &bitmapAndDigest));
+
+                        if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
+                            path = make_bitmap_filename(writePath, gm->shortName(),
+                                                        configName.c_str(),
+                                                        "", bitmapAndDigest.fDigest);
+                            errors.add(write_bitmap(path, bitmapAndDigest.fBitmap));
+                        }
+                    }
+                } else {
+                    errors.add(kIntentionallySkipped_ErrorType);
+                }
             }
         } else if (gRec.fBackend == kXPS_Backend) {
             generate_xps(gm, document);
@@ -1446,6 +1469,7 @@
 // Probably define spacial names like centerx, centery, top, bottom, left, right
 // then we can write something reabable like --rotate centerx centery 90
 DEFINE_bool(forcePerspectiveMatrix, false, "Force a perspective matrix.");
+DEFINE_bool(useDocumentInsteadOfDevice, false, "Use SkDocument::CreateFoo instead of SkFooDevice.");
 
 static SkData* encode_to_dct_data(size_t* pixelRefOffset, const SkBitmap& bitmap) {
     // Filter output of warnings that JPEG is not available for the image.
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
index 0ac8d45..2c75b26 100644
--- a/gyp/pdf.gyp
+++ b/gyp/pdf.gyp
@@ -15,6 +15,7 @@
       'include_dirs': [
         '../include/pdf',
         '../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h
+        '../src/pdf',
         '../src/utils', # needed to get SkBitSet.h
       ],
       'sources': [
diff --git a/gyp/pdf.gypi b/gyp/pdf.gypi
index d9a1bc4..fec201f 100644
--- a/gyp/pdf.gypi
+++ b/gyp/pdf.gypi
@@ -13,6 +13,8 @@
         '<(skia_src_path)/pdf/SkPDFCatalog.cpp',
         '<(skia_src_path)/pdf/SkPDFCatalog.h',
         '<(skia_src_path)/pdf/SkPDFDevice.cpp',
+        '<(skia_src_path)/pdf/SkPDFDeviceFlattener.cpp',
+        '<(skia_src_path)/pdf/SkPDFDeviceFlattener.h',
         '<(skia_src_path)/pdf/SkPDFDocument.cpp',
         '<(skia_src_path)/pdf/SkPDFFont.cpp',
         '<(skia_src_path)/pdf/SkPDFFont.h',
diff --git a/include/core/SkDraw.h b/include/core/SkDraw.h
index 1c2c66e..772e11e 100644
--- a/include/core/SkDraw.h
+++ b/include/core/SkDraw.h
@@ -97,16 +97,16 @@
     static RectType ComputeRectType(const SkPaint&, const SkMatrix&,
                                     SkPoint* strokeSize);
 
-private:
     void    drawText_asPaths(const char text[], size_t byteLength,
                              SkScalar x, SkScalar y, const SkPaint&) const;
-    void    drawDevMask(const SkMask& mask, const SkPaint&) const;
-    void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
-
     void    drawPosText_asPaths(const char text[], size_t byteLength,
                                 const SkScalar pos[], SkScalar constY,
                                 int scalarsPerPosition, const SkPaint&) const;
 
+private:
+    void    drawDevMask(const SkMask& mask, const SkPaint&) const;
+    void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
+
     /**
      *  Return the current clip bounds, in local coordinates, with slop to account
      *  for antialiasing or hairlines (i.e. device-bounds outset by 1, and then
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index 30ec2dc..d668be5 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -11,6 +11,7 @@
 #define SkPDFDevice_DEFINED
 
 #include "SkBitmapDevice.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkPaint.h"
 #include "SkPath.h"
@@ -63,7 +64,7 @@
      *         inverse scale+translate to accommodate the one that SkPDFDevice
      *         always does.
      */
-    // TODO(vandebo): The sizes should be SkSize and not SkISize.
+    // Deprecated, please use SkDocument::CreatePdf() instead.
     SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
                        const SkMatrix& initialTransform);
     SK_API virtual ~SkPDFDevice();
@@ -311,6 +312,11 @@
                                 const SkMatrix& matrix);
 
     typedef SkBitmapDevice INHERITED;
+
+    // TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
+    // an SkPDFDevice
+    //friend class SkDocument_PDF;
+    //friend class SkPDFImageShader;
 };
 
 #endif
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp
index b9b55f8..695929f 100644
--- a/src/doc/SkDocument_PDF.cpp
+++ b/src/doc/SkDocument_PDF.cpp
@@ -6,8 +6,8 @@
  */
 
 #include "SkDocument.h"
-#include "SkPDFDevice.h"
 #include "SkPDFDocument.h"
+#include "SkPDFDeviceFlattener.h"
 
 class SkDocument_PDF : public SkDocument {
 public:
@@ -27,19 +27,14 @@
 
 protected:
     virtual SkCanvas* onBeginPage(SkScalar width, SkScalar height,
-                                  const SkRect& content) SK_OVERRIDE {
+                                  const SkRect& trimBox) SK_OVERRIDE {
         SkASSERT(NULL == fCanvas);
         SkASSERT(NULL == fDevice);
 
-        SkISize pageS, contentS;
-        SkMatrix matrix;
+        SkSize mediaBoxSize;
+        mediaBoxSize.set(width, height);
 
-        pageS.set(SkScalarRoundToInt(width), SkScalarRoundToInt(height));
-        contentS.set(SkScalarRoundToInt(content.width()),
-                     SkScalarRoundToInt(content.height()));
-        matrix.setTranslate(content.fLeft, content.fTop);
-
-        fDevice = SkNEW_ARGS(SkPDFDevice, (pageS, contentS, matrix));
+        fDevice = SkNEW_ARGS(SkPDFDeviceFlattener, (mediaBoxSize, &trimBox));
         if (fEncoder) {
             fDevice->setDCTEncoder(fEncoder);
         }
@@ -78,7 +73,7 @@
 
 private:
     SkPDFDocument*  fDoc;
-    SkPDFDevice* fDevice;
+    SkPDFDeviceFlattener* fDevice;
     SkCanvas*       fCanvas;
     SkPicture::EncodeBitmap fEncoder;
 };
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 538422c..43117a2 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -2034,6 +2034,7 @@
                                      const SkBitmap& bitmap,
                                      const SkIRect* srcRect,
                                      const SkPaint& paint) {
+    // TODO(edisonn): Perspective matrix support implemented here
     SkMatrix scaled;
     // Adjust for origin flip.
     scaled.setScale(SK_Scalar1, -SK_Scalar1);
diff --git a/src/pdf/SkPDFDeviceFlattener.cpp b/src/pdf/SkPDFDeviceFlattener.cpp
new file mode 100644
index 0000000..9c0bf41
--- /dev/null
+++ b/src/pdf/SkPDFDeviceFlattener.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 "SkPDFDeviceFlattener.h"
+
+#include "SkDraw.h"
+
+static SkISize SkSizeToISize(const SkSize& size) {
+    return SkISize::Make(SkScalarRoundToInt(size.width()), SkScalarRoundToInt(size.height()));
+}
+
+SkPDFDeviceFlattener::SkPDFDeviceFlattener(const SkSize& pageSize, const SkRect* trimBox)
+            : SkPDFDevice(SkSizeToISize(pageSize),
+                          SkSizeToISize(pageSize),
+                          SkMatrix::I()) {
+    // TODO(edisonn): store the trimbox on emit.
+}
+
+SkPDFDeviceFlattener::~SkPDFDeviceFlattener() {
+}
+
+static void flattenPaint(const SkDraw& d, SkPaint* paint) {
+    if (paint->getShader()) {
+        SkMatrix local = paint->getShader()->getLocalMatrix();
+        local.preConcat(*d.fMatrix);
+        paint->getShader()->setLocalMatrix(local);
+    }
+}
+
+void SkPDFDeviceFlattener::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
+                                      size_t count, const SkPoint points[],
+                                      const SkPaint& paint) {
+    if (!mustFlatten(d)) {
+        INHERITED::drawPoints(d, mode, count, points, paint);
+        return;
+    }
+
+    SkPaint paintFlatten(paint);
+    flattenPaint(d, &paintFlatten);
+
+    SkPoint* flattenedPoints = SkNEW_ARRAY(SkPoint, count);
+    d.fMatrix->mapPoints(flattenedPoints, points, count);
+    SkDraw draw(d);
+    SkMatrix identity = SkMatrix::I();
+    draw.fMatrix = &identity;
+    INHERITED::drawPoints(draw, mode, count, flattenedPoints, paintFlatten);
+    SkDELETE_ARRAY(flattenedPoints);
+}
+
+void SkPDFDeviceFlattener::drawRect(const SkDraw& d, const SkRect& r, const SkPaint& paint) {
+    if (!mustFlatten(d)) {
+        INHERITED::drawRect(d, r, paint);
+        return;
+    }
+
+    SkPath path;
+    path.addRect(r);
+    path.transform(*d.fMatrix);
+    SkDraw draw(d);
+    SkMatrix matrix = SkMatrix::I();
+    draw.fMatrix = &matrix;
+
+    SkPaint paintFlatten(paint);
+    flattenPaint(d, &paintFlatten);
+
+    INHERITED::drawPath(draw, path, paintFlatten, NULL, true);
+}
+
+void SkPDFDeviceFlattener::drawPath(const SkDraw& d, const SkPath& origPath,
+                                    const SkPaint& paint, const SkMatrix* prePathMatrix,
+                                    bool pathIsMutable) {
+    if (!mustFlatten(d) && !(prePathMatrix && prePathMatrix->hasPerspective())) {
+        INHERITED::drawPath(d, origPath, paint, prePathMatrix, pathIsMutable);
+        return;
+    }
+
+    SkPath* pathPtr = (SkPath*)&origPath;
+    SkPath tmpPath;
+
+    if (!pathIsMutable) {
+        tmpPath = origPath;
+        pathPtr = &tmpPath;
+    }
+
+    if (prePathMatrix) {
+        pathPtr->transform(*prePathMatrix);
+    }
+
+    SkPaint paintFlatten(paint);
+    flattenPaint(d, &paintFlatten);
+
+    bool fill = paintFlatten.getFillPath(*pathPtr, &tmpPath);
+    SkDEBUGCODE(pathPtr = (SkPath*)0x12345678);  // Don't use pathPtr after this point.
+
+    paintFlatten.setPathEffect(NULL);
+    if (fill) {
+        paintFlatten.setStyle(SkPaint::kFill_Style);
+    } else {
+        paintFlatten.setStyle(SkPaint::kStroke_Style);
+        paintFlatten.setStrokeWidth(0);
+    }
+
+    tmpPath.transform(*d.fMatrix);
+
+    SkDraw draw(d);
+    SkMatrix matrix = SkMatrix::I();
+    draw.fMatrix = &matrix;
+
+    INHERITED::drawPath(draw, tmpPath, paintFlatten, NULL, true);
+}
+
+void SkPDFDeviceFlattener::drawText(const SkDraw& d, const void* text, size_t len,
+                                    SkScalar x, SkScalar y, const SkPaint& paint) {
+    if (mustPathText(d, paint)) {
+        d.drawText_asPaths((const char*)text, len, x, y, paint);
+        return;
+    }
+
+    INHERITED::drawText(d, text, len, x, y, paint);
+}
+
+void SkPDFDeviceFlattener::drawPosText(const SkDraw& d, const void* text, size_t len,
+                                       const SkScalar pos[], SkScalar constY,
+                                       int scalarsPerPos, const SkPaint& paint) {
+    if (mustPathText(d, paint)) {
+        d.drawPosText_asPaths((const char*)text, len, pos, constY, scalarsPerPos, paint);
+        return;
+    }
+    INHERITED::drawPosText(d, text, len, pos, constY,scalarsPerPos, paint);
+}
+
+void SkPDFDeviceFlattener::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
+                                          const SkPath& path, const SkMatrix* matrix,
+                                          const SkPaint& paint) {
+    if (mustPathText(d, paint) || (matrix && matrix->hasPerspective())) {
+        d.drawTextOnPath((const char*)text, len, path, matrix, paint);
+        return;
+    }
+    INHERITED::drawTextOnPath(d, text, len, path, matrix, paint);
+}
+
+bool SkPDFDeviceFlattener::mustFlatten(const SkDraw& d) const {
+    // TODO(edisonn): testability, add flag to force return true.
+    return d.fMatrix->hasPerspective();
+}
+
+bool SkPDFDeviceFlattener::mustPathText(const SkDraw& d, const SkPaint&) {
+    // TODO(edisonn): testability, add flag to force return true.
+    // TODO(edisonn): TBD: How to flatten MaskFilter.
+    return d.fMatrix->hasPerspective();
+}
diff --git a/src/pdf/SkPDFDeviceFlattener.h b/src/pdf/SkPDFDeviceFlattener.h
new file mode 100644
index 0000000..f1047db
--- /dev/null
+++ b/src/pdf/SkPDFDeviceFlattener.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPDFDeviceFlattener_DEFINED
+#define SkPDFDeviceFlattener_DEFINED
+
+#include "SkPDFDevice.h"
+
+
+/** \class SkPDFDeviceFlattener
+
+    The PDF Device Flattener is used to flatten features without native support in PDF.
+    For now, the only one implemented is Perspective.
+
+    TODO(edisonn): Rename the class once we know all the things it will do.
+*/
+class SkPDFDeviceFlattener : public SkPDFDevice {
+private:
+    typedef SkPDFDevice INHERITED;
+
+    SK_API SkPDFDeviceFlattener(const SkSize& pageSize, const SkRect* trimBox = NULL);
+
+public:
+    SK_API virtual ~SkPDFDeviceFlattener();
+
+    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
+                            size_t count, const SkPoint[],
+                            const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint);
+    virtual void drawPath(const SkDraw&, const SkPath& origpath,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable) SK_OVERRIDE;
+    virtual void drawText(const SkDraw&, const void* text, size_t len,
+                          SkScalar x, SkScalar y, const SkPaint&) SK_OVERRIDE;
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint&) SK_OVERRIDE;
+    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE;
+private:
+
+    bool mustFlatten(const SkDraw& d) const;
+    bool mustPathText(const SkDraw& d, const SkPaint& paint);
+
+    friend class SkDocument_PDF;
+};
+
+#endif  // SkPDFDeviceFlattener_DEFINED
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index 80ea2f2..70fb616 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -9,7 +9,6 @@
 
 #include "SkPDFShader.h"
 
-#include "SkCanvas.h"
 #include "SkData.h"
 #include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
@@ -870,6 +869,8 @@
     unflip.preScale(SK_Scalar1, -SK_Scalar1);
     SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()),
                                  SkScalarRound(deviceBounds.height()));
+    // TODO(edisonn): should we pass here the DCT encoder of the destination device?
+    // TODO(edisonn): NYI Perspective, use SkPDFDeviceFlattener.
     SkPDFDevice pattern(size, size, unflip);
     SkCanvas canvas(&pattern);
 
diff --git a/tools/PdfRenderer.cpp b/tools/PdfRenderer.cpp
index 704cbea..890abde 100644
--- a/tools/PdfRenderer.cpp
+++ b/tools/PdfRenderer.cpp
@@ -13,7 +13,7 @@
 
 namespace sk_tools {
 
-void PdfRenderer::init(SkPicture* pict) {
+void PdfRenderer::init(SkPicture* pict, SkWStream* stream) {
     SkASSERT(NULL == fPicture);
     SkASSERT(NULL == fCanvas.get());
     if (fPicture != NULL || NULL != fCanvas.get()) {
@@ -26,44 +26,35 @@
     }
 
     fPicture = pict;
-    fCanvas.reset(this->setupCanvas());
+    fCanvas.reset(this->setupCanvas(stream, pict->width(), pict->height()));
 }
 
-SkCanvas* PdfRenderer::setupCanvas() {
-    return this->setupCanvas(fPicture->width(), fPicture->height());
-}
+SkCanvas* PdfRenderer::setupCanvas(SkWStream* stream, int width, int height) {
+    fPdfDoc.reset(SkDocument::CreatePDF(stream, NULL, fEncoder));
 
-SkCanvas* PdfRenderer::setupCanvas(int width, int height) {
-    SkISize pageSize = SkISize::Make(width, height);
-    fPDFDevice = SkNEW_ARGS(SkPDFDevice, (pageSize, pageSize, SkMatrix::I()));
-    fPDFDevice->setDCTEncoder(fEncoder);
-    return SkNEW_ARGS(SkCanvas, (fPDFDevice));
+    SkCanvas* canvas = fPdfDoc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
+    canvas->ref();
+
+    return canvas;
 }
 
 void PdfRenderer::end() {
     fPicture = NULL;
     fCanvas.reset(NULL);
-    if (fPDFDevice) {
-        SkDELETE(fPDFDevice);
-        fPDFDevice = NULL;
-    }
+    fPdfDoc.reset(NULL);
 }
 
-void PdfRenderer::write(SkWStream* stream) const {
-    SkPDFDocument doc;
-    doc.appendPage(fPDFDevice);
-    doc.emitPDF(stream);
-}
-
-void SimplePdfRenderer::render() {
+bool SimplePdfRenderer::render() {
     SkASSERT(fCanvas.get() != NULL);
     SkASSERT(fPicture != NULL);
     if (NULL == fCanvas.get() || NULL == fPicture) {
-        return;
+        return false;
     }
 
     fCanvas->drawPicture(*fPicture);
     fCanvas->flush();
+
+    return fPdfDoc->close();
 }
 
 }
diff --git a/tools/PdfRenderer.h b/tools/PdfRenderer.h
index be338e9..d70c458 100644
--- a/tools/PdfRenderer.h
+++ b/tools/PdfRenderer.h
@@ -13,8 +13,8 @@
 // An SkPicture can be built manually, or read from an SKP file.
 //
 
+#include "SkDocument.h"
 #include "SkMath.h"
-#include "SkPDFDevice.h"
 #include "SkPicture.h"
 #include "SkTypes.h"
 #include "SkTDArray.h"
@@ -23,32 +23,30 @@
 
 class SkBitmap;
 class SkCanvas;
+class SkWStream;
 
 namespace sk_tools {
 
 class PdfRenderer : public SkRefCnt {
 public:
-    virtual void init(SkPicture* pict);
+    virtual void init(SkPicture* pict, SkWStream* stream);
     virtual void setup() {}
-    virtual void render() = 0;
+    virtual bool render() = 0;
     virtual void end();
 
     PdfRenderer(SkPicture::EncodeBitmap encoder)
         : fPicture(NULL)
-        , fPDFDevice(NULL)
         , fEncoder(encoder)
+        , fPdfDoc(NULL)
         {}
 
-    void write(SkWStream* stream) const;
-
 protected:
-    SkCanvas* setupCanvas();
-    SkCanvas* setupCanvas(int width, int height);
+    SkCanvas* setupCanvas(SkWStream* stream, int width, int height);
 
     SkAutoTUnref<SkCanvas> fCanvas;
     SkPicture* fPicture;
-    SkPDFDevice* fPDFDevice;
     SkPicture::EncodeBitmap fEncoder;
+    SkAutoTUnref<SkDocument> fPdfDoc;
 
 private:
     typedef SkRefCnt INHERITED;
@@ -58,7 +56,7 @@
 public:
     SimplePdfRenderer(SkPicture::EncodeBitmap encoder)
         : PdfRenderer(encoder) {}
-    virtual void render() SK_OVERRIDE;
+    virtual bool render() SK_OVERRIDE;
 
 private:
     typedef PdfRenderer INHERITED;
diff --git a/tools/render_pdfs_main.cpp b/tools/render_pdfs_main.cpp
index a710064..e07664c 100644
--- a/tools/render_pdfs_main.cpp
+++ b/tools/render_pdfs_main.cpp
@@ -131,28 +131,24 @@
  * @param inputFilename The skp file that was read.
  * @param renderer The object responsible to write the pdf file.
  */
-static bool write_output(const SkString& outputDir,
-                         const SkString& inputFilename,
-                         const sk_tools::PdfRenderer& renderer) {
+static SkWStream* open_stream(const SkString& outputDir,
+                              const SkString& inputFilename) {
     if (outputDir.isEmpty()) {
-        SkDynamicMemoryWStream stream;
-        renderer.write(&stream);
-        return true;
+        return SkNEW(SkDynamicMemoryWStream);
     }
 
     SkString outputPath;
     if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
-        return false;
+        return NULL;
     }
 
-    SkFILEWStream stream(outputPath.c_str());
-    if (!stream.isValid()) {
+    SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (outputPath.c_str()));
+    if (!stream->isValid()) {
         SkDebugf("Could not write to file %s\n", outputPath.c_str());
-        return false;
+        return NULL;
     }
-    renderer.write(&stream);
 
-    return true;
+    return stream;
 }
 
 /** Reads an skp file, renders it to pdf and writes the output to a pdf file
@@ -182,13 +178,19 @@
     SkDebugf("exporting... [%i %i] %s\n", picture->width(), picture->height(),
              inputPath.c_str());
 
-    renderer.init(picture);
+    SkWStream* stream(open_stream(outputDir, inputFilename));
 
-    renderer.render();
+    if (!stream) {
+        return false;
+    }
 
-    bool success = write_output(outputDir, inputFilename, renderer);
+    renderer.init(picture, stream);
+
+    bool success = renderer.render();
+    SkDELETE(stream);
 
     renderer.end();
+
     return success;
 }