Generalize the flip origin argument to the PDF device constructor.
authorvandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 11 Apr 2011 23:24:18 +0000 (23:24 +0000)
committervandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 11 Apr 2011 23:24:18 +0000 (23:24 +0000)
The argument still has a default value that does what most users will want, but provides more flexibility.
Chrome will use this change to support an initial translation of the origin to simulate a margin and to scale the entire content (needed on Windows).

When landing to Chrome, this will need http://codereview.chromium.org/6820038

Review URL: http://codereview.appspot.com/4373052

git-svn-id: http://skia.googlecode.com/svn/trunk@1111 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/gmmain.cpp
include/pdf/SkPDFDevice.h
include/pdf/SkPDFUtils.h
src/pdf/SkPDFDevice.cpp
src/pdf/SkPDFFont.cpp
src/pdf/SkPDFShader.cpp
src/pdf/SkPDFUtils.cpp

index 357a54e..cb4e036 100644 (file)
@@ -261,7 +261,10 @@ int main (int argc, char * const argv[]) {
             if (gRec[i].fBackend == kPDF_Backend && writePath) {
 #ifdef SK_SUPPORT_PDF
                 SkISize size = gm->getISize();
-                SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height());
+                SkMatrix identity;
+                identity.reset();
+                SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height(),
+                                                   identity);
                 SkAutoUnref aur(dev);
 
                 SkCanvas c(dev);
index 9292227..2b9e5e6 100644 (file)
@@ -33,8 +33,8 @@ class SkPDFShader;
 class SkPDFStream;
 
 class SkPDFDeviceFactory : public SkDeviceFactory {
-    virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width, int height,
-                                bool isOpaque, bool isForLayer);
+    virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width,
+                                int height, bool isOpaque, bool isForLayer);
 };
 
 /** \class SkPDFDevice
@@ -43,24 +43,22 @@ class SkPDFDeviceFactory : public SkDeviceFactory {
 */
 class SkPDFDevice : public SkDevice {
 public:
-    /** Skia generally uses the top left as the origin and PDFs natively use
-        the bottom left.  We can move the origin to the top left in the PDF
-        with a transform, but we have to be careful to apply the transform
-        only once.
-     */
-    enum OriginTransform {
-        kFlip_OriginTransform,
-        kNoFlip_OriginTransform,
-    };
-
     /** Create a PDF drawing context with the given width and height.
      *  72 points/in means letter paper is 612x792.
      *  @param width  Page width in points.
      *  @param height Page height in points.
-     *  @param flipOrigin Flip the origin from lower left to upper left.
+     *  @param initialTransform The initial transform to apply to the page.
+     *         This may be useful to, for example, move the origin in and
+     *         over a bit to account for a margin, scale the canvas,
+     *         or apply a rotation.  Note1: the SkPDFDevice also applies
+     *         a scale+translate transform to move the origin from the
+     *         bottom left (PDF default) to the top left.  Note2: drawDevice
+     *         (used by layer restore) draws the device after this initial
+     *         transform is applied, so the PDF device factory does an
+     *         inverse scale+translate to accommodate the one that SkPDFDevice
+     *         always does.
      */
-    SkPDFDevice(int width, int height,
-                OriginTransform flipOrigin = kFlip_OriginTransform);
+    SkPDFDevice(int width, int height, const SkMatrix& initialTransform);
     virtual ~SkPDFDevice();
 
     virtual SkDeviceFactory* getDeviceFactory() {
@@ -141,7 +139,7 @@ public:
 private:
     int fWidth;
     int fHeight;
-    OriginTransform fFlipOrigin;
+    SkMatrix fInitialTransform;
     SkRefPtr<SkPDFDict> fResourceDict;
 
     SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
index 501d746..62a5120 100644 (file)
@@ -40,6 +40,7 @@ class SkPDFArray;
 class SkPDFUtils {
 public:
     static SkPDFArray* MatrixToArray(const SkMatrix& matrix);
+    static void AppendTransform(const SkMatrix& matrix, SkWStream* content);
 
     static void MoveTo(SkScalar x, SkScalar y, SkWStream* content);
     static void AppendLine(SkScalar x, SkScalar y, SkWStream* content);
index 3665dd8..4a25511 100644 (file)
@@ -113,11 +113,13 @@ void alignText(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
 SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
                                         int width, int height, bool isOpaque,
                                         bool isForLayer) {
-    SkPDFDevice::OriginTransform flip = SkPDFDevice::kFlip_OriginTransform;
+    SkMatrix initialTransform;
+    initialTransform.reset();
     if (isForLayer) {
-        flip = SkPDFDevice::kNoFlip_OriginTransform;
+        initialTransform.setTranslate(0, height);
+        initialTransform.preScale(1, -1);
     }
-    return SkNEW_ARGS(SkPDFDevice, (width, height, flip));
+    return SkNEW_ARGS(SkPDFDevice, (width, height, initialTransform));
 }
 
 static inline SkBitmap makeABitmap(int width, int height) {
@@ -126,11 +128,11 @@ static inline SkBitmap makeABitmap(int width, int height) {
     return bitmap;
 }
 
-SkPDFDevice::SkPDFDevice(int width, int height, OriginTransform flipOrigin)
+SkPDFDevice::SkPDFDevice(int width, int height,
+                         const SkMatrix& initialTransform)
     : SkDevice(NULL, makeABitmap(width, height), false),
       fWidth(width),
       fHeight(height),
-      fFlipOrigin(flipOrigin),
       fGraphicStackIndex(0) {
     fGraphicStack[0].fColor = SK_ColorBLACK;
     fGraphicStack[0].fTextSize = SK_ScalarNaN;  // This has no default value.
@@ -142,10 +144,14 @@ SkPDFDevice::SkPDFDevice(int width, int height, OriginTransform flipOrigin)
     fGraphicStack[0].fClip.setRect(0,0, width, height);
     fGraphicStack[0].fTransform.reset();
 
-    if (flipOrigin == kFlip_OriginTransform) {
-        fContent.writeText("1 0 0 -1 0 ");
-        fContent.writeDecAsText(fHeight);
-        fContent.writeText(" cm\n");
+    // Skia generally uses the top left as the origin but PDF natively has the
+    // origin at the bottom left. This matrix corrects for that.  When layering,
+    // we specify an inverse correction to cancel this out.
+    fInitialTransform.setTranslate(0, height);
+    fInitialTransform.preScale(1, -1);
+    fInitialTransform.preConcat(initialTransform);
+    if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
+        SkPDFUtils::AppendTransform(fInitialTransform, &fContent);
     }
 }
 
@@ -601,13 +607,10 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
         // PDF positions patterns relative to the initial transform, so
         // we need to apply the current transform to the shader parameters.
         SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
-        if (fFlipOrigin == kFlip_OriginTransform) {
-            transform.postScale(1, -1);
-            transform.postTranslate(0, fHeight);
-        }
+        transform.postConcat(fInitialTransform);
 
         // PDF doesn't support kClamp_TileMode, so we simulate it by making
-        // a pattern the size of the drawing service.
+        // a pattern the size of the drawing surface.
         SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
         pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
         SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref
@@ -808,13 +811,7 @@ SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
             fGraphicStack[fGraphicStackIndex - 1].fClip)
         pushGS();
 
-    SkScalar transform[6];
-    SkAssertResult(m.pdfTransform(transform));
-    for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
-        SkPDFScalar::Append(transform[i], &fContent);
-        fContent.writeText(" ");
-    }
-    fContent.writeText("cm\n");
+    SkPDFUtils::AppendTransform(m, &fContent);
     fGraphicStack[fGraphicStackIndex].fTransform = m;
 
     return old;
index 9a2180d..3e363f7 100644 (file)
@@ -251,15 +251,7 @@ void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box,
     content->writeText(" d1\n");
 }
 
-SkPDFArray* makeFontBBox(
-        SkIRect glyphBBox, uint16_t emSize,
-        SkPDFDevice::OriginTransform flipOrigin =
-            SkPDFDevice::kNoFlip_OriginTransform) {
-    if (flipOrigin == SkPDFDevice::kFlip_OriginTransform) {
-        int32_t temp = -glyphBBox.fTop;
-        glyphBBox.fTop = -glyphBBox.fBottom;
-        glyphBBox.fBottom = temp;
-    }
+SkPDFArray* makeFontBBox(SkIRect glyphBBox, uint16_t emSize) {
     SkPDFArray* bbox = new SkPDFArray;
     bbox->reserve(4);
     bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fLeft,
index c833c5d..3f383e5 100644 (file)
@@ -495,8 +495,10 @@ void SkPDFShader::doImageShader() {
     surfaceBBox.set(fState.get()->fBBox);
     transformBBox(finalMatrix, &surfaceBBox);
 
-    SkPDFDevice pattern(surfaceBBox.fRight, surfaceBBox.fBottom,
-                        SkPDFDevice::kNoFlip_OriginTransform);
+    SkMatrix unflip;
+    unflip.setTranslate(0, surfaceBBox.fBottom);
+    unflip.preScale(1, -1);
+    SkPDFDevice pattern(surfaceBBox.fRight, surfaceBBox.fBottom, unflip);
     SkCanvas canvas(&pattern);
     canvas.clipRect(surfaceBBox, SkRegion::kReplace_Op);
 
index 8bd9c8f..57e0cb7 100644 (file)
@@ -35,6 +35,17 @@ SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
 }
 
 // static
+void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
+    SkScalar values[6];
+    SkAssertResult(matrix.pdfTransform(values));
+    for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
+        SkPDFScalar::Append(values[i], content);
+        content->writeText(" ");
+    }
+    content->writeText("cm\n");
+}
+
+// static
 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
     SkPDFScalar::Append(x, content);
     content->writeText(" ");