Merge pull request #31 from ConradIrwin/commonjs

Make canvas2svg work with commonjs and jsdom
This commit is contained in:
Gabriel Hernandez 2015-09-09 12:05:36 -07:00
commit 92bed4b9d6
6 changed files with 110 additions and 51 deletions

View File

@ -80,6 +80,24 @@ You should now be able to select your new example from playground.html or see it
If you find a bug, or want to add functionality, please add a new test case and include it with your pull request. If you find a bug, or want to add functionality, please add a new test case and include it with your pull request.
Using with node.js
==================
You can use `canvas2svg` with node.js using [jsdom](https://github.com/tmpvar/jsdom) with [node-canvas](https://github.com/Automattic/node-canvas). To do this first create a new document object, and then create a new instance of `C2S` based on that document:
```javascript
var canvas = require('canvas'),
jsdom = require('jsdom'),
C2S = require('canvas2svg');
var document = jsdom.jsdom();
var ctx = new C2S({document: document});
// ... drawing code goes here ...
```
N.B. You may not need node-canvas for some simple operations when using jsdom >= 6.3.0, but it's still recommended that you install it.
Updates Updates
========== ==========
- v1.0.15 Setup travis, add testharness and debug playground, and fix regression for __createElement refactor - v1.0.15 Setup travis, add testharness and debug playground, and fix regression for __createElement refactor
@ -103,6 +121,17 @@ Misc
========== ==========
Some canvas 2d context methods are not implemented yet. Watch out for setTransform and arcTo. Some canvas 2d context methods are not implemented yet. Watch out for setTransform and arcTo.
Releasing
=========
To release a new version:
* Run `gulp bump` to update the version number
* Add a new entry to the [Updates](#Updates) table
* `git commit -am v1.0.xx`
* `git push`
* `npm publish`
License License
========== ==========
This library is licensed under the MIT license. This library is licensed under the MIT license.

5
bower.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "canvas2svg",
"version": "1.0.14",
"main": "./canvas2svg.js"
}

View File

@ -72,29 +72,6 @@
return mapping[textBaseline] || mapping.alphabetic; return mapping[textBaseline] || mapping.alphabetic;
} }
/**
* Creates the specified svg element
* @private
*/
function createElement(elementName, properties, resetFill) {
if (typeof properties === "undefined") {
properties = {};
}
var element = document.createElementNS("http://www.w3.org/2000/svg", elementName),
keys = Object.keys(properties), i, key;
if(resetFill) {
//if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black.
element.setAttribute("fill", "none");
element.setAttribute("stroke", "none");
}
for(i=0; i<keys.length; i++) {
key = keys[i];
element.setAttribute(key, properties[key]);
}
return element;
}
// Unpack entities lookup where the numbers are in radix 32 to reduce the size // Unpack entities lookup where the numbers are in radix 32 to reduce the size
// entity mapping courtesy of tinymce // entity mapping courtesy of tinymce
namedEntities = createNamedToNumberedLookup( namedEntities = createNamedToNumberedLookup(
@ -198,15 +175,16 @@
* @param gradientNode - reference to the gradient * @param gradientNode - reference to the gradient
* @constructor * @constructor
*/ */
CanvasGradient = function(gradientNode) { CanvasGradient = function(gradientNode, ctx) {
this.__root = gradientNode; this.__root = gradientNode;
this.__ctx = ctx;
}; };
/** /**
* Adds a color stop to the gradient root * Adds a color stop to the gradient root
*/ */
CanvasGradient.prototype.addColorStop = function(offset, color) { CanvasGradient.prototype.addColorStop = function(offset, color) {
var stop = createElement("stop"), regex, matches; var stop = this.__ctx.__createElement("stop"), regex, matches;
stop.setAttribute("offset", offset); stop.setAttribute("offset", offset);
if(color.indexOf("rgba") !== -1) { if(color.indexOf("rgba") !== -1) {
//separate alpha value, since webkit can't handle it //separate alpha value, since webkit can't handle it
@ -231,10 +209,11 @@
* width - width of your canvas (defaults to 500) * width - width of your canvas (defaults to 500)
* height - height of your canvas (defaults to 500) * height - height of your canvas (defaults to 500)
* enableMirroring - enables canvas mirroring (get image data) (defaults to false) * enableMirroring - enables canvas mirroring (get image data) (defaults to false)
* document - the document object (defaults to the current document)
*/ */
ctx = function(o) { ctx = function(o) {
var defaultOptions = { width:500, height:500, enableMirroring : false }, options; var defaultOptions = { width:500, height:500, enableMirroring : false}, options;
//keep support for this way of calling C2S: new C2S(width,height) //keep support for this way of calling C2S: new C2S(width,height)
if(arguments.length > 1) { if(arguments.length > 1) {
@ -258,7 +237,8 @@
this.enableMirroring = options.enableMirroring !== undefined ? options.enableMirroring : defaultOptions.enableMirroring; this.enableMirroring = options.enableMirroring !== undefined ? options.enableMirroring : defaultOptions.enableMirroring;
this.canvas = this; ///point back to this instance! this.canvas = this; ///point back to this instance!
this.__canvas = document.createElement("canvas"); this.__document = options.document || document;
this.__canvas = this.__document.createElement("canvas");
this.__ctx = this.__canvas.getContext("2d"); this.__ctx = this.__canvas.getContext("2d");
this.__setDefaultStyles(); this.__setDefaultStyles();
@ -266,7 +246,7 @@
this.__groupStack = []; this.__groupStack = [];
//the root svg element //the root svg element
this.__root = document.createElementNS("http://www.w3.org/2000/svg", "svg"); this.__root = this.__document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.__root.setAttribute("version", 1.1); this.__root.setAttribute("version", 1.1);
this.__root.setAttribute("xmlns", "http://www.w3.org/2000/svg"); this.__root.setAttribute("xmlns", "http://www.w3.org/2000/svg");
this.__root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); this.__root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
@ -277,14 +257,38 @@
this.__ids = {}; this.__ids = {};
//defs tag //defs tag
this.__defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); this.__defs = this.__document.createElementNS("http://www.w3.org/2000/svg", "defs");
this.__root.appendChild(this.__defs); this.__root.appendChild(this.__defs);
//also add a group child. the svg element can't use the transform attribute //also add a group child. the svg element can't use the transform attribute
this.__currentElement = document.createElementNS("http://www.w3.org/2000/svg", "g"); this.__currentElement = this.__document.createElementNS("http://www.w3.org/2000/svg", "g");
this.__root.appendChild(this.__currentElement); this.__root.appendChild(this.__currentElement);
}; };
/**
* Creates the specified svg element
* @private
*/
ctx.prototype.__createElement = function (elementName, properties, resetFill) {
if (typeof properties === "undefined") {
properties = {};
}
var element = this.__document.createElementNS("http://www.w3.org/2000/svg", elementName),
keys = Object.keys(properties), i, key;
if(resetFill) {
//if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black.
element.setAttribute("fill", "none");
element.setAttribute("stroke", "none");
}
for(i=0; i<keys.length; i++) {
key = keys[i];
element.setAttribute(key, properties[key]);
}
return element;
};
/** /**
* Applies default canvas styles to the context * Applies default canvas styles to the context
* @private * @private
@ -426,7 +430,7 @@
* Will generate a group tag. * Will generate a group tag.
*/ */
ctx.prototype.save = function() { ctx.prototype.save = function() {
var group = createElement("g"), parent = this.__closestGroupOrSvg(); var group = this.__createElement("g"), parent = this.__closestGroupOrSvg();
this.__groupStack.push(parent); this.__groupStack.push(parent);
parent.appendChild(group); parent.appendChild(group);
this.__currentElement = group; this.__currentElement = group;
@ -451,7 +455,7 @@
//if the current element has siblings, add another group //if the current element has siblings, add another group
var parent = this.__closestGroupOrSvg(); var parent = this.__closestGroupOrSvg();
if(parent.childNodes.length > 0) { if(parent.childNodes.length > 0) {
var group = createElement("g"); var group = this.__createElement("g");
parent.appendChild(group); parent.appendChild(group);
this.__currentElement = group; this.__currentElement = group;
} }
@ -509,7 +513,7 @@
this.__currentDefaultPath = ""; this.__currentDefaultPath = "";
this.__currentPosition = {}; this.__currentPosition = {};
path = createElement("path", {}, true); path = this.__createElement("path", {}, true);
parent = this.__closestGroupOrSvg(); parent = this.__closestGroupOrSvg();
parent.appendChild(path); parent.appendChild(path);
this.__currentElement = path; this.__currentElement = path;
@ -732,7 +736,7 @@
*/ */
ctx.prototype.fillRect = function(x, y, width, height){ ctx.prototype.fillRect = function(x, y, width, height){
var rect, parent; var rect, parent;
rect = createElement("rect", { rect = this.__createElement("rect", {
x : x, x : x,
y : y, y : y,
width : width, width : width,
@ -753,7 +757,7 @@
*/ */
ctx.prototype.strokeRect = function(x, y, width, height){ ctx.prototype.strokeRect = function(x, y, width, height){
var rect, parent; var rect, parent;
rect = createElement("rect", { rect = this.__createElement("rect", {
x : x, x : x,
y : y, y : y,
width : width, width : width,
@ -771,7 +775,7 @@
*/ */
ctx.prototype.clearRect = function(x, y, width, height) { ctx.prototype.clearRect = function(x, y, width, height) {
var rect, parent = this.__closestGroupOrSvg(); var rect, parent = this.__closestGroupOrSvg();
rect = createElement("rect", { rect = this.__createElement("rect", {
x : x, x : x,
y : y, y : y,
width : width, width : width,
@ -786,7 +790,7 @@
* Returns a canvas gradient object that has a reference to it's parent def * Returns a canvas gradient object that has a reference to it's parent def
*/ */
ctx.prototype.createLinearGradient = function(x1, y1, x2, y2){ ctx.prototype.createLinearGradient = function(x1, y1, x2, y2){
var grad = createElement("linearGradient", { var grad = this.__createElement("linearGradient", {
id : randomString(this.__ids), id : randomString(this.__ids),
x1 : x1+"px", x1 : x1+"px",
x2 : x2+"px", x2 : x2+"px",
@ -795,7 +799,7 @@
"gradientUnits" : "userSpaceOnUse" "gradientUnits" : "userSpaceOnUse"
}, false); }, false);
this.__defs.appendChild(grad); this.__defs.appendChild(grad);
return new CanvasGradient(grad); return new CanvasGradient(grad, this);
}; };
/** /**
@ -803,7 +807,7 @@
* Returns a canvas gradient object that has a reference to it's parent def * Returns a canvas gradient object that has a reference to it's parent def
*/ */
ctx.prototype.createRadialGradient = function(x0, y0, r0, x1, y1, r1){ ctx.prototype.createRadialGradient = function(x0, y0, r0, x1, y1, r1){
var grad = createElement("radialGradient", { var grad = this.__createElement("radialGradient", {
id : randomString(this.__ids), id : randomString(this.__ids),
cx : x1+"px", cx : x1+"px",
cy : y1+"px", cy : y1+"px",
@ -813,7 +817,7 @@
"gradientUnits" : "userSpaceOnUse" "gradientUnits" : "userSpaceOnUse"
}, false); }, false);
this.__defs.appendChild(grad); this.__defs.appendChild(grad);
return new CanvasGradient(grad); return new CanvasGradient(grad, this);
}; };
@ -855,7 +859,7 @@
*/ */
ctx.prototype.__wrapTextLink = function(font, element) { ctx.prototype.__wrapTextLink = function(font, element) {
if(font.href) { if(font.href) {
var a = createElement("a"); var a = this.__createElement("a");
a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", font.href); a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", font.href);
a.appendChild(element); a.appendChild(element);
return a; return a;
@ -874,7 +878,7 @@
ctx.prototype.__applyText = function(text, x, y, action) { ctx.prototype.__applyText = function(text, x, y, action) {
var font = this.__parseFont(), var font = this.__parseFont(),
parent = this.__closestGroupOrSvg(), parent = this.__closestGroupOrSvg(),
textElement = createElement("text", { textElement = this.__createElement("text", {
"font-family" : font.family, "font-family" : font.family,
"font-size" : font.size, "font-size" : font.size,
"font-style" : font.style, "font-style" : font.style,
@ -886,7 +890,7 @@
"dominant-baseline": getDominantBaseline(this.textBaseline) "dominant-baseline": getDominantBaseline(this.textBaseline)
}, true); }, true);
textElement.appendChild(document.createTextNode(text)); textElement.appendChild(this.__document.createTextNode(text));
this.__currentElement = textElement; this.__currentElement = textElement;
this.__applyStyleToCurrentElement(action); this.__applyStyleToCurrentElement(action);
parent.appendChild(this.__wrapTextLink(font,textElement)); parent.appendChild(this.__wrapTextLink(font,textElement));
@ -963,9 +967,9 @@
*/ */
ctx.prototype.clip = function(){ ctx.prototype.clip = function(){
var group = this.__closestGroupOrSvg(), var group = this.__closestGroupOrSvg(),
clipPath = createElement("clipPath"), clipPath = this.__createElement("clipPath"),
id = randomString(this.__ids), id = randomString(this.__ids),
newGroup = createElement("g"); newGroup = this.__createElement("g");
group.removeChild(this.__currentElement); group.removeChild(this.__currentElement);
clipPath.setAttribute("id", id); clipPath.setAttribute("id", id);
@ -1043,14 +1047,14 @@
this.__currentElement = currentElement; this.__currentElement = currentElement;
} else if(image.nodeName === "CANVAS" || image.nodeName === "IMG") { } else if(image.nodeName === "CANVAS" || image.nodeName === "IMG") {
//canvas or image //canvas or image
svgImage = createElement("image"); svgImage = this.__createElement("image");
svgImage.setAttribute("width", dw); svgImage.setAttribute("width", dw);
svgImage.setAttribute("height", dh); svgImage.setAttribute("height", dh);
svgImage.setAttribute("preserveAspectRatio", "none"); svgImage.setAttribute("preserveAspectRatio", "none");
if(sx || sy || sw !== image.width || sh !== image.height) { if(sx || sy || sw !== image.width || sh !== image.height) {
//crop the image using a temporary canvas //crop the image using a temporary canvas
canvas = document.createElement("canvas"); canvas = this.__document.createElement("canvas");
canvas.width = dw; canvas.width = dw;
canvas.height = dh; canvas.height = dh;
context = canvas.getContext("2d"); context = canvas.getContext("2d");
@ -1071,13 +1075,13 @@
* Generates a pattern tag * Generates a pattern tag
*/ */
ctx.prototype.createPattern = function(image, repetition){ ctx.prototype.createPattern = function(image, repetition){
var pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern"), id = randomString(this.__ids), var pattern = this.__document.createElementNS("http://www.w3.org/2000/svg", "pattern"), id = randomString(this.__ids),
img; img;
pattern.setAttribute("id", id); pattern.setAttribute("id", id);
pattern.setAttribute("width", image.width); pattern.setAttribute("width", image.width);
pattern.setAttribute("height", image.height); pattern.setAttribute("height", image.height);
if(image.nodeName === "CANVAS" || image.nodeName === "IMG") { if(image.nodeName === "CANVAS" || image.nodeName === "IMG") {
img = document.createElementNS("http://www.w3.org/2000/svg", "image"); img = this.__document.createElementNS("http://www.w3.org/2000/svg", "image");
img.setAttribute("width", image.width); img.setAttribute("width", image.width);
img.setAttribute("height", image.height); img.setAttribute("height", image.height);
img.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", img.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href",
@ -1102,6 +1106,13 @@
ctx.prototype.setTransform = function(){}; ctx.prototype.setTransform = function(){};
//add options for alternative namespace //add options for alternative namespace
window.C2S = ctx; if (typeof window === "object") {
window.C2S = ctx;
}
// CommonJS/Browserify
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = ctx;
}
}()); }());

View File

@ -4,6 +4,7 @@ var gulp = require('gulp');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var cheerio = require('cheerio'); var cheerio = require('cheerio');
var bump = require('gulp-bump');
function updateExample(filename) { function updateExample(filename) {
@ -28,4 +29,10 @@ gulp.task('update_examples', function() {
updateExample('test/testrunner.html'); updateExample('test/testrunner.html');
}); });
gulp.task('default', ['update_examples']); gulp.task('bump', function() {
gulp.src(["./package.json", "./bower.json"])
.pipe(bump({type:'patch'}))
.pipe(gulp.dest('./'));
});
gulp.task('default', ['update_examples']);

View File

@ -22,6 +22,7 @@
"chai": "^2.1.1", "chai": "^2.1.1",
"cheerio": "^0.19.0", "cheerio": "^0.19.0",
"gulp": "^3.9.0", "gulp": "^3.9.0",
"gulp-bump": "^0.3.1",
"karma": "^0.12.36", "karma": "^0.12.36",
"karma-chai": "^0.1.0", "karma-chai": "^0.1.0",
"karma-chrome-launcher": "^0.1.12", "karma-chrome-launcher": "^0.1.12",

View File

@ -46,6 +46,12 @@ describe('canvas2svg', function() {
}); });
it("can be created on another document", function () {
var otherDoc = document.implementation.createHTMLDocument();
var ctx = C2S({document: otherDoc});
expect(ctx.getSvg().ownerDocument).to.equal(otherDoc);
});
}); });
describe("can export to", function() { describe("can export to", function() {