Add svg rendering with fabric.js

This commit is contained in:
Niklas von Hertzen 2014-09-04 20:50:31 +03:00
parent 2e2d722e3d
commit ba9d5201cf
10 changed files with 22925 additions and 99 deletions

View File

@ -14,6 +14,6 @@
"globals": {
"jQuery": true
},
"predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise", "getBounds", "offsetBounds",
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "GradientContainer", "LinearGradientContainer", "WebkitGradientContainer", "log", "smallImage", "parseBackgrounds"]
"predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise", "getBounds", "offsetBounds", "XHR",
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "Font", "FontMetrics", "GradientContainer", "LinearGradientContainer", "WebkitGradientContainer", "SVGContainer", "html2canvas", "log", "smallImage", "parseBackgrounds"]
}

View File

@ -9,7 +9,7 @@ module.exports = function(grunt) {
' Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>' +
'\n\n Released under <%= _.pluck(pkg.licenses, "type").join(", ") %> License\n*/\n',
pre: '\n(function(window, document, undefined){\n\n',
post: '\n})(window,document);'
post: '\n})(window, document);'
};
// Project configuration.
@ -23,13 +23,23 @@ module.exports = function(grunt) {
concat: {
dist: {
src: [
'src/promise.js', 'src/fallback.js', 'src/**/*.js'
'src/promise.js', 'src/fallback.js', 'src/*.js', 'src/renderers/*.js'
],
dest: 'dist/<%= pkg.name %>.js'
dest: 'dist/<%= pkg.name %>.js',
options:{
banner: meta.banner + meta.pre,
footer: meta.post
}
},
options:{
banner: meta.banner + meta.pre,
footer: meta.post
svg: {
src: [
'src/fabric/dist/fabric.js'
],
dest: 'dist/<%= pkg.name %>.svg.js',
options:{
banner: meta.banner + '\n(function(window, document, exports, undefined){\n\n',
footer: '\n})(window, document, html2canvas);'
}
}
},
connect: {
@ -73,6 +83,10 @@ module.exports = function(grunt) {
src: ['<%= concat.dist.dest %>'],
dest: 'dist/<%= pkg.name %>.min.js'
},
svg: {
src: ['<%= concat.svg.dest %>'],
dest: 'dist/<%= pkg.name %>.svg.min.js'
},
options: {
banner: meta.banner
}
@ -82,7 +96,7 @@ module.exports = function(grunt) {
tasks: ['jshint', 'build']
},
jshint: {
all: ['src/**/*.js', '!src/promise.js'],
all: ['src/*.js', 'src/renderers/*.js', '!src/promise.js'],
options: grunt.file.readJSON('./.jshintrc')
},
webdriver: {

217
dist/html2canvas.js vendored
View File

@ -323,6 +323,8 @@ ImageLoader.prototype.loadImage = function(imageData) {
var src = imageData.args[0];
if (src.match(/data:image\/.*;base64,/i)) {
return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
} else if (/(.+).svg$/i.test(src)) {
return new SVGContainer(src);
} else if (this.isSameOrigin(src) || this.options.allowTaint === true) {
return new ImageContainer(src, false);
} else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {
@ -1676,6 +1678,133 @@ Renderer.prototype.isTransparent = function(color) {
return (!color || color === "transparent" || color === "rgba(0, 0, 0, 0)");
};
function StackingContext(hasOwnStacking, opacity, element, parent) {
NodeContainer.call(this, element, parent);
this.ownStacking = hasOwnStacking;
this.contexts = [];
this.children = [];
this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity;
}
StackingContext.prototype = Object.create(NodeContainer.prototype);
StackingContext.prototype.getParentStack = function(context) {
var parentStack = (this.parent) ? this.parent.stack : null;
return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack;
};
function Support(document) {
this.rangeBounds = this.testRangeBounds(document);
this.cors = this.testCORS();
}
Support.prototype.testRangeBounds = function(document) {
var range, testElement, rangeBounds, rangeHeight, support = false;
if (document.createRange) {
range = document.createRange();
if (range.getBoundingClientRect) {
testElement = document.createElement('boundtest');
testElement.style.height = "123px";
testElement.style.display = "block";
document.body.appendChild(testElement);
range.selectNode(testElement);
rangeBounds = range.getBoundingClientRect();
rangeHeight = rangeBounds.height;
if (rangeHeight === 123) {
support = true;
}
document.body.removeChild(testElement);
}
}
return support;
};
Support.prototype.testCORS = function() {
return typeof((new Image()).crossOrigin) !== "undefined";
};
function SVGContainer(src) {
this.src = src;
this.image = null;
var self = this;
this.promise = XHR(src).then(function(svg) {
return new Promise(function(resolve) {
html2canvas.fabric.loadSVGFromString(svg, function (objects, options) {
var canvas = new html2canvas.fabric.StaticCanvas('c');
self.image = canvas.lowerCanvasEl;
canvas
.setWidth(options.width)
.setHeight(options.height)
.add(html2canvas.fabric.util.groupSVGElements(objects, options))
.renderAll();
resolve(canvas.lowerCanvasEl);
});
});
});
}
function TextContainer(node, parent) {
NodeContainer.call(this, node, parent);
}
TextContainer.prototype = Object.create(NodeContainer.prototype);
TextContainer.prototype.applyTextTransform = function() {
this.node.data = this.transform(this.parent.css("textTransform"));
};
TextContainer.prototype.transform = function(transform) {
var text = this.node.data;
switch(transform){
case "lowercase":
return text.toLowerCase();
case "capitalize":
return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize);
case "uppercase":
return text.toUpperCase();
default:
return text;
}
};
function capitalize(m, p1, p2) {
if (m.length > 0) {
return p1 + p2.toUpperCase();
}
}
function WebkitGradientContainer(imageData) {
GradientContainer.apply(this, arguments);
this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL;
}
WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);
function XHR(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(Error(xhr.statusText));
}
};
xhr.onerror = function() {
reject(Error("Network Error"));
};
xhr.send();
});
}
function CanvasRenderer(width, height) {
Renderer.apply(this, arguments);
this.canvas = document.createElement("canvas");
@ -1826,90 +1955,4 @@ CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
return canvas;
};
function StackingContext(hasOwnStacking, opacity, element, parent) {
NodeContainer.call(this, element, parent);
this.ownStacking = hasOwnStacking;
this.contexts = [];
this.children = [];
this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity;
}
StackingContext.prototype = Object.create(NodeContainer.prototype);
StackingContext.prototype.getParentStack = function(context) {
var parentStack = (this.parent) ? this.parent.stack : null;
return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack;
};
function Support(document) {
this.rangeBounds = this.testRangeBounds(document);
this.cors = this.testCORS();
}
Support.prototype.testRangeBounds = function(document) {
var range, testElement, rangeBounds, rangeHeight, support = false;
if (document.createRange) {
range = document.createRange();
if (range.getBoundingClientRect) {
testElement = document.createElement('boundtest');
testElement.style.height = "123px";
testElement.style.display = "block";
document.body.appendChild(testElement);
range.selectNode(testElement);
rangeBounds = range.getBoundingClientRect();
rangeHeight = rangeBounds.height;
if (rangeHeight === 123) {
support = true;
}
document.body.removeChild(testElement);
}
}
return support;
};
Support.prototype.testCORS = function() {
return typeof((new Image()).crossOrigin) !== "undefined";
};
function TextContainer(node, parent) {
NodeContainer.call(this, node, parent);
}
TextContainer.prototype = Object.create(NodeContainer.prototype);
TextContainer.prototype.applyTextTransform = function() {
this.node.data = this.transform(this.parent.css("textTransform"));
};
TextContainer.prototype.transform = function(transform) {
var text = this.node.data;
switch(transform){
case "lowercase":
return text.toLowerCase();
case "capitalize":
return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize);
case "uppercase":
return text.toUpperCase();
default:
return text;
}
};
function capitalize(m, p1, p2) {
if (m.length > 0) {
return p1 + p2.toUpperCase();
}
}
function WebkitGradientContainer(imageData) {
GradientContainer.apply(this, arguments);
this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL;
}
WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);
})(window,document);
})(window, document);

File diff suppressed because one or more lines are too long

22713
dist/html2canvas.svg.js vendored Normal file

File diff suppressed because it is too large Load Diff

13
dist/html2canvas.svg.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,8 @@ ImageLoader.prototype.loadImage = function(imageData) {
var src = imageData.args[0];
if (src.match(/data:image\/.*;base64,/i)) {
return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
} else if (/(.+).svg$/i.test(src)) {
return new SVGContainer(src);
} else if (this.isSameOrigin(src) || this.options.allowTaint === true) {
return new ImageContainer(src, false);
} else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {

19
src/svgcontainer.js Normal file
View File

@ -0,0 +1,19 @@
function SVGContainer(src) {
this.src = src;
this.image = null;
var self = this;
this.promise = XHR(src).then(function(svg) {
return new Promise(function(resolve) {
html2canvas.fabric.loadSVGFromString(svg, function (objects, options) {
var canvas = new html2canvas.fabric.StaticCanvas('c');
self.image = canvas.lowerCanvasEl;
canvas
.setWidth(options.width)
.setHeight(options.height)
.add(html2canvas.fabric.util.groupSVGElements(objects, options))
.renderAll();
resolve(canvas.lowerCanvasEl);
});
});
});
}

20
src/xhr.js Normal file
View File

@ -0,0 +1,20 @@
function XHR(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(Error(xhr.statusText));
}
};
xhr.onerror = function() {
reject(Error("Network Error"));
};
xhr.send();
});
}

View File

@ -12,10 +12,12 @@ var h2cSelector, h2cOptions;
}
var sources = ['log', 'nodecontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', 'dummyimagecontainer', 'proxyimagecontainer', 'gradientcontainer',
'lineargradientcontainer', 'webkitgradientcontainer', 'imageloader', 'nodeparser', 'font', 'fontmetrics', 'core', 'renderer', 'promise', 'renderers/canvas'];
'lineargradientcontainer', 'webkitgradientcontainer', 'svgcontainer', 'imageloader', 'nodeparser', 'font', 'fontmetrics', 'core', 'renderer', 'promise', 'xhr', 'renderers/canvas'];
['/tests/assets/jquery-1.6.2'].concat(window.location.search === "?selenium" ? ['/dist/html2canvas'] : sources.map(function(src) { return '/src/' + src; })).forEach(appendScript);
appendScript('/dist/html2canvas.svg');
window.onload = function() {
(function( $ ){
$.fn.html2canvas = function(options) {