SkDocument base for pdf, xps, etc.
R=scroggo@google.com
Review URL: https://codereview.chromium.org/16660002
git-svn-id: http://skia.googlecode.com/svn/trunk@9476 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 534c888..a88f722 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -186,6 +186,8 @@
'<(skia_src_path)/core/SkWriter32.cpp',
'<(skia_src_path)/core/SkXfermode.cpp',
+ '<(skia_src_path)/doc/SkDocument.cpp',
+
'<(skia_src_path)/image/SkDataPixelRef.cpp',
'<(skia_src_path)/image/SkImage.cpp',
'<(skia_src_path)/image/SkImagePriv.cpp',
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
index 31b48db..f89df93 100644
--- a/gyp/pdf.gyp
+++ b/gyp/pdf.gyp
@@ -47,6 +47,8 @@
'../src/pdf/SkPDFUtils.cpp',
'../src/pdf/SkPDFUtils.h',
'../src/pdf/SkTSet.h',
+
+ '../src/doc/SkDocument_PDF.cpp',
],
# This section makes all targets that depend on this target
# #define SK_SUPPORT_PDF and have access to the pdf header files.
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 25aa1db..62bbbd0 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -64,6 +64,7 @@
],
'dependencies': [
'skia_lib.gyp:skia_lib',
+ 'pdf.gyp:pdf',
'flags.gyp:flags',
],
},
diff --git a/include/core/SkDocument.h b/include/core/SkDocument.h
new file mode 100644
index 0000000..5ca0a5c
--- /dev/null
+++ b/include/core/SkDocument.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDocument_DEFINED
+#define SkDocument_DEFINED
+
+#include "SkRect.h"
+#include "SkRefCnt.h"
+
+class SkCanvas;
+class SkWStream;
+
+/**
+ * High-level API for creating a document-based canvas. To use..
+ *
+ * 1. Create a document, specifying a stream to store the output.
+ * 2. For each "page" of content:
+ * a. canvas = doc->beginPage(...)
+ * b. draw_my_content(canvas);
+ * c. doc->endPage();
+ * 3. Close the document with doc->close().
+ */
+class SkDocument : public SkRefCnt {
+public:
+ /**
+ * Create a PDF-backed document, writing the results into a file.
+ * If there is an error trying to create the doc, returns NULL.
+ */
+ static SkDocument* CreatePDF(const char filename[]);
+
+ /**
+ * Create a PDF-backed document, writing the results into a stream.
+ * If there is an error trying to create the doc, returns NULL.
+ *
+ * The document may write to the stream at anytime during its lifetime,
+ * until either close() is called or the document is deleted. Once close()
+ * has been called, and all of the data has been written to the stream,
+ * if there is a Done proc provided, it will be called with the stream.
+ * The proc can delete the stream, or whatever it needs to do.
+ */
+ static SkDocument* CreatePDF(SkWStream*, void (*Done)(SkWStream*) = NULL);
+
+ /**
+ * Begin a new page for the document, returning the canvas that will draw
+ * into the page. The document owns this canvas, and it will go out of
+ * scope when endPage() or close() is called, or the document is deleted.
+ */
+ SkCanvas* beginPage(SkScalar width, SkScalar height,
+ const SkRect* content = NULL);
+
+ /**
+ * Call endPage() when the content for the current page has been drawn
+ * (into the canvas returned by beginPage()). After this call the canvas
+ * returned by beginPage() will be out-of-scope.
+ */
+ void endPage();
+
+ /**
+ * Call close() when all pages have been drawn. This will close the file
+ * or stream holding the document's contents. After close() the document
+ * can no longer add new pages. Deleting the document will automatically
+ * call close() if need be.
+ */
+ void close();
+
+protected:
+ SkDocument(SkWStream*, void (*)(SkWStream*));
+ // note: subclasses must call close() in their destructor, as the base class
+ // cannot do this for them.
+ virtual ~SkDocument();
+
+ virtual SkCanvas* onBeginPage(SkScalar width, SkScalar height,
+ const SkRect& content) = 0;
+ virtual void onEndPage() = 0;
+ virtual void onClose(SkWStream*) = 0;
+
+ enum State {
+ kBetweenPages_State,
+ kInPage_State,
+ kClosed_State
+ };
+ State getState() const { return fState; }
+
+private:
+ SkWStream* fStream;
+ void (*fDoneProc)(SkWStream*);
+ State fState;
+};
+
+#endif
diff --git a/src/doc/SkDocument.cpp b/src/doc/SkDocument.cpp
new file mode 100644
index 0000000..4298b3f
--- /dev/null
+++ b/src/doc/SkDocument.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDocument.h"
+#include "SkStream.h"
+
+SkDocument::SkDocument(SkWStream* stream, void (*doneProc)(SkWStream*)) {
+ fStream = stream; // we do not own this object.
+ fDoneProc = doneProc;
+ fState = kBetweenPages_State;
+}
+
+SkDocument::~SkDocument() {
+ this->close();
+}
+
+SkCanvas* SkDocument::beginPage(SkScalar width, SkScalar height,
+ const SkRect* content) {
+ if (width <= 0 || height <= 0) {
+ return NULL;
+ }
+
+ SkRect outer = SkRect::MakeWH(width, height);
+ SkRect inner;
+ if (content) {
+ inner = *content;
+ if (!inner.intersect(outer)) {
+ return NULL;
+ }
+ } else {
+ inner = outer;
+ }
+
+ for (;;) {
+ switch (fState) {
+ case kBetweenPages_State:
+ fState = kInPage_State;
+ return this->onBeginPage(width, height, inner);
+ case kInPage_State:
+ this->endPage();
+ break;
+ case kClosed_State:
+ return NULL;
+ }
+ }
+ SkASSERT(!"never get here");
+ return NULL;
+}
+
+void SkDocument::endPage() {
+ if (kInPage_State == fState) {
+ fState = kBetweenPages_State;
+ this->onEndPage();
+ }
+}
+
+void SkDocument::close() {
+ for (;;) {
+ switch (fState) {
+ case kBetweenPages_State:
+ fState = kClosed_State;
+ this->onClose(fStream);
+
+ if (fDoneProc) {
+ fDoneProc(fStream);
+ }
+ // we don't own the stream, but we mark it NULL since we can
+ // no longer write to it.
+ fStream = NULL;
+ return;
+ case kInPage_State:
+ this->endPage();
+ break;
+ case kClosed_State:
+ return;
+ }
+ }
+}
+
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp
new file mode 100644
index 0000000..3d9c463
--- /dev/null
+++ b/src/doc/SkDocument_PDF.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDocument.h"
+#include "SkPDFDocument.h"
+#include "SkPDFDevice.h"
+
+class SkDocument_PDF : public SkDocument {
+public:
+ SkDocument_PDF(SkWStream* stream, void (*doneProc)(SkWStream*))
+ : SkDocument(stream, doneProc) {
+ fDoc = SkNEW(SkPDFDocument);
+ fCanvas = NULL;
+ fDevice = NULL;
+ }
+
+ virtual ~SkDocument_PDF() {
+ // subclasses must call close() in their destructors
+ this->close();
+ }
+
+protected:
+ virtual SkCanvas* onBeginPage(SkScalar width, SkScalar height,
+ const SkRect& content) SK_OVERRIDE {
+ SkASSERT(NULL == fCanvas);
+ SkASSERT(NULL == fDevice);
+
+ SkISize pageS, contentS;
+ SkMatrix matrix;
+
+ pageS.set(SkScalarRoundToInt(width), SkScalarRoundToInt(height));
+ contentS.set(SkScalarRoundToInt(content.width()),
+ SkScalarRoundToInt(content.height()));
+ matrix.setTranslate(content.fLeft, content.fTop);
+
+ fDevice = SkNEW_ARGS(SkPDFDevice, (pageS, contentS, matrix));
+ fCanvas = SkNEW_ARGS(SkCanvas, (fDevice));
+ return fCanvas;
+ }
+
+ virtual void onEndPage() SK_OVERRIDE {
+ SkASSERT(fCanvas);
+ SkASSERT(fDevice);
+
+ fCanvas->flush();
+ fDoc->appendPage(fDevice);
+
+ fCanvas->unref();
+ fDevice->unref();
+
+ fCanvas = NULL;
+ fDevice = NULL;
+ }
+
+ virtual void onClose(SkWStream* stream) SK_OVERRIDE {
+ SkASSERT(NULL == fCanvas);
+ SkASSERT(NULL == fDevice);
+
+ fDoc->emitPDF(stream);
+ SkDELETE(fDoc);
+ fDoc = NULL;
+ }
+
+private:
+ SkPDFDocument* fDoc;
+ SkPDFDevice* fDevice;
+ SkCanvas* fCanvas;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDocument* SkDocument::CreatePDF(SkWStream* stream, void (*done)(SkWStream*)) {
+ return stream ? SkNEW_ARGS(SkDocument_PDF, (stream, done)) : NULL;
+}
+
+static void delete_wstream(SkWStream* stream) {
+ SkDELETE(stream);
+}
+
+SkDocument* SkDocument::CreatePDF(const char path[]) {
+ SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path));
+ if (!stream->isValid()) {
+ SkDELETE(stream);
+ return NULL;
+ }
+ return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream));
+}
+
diff --git a/tools/skhello.cpp b/tools/skhello.cpp
index 668c3a9..036b08a 100644
--- a/tools/skhello.cpp
+++ b/tools/skhello.cpp
@@ -8,22 +8,62 @@
#include "SkCanvas.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
+#include "SkDocument.h"
#include "SkGraphics.h"
#include "SkSurface.h"
#include "SkImage.h"
#include "SkStream.h"
#include "SkString.h"
-DEFINE_string2(outFile, o, "skhello.png", "The filename to write the image.");
+DEFINE_string2(outFile, o, "skhello", "The filename to write the image.");
DEFINE_string2(text, t, "Hello", "The string to write.");
+static void doDraw(SkCanvas* canvas, const SkPaint& paint, const char text[]) {
+ SkRect bounds;
+ canvas->getClipBounds(&bounds);
+
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->drawText(text, strlen(text),
+ bounds.centerX(), bounds.centerY(),
+ paint);
+}
+
+static bool do_surface(int w, int h, const char path[], const char text[],
+ const SkPaint& paint) {
+ SkImage::Info info = {
+ w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
+ };
+ SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
+ doDraw(surface->getCanvas(), paint, text);
+
+ SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
+ SkAutoDataUnref data(image->encode());
+ if (NULL == data.get()) {
+ return false;
+ }
+ SkFILEWStream stream(path);
+ return stream.write(data->data(), data->size());
+}
+
+static bool do_document(int w, int h, const char path[], const char text[],
+ const SkPaint& paint) {
+ SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path));
+ if (doc.get()) {
+ SkScalar width = SkIntToScalar(w);
+ SkScalar height = SkIntToScalar(h);
+ doDraw(doc->beginPage(width, height, NULL), paint, text);
+ return true;
+ }
+ return false;
+}
+
int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
SkCommandLineFlags::SetUsage("");
SkCommandLineFlags::Parse(argc, argv);
SkAutoGraphics ag;
- SkString path("skhello.png");
+ SkString path("skhello");
SkString text("Hello");
if (!FLAGS_outFile.isEmpty()) {
@@ -44,24 +84,23 @@
int w = SkScalarRound(width) + 30;
int h = SkScalarRound(spacing) + 30;
- SkImage::Info info = {
- w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
+ static const struct {
+ bool (*fProc)(int w, int h, const char path[], const char text[],
+ const SkPaint&);
+ const char* fSuffix;
+ } gRec[] = {
+ { do_surface, ".png" },
+ { do_document, ".pdf" },
};
- SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
- SkCanvas* canvas = surface->getCanvas();
-
- canvas->drawColor(SK_ColorWHITE);
- canvas->drawText(text.c_str(), text.size(),
- SkIntToScalar(w)/2, SkIntToScalar(h)*2/3,
- paint);
-
- SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
- SkAutoDataUnref data(image->encode());
- if (NULL == data.get()) {
- return -1;
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+ SkString file;
+ file.printf("%s%s", path.c_str(), gRec[i].fSuffix);
+ if (!gRec[i].fProc(w, h, file.c_str(), text.c_str(), paint)) {
+ return -1;
+ }
}
- SkFILEWStream stream(path.c_str());
- return stream.write(data->data(), data->size());
+ return 0;
}
#if !defined SK_BUILD_FOR_IOS