Browse Source

Offer SVG output.

v1.x
Thaddee Tyl 10 years ago
parent
commit
79f1d42fb5
  1. 1
      .gitignore
  2. 11
      Readme.md
  3. 22
      examples/small-svg.js
  4. 35
      src/Canvas.cc
  5. 4
      src/Canvas.h
  6. 2
      test/canvas.test.js

1
.gitignore

@ -7,6 +7,7 @@ examples/*.jpg
testing
out.png
out.pdf
out.svg
.pomo
node_modules

11
Readme.md

@ -250,6 +250,17 @@ ctx.fillText('Hello World 3', 50, 80);
ctx.addPage();
```
## SVG support
Just like PDF support, make sure to install cairo with `--enable-svg=yes`.
You also need to tell node-canvas that it is working on SVG upon its initialization:
```js
var canvas = new Canvas(200, 500, 'svg');
// Use the normal primitives.
fs.writeFile('out.svg', canvas.toBuffer());
```
## Benchmarks
Although node-canvas is extremely new, and we have not even begun optimization yet it is already quite fast. For benchmarks vs other node canvas implementations view this [gist](https://gist.github.com/664922), or update the submodules and run `$ make benchmark` yourself.

22
examples/small-svg.js

@ -0,0 +1,22 @@
var Canvas = require('../')
, canvas = new Canvas(400, 200, 'svg')
, ctx = canvas.getContext('2d')
, fs = require('fs');
var y = 80
, x = 50;
ctx.font = '22px Helvetica';
ctx.fillText('node-canvas SVG', x, y);
ctx.font = '10px Arial';
ctx.fillText('Just a quick example of SVGs with node-canvas', x, y += 20);
ctx.globalAlpha = .5;
ctx.fillRect(x, y += 20, 10, 10);
ctx.fillRect(x += 20, y, 10, 10);
ctx.fillRect(x += 20, y, 10, 10);
fs.writeFile('out.svg', canvas.toBuffer());
console.log('created out.svg');

35
src/Canvas.cc

@ -13,6 +13,7 @@
#include <node_buffer.h>
#include <node_version.h>
#include <cairo/cairo-pdf.h>
#include <cairo/cairo-svg.h>
#include "closure.h"
#ifdef HAVE_JPEG
@ -69,6 +70,8 @@ NAN_METHOD(Canvas::New) {
if (args[1]->IsNumber()) height = args[1]->Uint32Value();
if (args[2]->IsString()) type = !strcmp("pdf", *String::Utf8Value(args[2]))
? CANVAS_TYPE_PDF
: !strcmp("svg", *String::Utf8Value(args[2]))
? CANVAS_TYPE_SVG
: CANVAS_TYPE_IMAGE;
Canvas *canvas = new Canvas(width, height, type);
canvas->Wrap(args.This());
@ -82,7 +85,7 @@ NAN_METHOD(Canvas::New) {
NAN_GETTER(Canvas::GetType) {
NanScope();
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
NanReturnValue(NanNew<String>(canvas->isPDF() ? "pdf" : "image"));
NanReturnValue(NanNew<String>(canvas->isPDF() ? "pdf" : canvas->isSVG() ? "svg" : "image"));
}
/*
@ -238,7 +241,7 @@ NAN_METHOD(Canvas::ToBuffer) {
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
// TODO: async / move this out
if (canvas->isPDF()) {
if (canvas->isPDF() || canvas->isSVG()) {
cairo_surface_finish(canvas->surface());
closure_t *closure = (closure_t *) canvas->closure();
@ -480,6 +483,12 @@ Canvas::Canvas(int w, int h, canvas_type_t t): ObjectWrap() {
cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS);
assert(status == CAIRO_STATUS_SUCCESS);
_surface = cairo_pdf_surface_create_for_stream(toBuffer, _closure, w, h);
} else if (CANVAS_TYPE_SVG == t) {
_closure = malloc(sizeof(closure_t));
assert(_closure);
cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS);
assert(status == CAIRO_STATUS_SUCCESS);
_surface = cairo_svg_surface_create_for_stream(toBuffer, _closure, w, h);
} else {
_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
assert(_surface);
@ -494,6 +503,7 @@ Canvas::Canvas(int w, int h, canvas_type_t t): ObjectWrap() {
Canvas::~Canvas() {
switch (type) {
case CANVAS_TYPE_PDF:
case CANVAS_TYPE_SVG:
cairo_surface_finish(_surface);
closure_destroy((closure_t *) _closure);
free(_closure);
@ -512,10 +522,29 @@ Canvas::~Canvas() {
void
Canvas::resurface(Handle<Object> canvas) {
NanScope();
Handle<Value> context;
switch (type) {
case CANVAS_TYPE_PDF:
cairo_pdf_surface_set_size(_surface, width, height);
break;
case CANVAS_TYPE_SVG:
// Re-surface
cairo_surface_finish(_surface);
closure_destroy((closure_t *) _closure);
cairo_surface_destroy(_surface);
closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS);
_surface = cairo_svg_surface_create_for_stream(toBuffer, _closure, width, height);
// Reset context
context = canvas->Get(NanNew<String>("context"));
if (!context->IsUndefined()) {
Context2d *context2d = ObjectWrap::Unwrap<Context2d>(context->ToObject());
cairo_t *prev = context2d->context();
context2d->setContext(cairo_create(surface()));
cairo_destroy(prev);
}
break;
case CANVAS_TYPE_IMAGE:
// Re-surface
int old_width = cairo_image_surface_get_width(_surface);
@ -525,7 +554,7 @@ Canvas::resurface(Handle<Object> canvas) {
NanAdjustExternalMemory(4 * (width * height - old_width * old_height));
// Reset context
Handle<Value> context = canvas->Get(NanNew<String>("context"));
context = canvas->Get(NanNew<String>("context"));
if (!context->IsUndefined()) {
Context2d *context2d = ObjectWrap::Unwrap<Context2d>(context->ToObject());
cairo_t *prev = context2d->context();

4
src/Canvas.h

@ -39,7 +39,8 @@ using namespace node;
typedef enum {
CANVAS_TYPE_IMAGE,
CANVAS_TYPE_PDF
CANVAS_TYPE_PDF,
CANVAS_TYPE_SVG
} canvas_type_t;
/*
@ -78,6 +79,7 @@ class Canvas: public node::ObjectWrap {
#endif
inline bool isPDF(){ return CANVAS_TYPE_PDF == type; }
inline bool isSVG(){ return CANVAS_TYPE_SVG == type; }
inline cairo_surface_t *surface(){ return _surface; }
inline void *closure(){ return _closure; }
inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); }

2
test/canvas.test.js

@ -165,6 +165,8 @@ module.exports = {
assert('image' == canvas.type);
var canvas = new Canvas(10, 10, 'pdf');
assert('pdf' == canvas.type);
var canvas = new Canvas(10, 10, 'svg');
assert('svg' == canvas.type);
var canvas = new Canvas(10, 10, 'hey');
assert('image' == canvas.type);
},

Loading…
Cancel
Save