From 75f97e452e8f2ee55cd2b283df7d7734f48bc2bf Mon Sep 17 00:00:00 2001 From: "vandebo@chromium.org" Date: Mon, 11 Apr 2011 23:24:18 +0000 Subject: [PATCH] Generalize the flip origin argument to the PDF device constructor. 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 | 5 ++++- include/pdf/SkPDFDevice.h | 30 ++++++++++++++---------------- include/pdf/SkPDFUtils.h | 1 + src/pdf/SkPDFDevice.cpp | 39 ++++++++++++++++++--------------------- src/pdf/SkPDFFont.cpp | 10 +--------- src/pdf/SkPDFShader.cpp | 6 ++++-- src/pdf/SkPDFUtils.cpp | 11 +++++++++++ 7 files changed, 53 insertions(+), 49 deletions(-) diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp index 357a54e..cb4e036 100644 --- a/gm/gmmain.cpp +++ b/gm/gmmain.cpp @@ -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); diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h index 9292227..2b9e5e6 100644 --- a/include/pdf/SkPDFDevice.h +++ b/include/pdf/SkPDFDevice.h @@ -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 fResourceDict; SkTDArray fGraphicStateResources; diff --git a/include/pdf/SkPDFUtils.h b/include/pdf/SkPDFUtils.h index 501d746..62a5120 100644 --- a/include/pdf/SkPDFUtils.h +++ b/include/pdf/SkPDFUtils.h @@ -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); diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 3665dd8..4a25511 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -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; diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp index 9a2180d..3e363f7 100644 --- a/src/pdf/SkPDFFont.cpp +++ b/src/pdf/SkPDFFont.cpp @@ -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, diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index c833c5d..3f383e5 100644 --- a/src/pdf/SkPDFShader.cpp +++ b/src/pdf/SkPDFShader.cpp @@ -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); diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp index 8bd9c8f..57e0cb7 100644 --- a/src/pdf/SkPDFUtils.cpp +++ b/src/pdf/SkPDFUtils.cpp @@ -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(" "); -- 2.7.4