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
This commit is contained in:
vandebo@chromium.org 2011-04-11 23:24:18 +00:00
parent 7aaee005fa
commit 75f97e452e
7 changed files with 53 additions and 49 deletions

View 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);

View 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;

View 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);

View 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;

View 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,

View 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);

View File

@ -34,6 +34,17 @@ SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
return result;
}
// 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);