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);
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
*/
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() {
private:
int fWidth;
int fHeight;
- OriginTransform fFlipOrigin;
+ SkMatrix fInitialTransform;
SkRefPtr<SkPDFDict> fResourceDict;
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
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);
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) {
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.
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);
}
}
// 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
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;
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,
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);
}
// 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(" ");