diff --git a/gyp/core.gypi b/gyp/core.gypi index 534c8884d4..a88f7223af 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 31b48dbe28..f89df933a4 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 25aa1dbf06..62bbbd08c1 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 0000000000..5ca0a5cd6e --- /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 0000000000..4298b3f595 --- /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 0000000000..3d9c46362d --- /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 668c3a993d..036b08a00e 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 surface(SkSurface::NewRaster(info)); + doDraw(surface->getCanvas(), paint, text); + + SkAutoTUnref 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 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 tool_main(int argc, char** argv) { 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 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 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