PDF : New factory function for SkPDFDevice

SkPDFDevice now has factory function that matches what callers need.

Review URL: https://codereview.chromium.org/941023005
This commit is contained in:
halcanary 2015-02-20 06:17:26 -08:00 committed by Commit bot
parent 07d5947b88
commit a1f1ee98a1
8 changed files with 131 additions and 168 deletions

View File

@ -6,6 +6,7 @@
*/
#include "SkDocument.h"
#include "SkPDFCanon.h"
#include "SkPDFDocument.h"
#include "SkPDFDevice.h"
@ -16,8 +17,6 @@ public:
SkScalar rasterDpi)
: SkDocument(stream, doneProc)
, fDoc(SkNEW(SkPDFDocument))
, fDevice(NULL)
, fCanvas(NULL)
, fRasterDpi(rasterDpi) {}
virtual ~SkDocument_PDF() {
@ -28,56 +27,50 @@ public:
protected:
virtual SkCanvas* onBeginPage(SkScalar width, SkScalar height,
const SkRect& trimBox) SK_OVERRIDE {
SkASSERT(NULL == fCanvas);
SkASSERT(NULL == fDevice);
SkASSERT(!fCanvas.get());
SkASSERT(!fDevice.get());
SkISize mediaBoxSize;
mediaBoxSize.set(SkScalarRoundToInt(width), SkScalarRoundToInt(height));
fDevice = SkNEW_ARGS(SkPDFDevice, (mediaBoxSize, mediaBoxSize, SkMatrix::I()));
if (fRasterDpi != 0) {
fDevice->setRasterDpi(fRasterDpi);
}
fCanvas = SkNEW_ARGS(SkCanvas, (fDevice));
SkISize pageSize = SkISize::Make(
SkScalarRoundToInt(width), SkScalarRoundToInt(height));
fDevice.reset(SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
fCanvas.reset(SkNEW_ARGS(SkCanvas, (fDevice)));
fCanvas->clipRect(trimBox);
fCanvas->translate(trimBox.x(), trimBox.y());
return fCanvas;
return fCanvas.get();
}
void onEndPage() SK_OVERRIDE {
SkASSERT(fCanvas);
SkASSERT(fDevice);
SkASSERT(fCanvas.get());
SkASSERT(fDevice.get());
fCanvas->flush();
fDoc->appendPage(fDevice);
fDoc->appendPage(fDevice.get());
fCanvas->unref();
fDevice->unref();
fCanvas = NULL;
fDevice = NULL;
fCanvas.reset(NULL);
fDevice.reset(NULL);
}
bool onClose(SkWStream* stream) SK_OVERRIDE {
SkASSERT(NULL == fCanvas);
SkASSERT(NULL == fDevice);
SkASSERT(!fCanvas.get());
SkASSERT(!fDevice.get());
bool success = fDoc->emitPDF(stream);
SkDELETE(fDoc);
fDoc = NULL;
fDoc.free();
SkDEBUGCODE(fCanon.assertEmpty());
return success;
}
void onAbort() SK_OVERRIDE {
SkDELETE(fDoc);
fDoc = NULL;
fDoc.free();
SkDEBUGCODE(fCanon.assertEmpty());
}
private:
SkPDFDocument* fDoc;
SkPDFDevice* fDevice;
SkCanvas* fCanvas;
SkScalar fRasterDpi;
SkPDFCanon fCanon;
SkAutoTDelete<SkPDFDocument> fDoc;
SkAutoTUnref<SkPDFDevice> fDevice;
SkAutoTUnref<SkCanvas> fCanvas;
SkScalar fRasterDpi;
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -73,6 +73,15 @@ public:
void addBitmap(SkPDFBitmap*);
void removeBitmap(SkPDFBitmap*);
void assertEmpty() const {
SkASSERT(fFontRecords.isEmpty());
SkASSERT(fFunctionShaderRecords.isEmpty());
SkASSERT(fAlphaShaderRecords.isEmpty());
SkASSERT(fImageShaderRecords.isEmpty());
SkASSERT(fGraphicStateRecords.isEmpty());
SkASSERT(fBitmapRecords.isEmpty());
}
private:
struct FontRec {
SkPDFFont* fFont;

View File

@ -575,11 +575,8 @@ SkBaseDevice* SkPDFDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) {
if (kImageFilter_Usage == cinfo.fUsage) {
return SkBitmapDevice::Create(cinfo.fInfo);
}
SkMatrix initialTransform;
initialTransform.reset();
SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height());
return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
return SkPDFDevice::Create(size, fRasterDpi, fCanon);
}
@ -695,76 +692,36 @@ private:
////////////////////////////////////////////////////////////////////////////////
static inline SkImageInfo make_content_info(const SkISize& contentSize,
const SkMatrix* initialTransform) {
SkImageInfo info;
if (initialTransform) {
// Compute the size of the drawing area.
SkVector drawingSize;
SkMatrix inverse;
drawingSize.set(SkIntToScalar(contentSize.fWidth),
SkIntToScalar(contentSize.fHeight));
if (!initialTransform->invert(&inverse)) {
// This shouldn't happen, initial transform should be invertible.
SkASSERT(false);
inverse.reset();
}
inverse.mapVectors(&drawingSize, 1);
SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound();
info = SkImageInfo::MakeUnknown(abs(size.fWidth), abs(size.fHeight));
} else {
info = SkImageInfo::MakeUnknown(abs(contentSize.fWidth),
abs(contentSize.fHeight));
}
return info;
}
// TODO(vandebo) change pageSize to SkSize.
SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
const SkMatrix& initialTransform)
SkPDFDevice::SkPDFDevice(SkISize pageSize,
SkScalar rasterDpi,
SkPDFCanon* canon,
bool flip)
: fPageSize(pageSize)
, fContentSize(contentSize)
, fContentSize(pageSize)
, fExistingClipRegion(SkIRect::MakeSize(pageSize))
, fAnnotations(NULL)
, fResourceDict(NULL)
, fLastContentEntry(NULL)
, fLastMarginContentEntry(NULL)
, fDrawingArea(kContent_DrawingArea)
, fClipStack(NULL)
, fRasterDpi(72.0f)
{
const SkImageInfo info = make_content_info(contentSize, &initialTransform);
// Just report that PDF does not supports perspective in the
// initial transform.
NOT_IMPLEMENTED(initialTransform.hasPerspective(), true);
// Skia generally uses the top left as the origin but PDF natively has the
// origin at the bottom left. This matrix corrects for that. But that only
// needs to be done once, we don't do it when layering.
fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight));
fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
fInitialTransform.preConcat(initialTransform);
fLegacyBitmap.setInfo(info);
SkIRect existingClip = info.bounds();
fExistingClipRegion.setRect(existingClip);
this->init();
}
// TODO(vandebo) change layerSize to SkSize.
SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
const SkClipStack& existingClipStack,
const SkRegion& existingClipRegion)
: fPageSize(layerSize)
, fContentSize(layerSize)
, fExistingClipStack(existingClipStack)
, fExistingClipRegion(existingClipRegion)
, fLastContentEntry(NULL)
, fLastMarginContentEntry(NULL)
, fClipStack(NULL)
, fRasterDpi(72.0f)
{
fInitialTransform.reset();
fLegacyBitmap.setInfo(make_content_info(layerSize, NULL));
this->init();
, fFontGlyphUsage(SkNEW(SkPDFGlyphSetMap))
, fRasterDpi(rasterDpi)
, fCanon(canon) {
SkASSERT(pageSize.width() > 0);
SkASSERT(pageSize.height() > 0);
fLegacyBitmap.setInfo(
SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()));
if (flip) {
// Skia generally uses the top left as the origin but PDF
// natively has the origin at the bottom left. This matrix
// corrects for that. But that only needs to be done once, we
// don't do it when layering.
fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight));
fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
} else {
fInitialTransform.setIdentity();
}
}
SkPDFDevice::~SkPDFDevice() {

View File

@ -23,6 +23,7 @@
#include "SkTemplates.h"
class SkPDFArray;
class SkPDFCanon;
class SkPDFDevice;
class SkPDFDict;
class SkPDFFont;
@ -47,28 +48,37 @@ struct NamedDestination;
*/
class SkPDFDevice : public SkBaseDevice {
public:
/** Create a PDF drawing context with the given width and height.
* 72 points/in means letter paper is 612x792.
* @param pageSize Page size in points.
* @param contentSize The content size of the page in points. This will be
* combined with the initial transform to determine the drawing area
* (as reported by the width and height methods). Anything outside
* of the drawing area will be clipped.
* @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 does an
* inverse scale+translate to accommodate the one that SkPDFDevice
* always does.
/** Create a PDF drawing context. SkPDFDevice applies a
* scale-and-translate transform to move the origin from the
* bottom left (PDF default) to the top left (Skia default).
* @param pageSize Page size in point units.
* 1 point == 127/360 mm == 1/72 inch
* @param rasterDpi the DPI at which features without native PDF
* support will be rasterized (e.g. draw image with
* perspective, draw text with perspective, ...). A
* larger DPI would create a PDF that reflects the
* original intent with better fidelity, but it can make
* for larger PDF files too, which would use more memory
* while rendering, and it would be slower to be processed
* or sent online or to printer. A good choice is
* SK_ScalarDefaultRasterDPI(72.0f).
* @param SkPDFCanon. Should be non-null, and shared by all
* devices in a document.
*/
// Deprecated, please use SkDocument::CreatePdf() instead.
SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
const SkMatrix& initialTransform);
SK_API virtual ~SkPDFDevice();
static SkPDFDevice* Create(SkISize pageSize,
SkScalar rasterDpi,
SkPDFCanon* canon) {
return SkNEW_ARGS(SkPDFDevice, (pageSize, rasterDpi, canon, true));
}
/** Create a PDF drawing context without fipping the y-axis. */
static SkPDFDevice* CreateUnflipped(SkISize pageSize,
SkScalar rasterDpi,
SkPDFCanon* canon) {
return SkNEW_ARGS(SkPDFDevice, (pageSize, rasterDpi, canon, false));
}
virtual ~SkPDFDevice();
/** These are called inside the per-device-layer loop for each draw call.
When these are called, we have already applied any saveLayer operations,
@ -173,21 +183,6 @@ public:
return *(fFontGlyphUsage.get());
}
/**
* rasterDpi - the DPI at which features without native PDF support
* will be rasterized (e.g. draw image with perspective,
* draw text with perspective, ...)
* A larger DPI would create a PDF that reflects the original
* intent with better fidelity, but it can make for larger
* PDF files too, which would use more memory while rendering,
* and it would be slower to be processed or sent online or
* to printer.
*/
void setRasterDpi(SkScalar rasterDpi) {
fRasterDpi = rasterDpi;
}
protected:
const SkBitmap& onAccessBitmap() SK_OVERRIDE {
return fLegacyBitmap;
@ -224,8 +219,6 @@ private:
// Accessor and setter functions based on the current DrawingArea.
SkAutoTDelete<ContentEntry>* getContentEntries();
ContentEntry* getLastContentEntry();
void setLastContentEntry(ContentEntry* contentEntry);
// Glyph ids used for each font on this device.
SkAutoTDelete<SkPDFGlyphSetMap> fFontGlyphUsage;
@ -234,8 +227,16 @@ private:
SkBitmap fLegacyBitmap;
SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
const SkRegion& existingClipRegion);
SkPDFCanon* fCanon; // Owned by SkDocument_PDF
////////////////////////////////////////////////////////////////////////////
SkPDFDevice(SkISize pageSize,
SkScalar rasterDpi,
SkPDFCanon* canon,
bool flip);
ContentEntry* getLastContentEntry();
void setLastContentEntry(ContentEntry* contentEntry);
// override from SkBaseDevice
SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE;

View File

@ -942,15 +942,11 @@ SkPDFImageShader* SkPDFImageShader::Create(
deviceBounds.join(bitmapBounds);
}
SkMatrix unflip;
unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height()));
unflip.preScale(SK_Scalar1, -SK_Scalar1);
SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
SkScalarRoundToInt(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);
SkAutoTUnref<SkPDFDevice> patternDevice(
SkPDFDevice::CreateUnflipped(size, 72.0f, NULL));
SkCanvas canvas(patternDevice.get());
SkRect patternBBox;
image->getBounds(&patternBBox);
@ -1108,14 +1104,16 @@ SkPDFImageShader* SkPDFImageShader::Create(
}
// Put the canvas into the pattern stream (fContent).
SkAutoTDelete<SkStream> content(pattern.content());
SkAutoTDelete<SkStream> content(patternDevice->content());
SkPDFImageShader* imageShader =
SkNEW_ARGS(SkPDFImageShader, (autoState->detach()));
imageShader->setData(content.get());
populate_tiling_pattern_dict(imageShader, patternBBox,
pattern.getResourceDict(), finalMatrix);
populate_tiling_pattern_dict(imageShader,
patternBBox,
patternDevice->getResourceDict(),
finalMatrix);
imageShader->fShaderState->fImage.unlockPixels();

View File

@ -8,6 +8,7 @@
#include "SkAnnotation.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkPDFCanon.h"
#include "SkPDFDevice.h"
#include "SkPDFDocument.h"
#include "Test.h"
@ -41,10 +42,9 @@ DEF_TEST(Annotation_NoDraw, reporter) {
DEF_TEST(Annotation_PdfLink, reporter) {
SkISize size = SkISize::Make(612, 792);
SkMatrix initialTransform;
initialTransform.reset();
SkPDFDevice device(size, size, initialTransform);
SkCanvas canvas(&device);
SkPDFCanon canon;
SkAutoTUnref<SkPDFDevice> device(SkPDFDevice::Create(size, 72.0f, &canon));
SkCanvas canvas(device.get());
SkRect r = SkRect::MakeXYWH(SkIntToScalar(72), SkIntToScalar(72),
SkIntToScalar(288), SkIntToScalar(72));
@ -52,7 +52,7 @@ DEF_TEST(Annotation_PdfLink, reporter) {
SkAnnotateRectWithURL(&canvas, r, data.get());
SkPDFDocument doc;
doc.appendPage(&device);
doc.appendPage(device.get());
SkDynamicMemoryWStream outStream;
doc.emitPDF(&outStream);
SkAutoDataUnref out(outStream.copyToData());
@ -63,17 +63,16 @@ DEF_TEST(Annotation_PdfLink, reporter) {
DEF_TEST(Annotation_NamedDestination, reporter) {
SkISize size = SkISize::Make(612, 792);
SkMatrix initialTransform;
initialTransform.reset();
SkPDFDevice device(size, size, initialTransform);
SkCanvas canvas(&device);
SkPDFCanon canon;
SkAutoTUnref<SkPDFDevice> device(SkPDFDevice::Create(size, 72.0f, &canon));
SkCanvas canvas(device.get());
SkPoint p = SkPoint::Make(SkIntToScalar(72), SkIntToScalar(72));
SkAutoDataUnref data(SkData::NewWithCString("example"));
SkAnnotateNamedDestination(&canvas, p, data.get());
SkPDFDocument doc;
doc.appendPage(&device);
doc.appendPage(device.get());
SkDynamicMemoryWStream outStream;
doc.emitPDF(&outStream);
SkAutoDataUnref out(outStream.copyToData());

View File

@ -49,6 +49,7 @@
#include "SkDevice.h"
#include "SkMatrix.h"
#include "SkNWayCanvas.h"
#include "SkPDFCanon.h"
#include "SkPDFDevice.h"
#include "SkPDFDocument.h"
#include "SkPaint.h"
@ -558,12 +559,14 @@ static void TestPdfDevice(skiatest::Reporter* reporter,
const TestData& d,
CanvasTestStep* testStep) {
SkISize pageSize = SkISize::Make(d.fWidth, d.fHeight);
SkPDFDevice device(pageSize, pageSize, SkMatrix::I());
SkCanvas canvas(&device);
SkPDFCanon canon;
SkAutoTUnref<SkPDFDevice> pdfDevice(
SkPDFDevice::Create(pageSize, 72.0f, &canon));
SkCanvas canvas(pdfDevice.get());
testStep->setAssertMessageFormat(kPdfAssertMessageFormat);
testStep->draw(&canvas, d, reporter);
SkPDFDocument doc;
doc.appendPage(&device);
doc.appendPage(pdfDevice.get());
SkDynamicMemoryWStream stream;
doc.emitPDF(&stream);
}

View File

@ -11,6 +11,7 @@
#include "SkFlate.h"
#include "SkImageEncoder.h"
#include "SkMatrix.h"
#include "SkPDFCanon.h"
#include "SkPDFCatalog.h"
#include "SkPDFDevice.h"
#include "SkPDFStream.h"
@ -215,8 +216,8 @@ static void TestSubstitute(skiatest::Reporter* reporter) {
// and there is no assert on input data in Debug mode.
static void test_issue1083() {
SkISize pageSize = SkISize::Make(100, 100);
SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
SkPDFCanon canon;
SkAutoTUnref<SkPDFDevice> dev(SkPDFDevice::Create(pageSize, 72.0f, &canon));
SkCanvas c(dev);
SkPaint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
@ -354,8 +355,10 @@ void DummyImageFilter::toString(SkString* str) const {
// CPU rasterization.
DEF_TEST(PDFImageFilter, reporter) {
SkISize pageSize = SkISize::Make(100, 100);
SkAutoTUnref<SkPDFDevice> device(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
SkCanvas canvas(device.get());
SkPDFCanon canon;
SkAutoTUnref<SkPDFDevice> pdfDevice(
SkPDFDevice::Create(pageSize, 72.0f, &canon));
SkCanvas canvas(pdfDevice.get());
SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter());
// Filter just created; should be unvisited.