mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
424 lines
14 KiB
JavaScript
424 lines
14 KiB
JavaScript
(function(){
|
|
var Util = _html2canvas.Util,
|
|
Generate = {};
|
|
|
|
_html2canvas.Generate = Generate;
|
|
|
|
var reGradients = [
|
|
/^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
|
|
/^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
|
|
/^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/,
|
|
/^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/,
|
|
/^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/,
|
|
/^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/,
|
|
/^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/
|
|
];
|
|
|
|
/*
|
|
* TODO: Add IE10 vendor prefix (-ms) support
|
|
* TODO: Add W3C gradient (linear-gradient) support
|
|
* TODO: Add old Webkit -webkit-gradient(radial, ...) support
|
|
* TODO: Maybe some RegExp optimizations are possible ;o)
|
|
*/
|
|
Generate.parseGradient = function(css, bounds) {
|
|
var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl;
|
|
|
|
for(i = 0; i < len; i+=1){
|
|
m1 = css.match(reGradients[i]);
|
|
if(m1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(m1) {
|
|
switch(m1[1]) {
|
|
case '-webkit-linear-gradient':
|
|
case '-o-linear-gradient':
|
|
|
|
gradient = {
|
|
type: 'linear',
|
|
x0: null,
|
|
y0: null,
|
|
x1: null,
|
|
y1: null,
|
|
colorStops: []
|
|
};
|
|
|
|
// get coordinates
|
|
m2 = m1[2].match(/\w+/g);
|
|
if(m2){
|
|
m2Len = m2.length;
|
|
for(i = 0; i < m2Len; i+=1){
|
|
switch(m2[i]) {
|
|
case 'top':
|
|
gradient.y0 = 0;
|
|
gradient.y1 = bounds.height;
|
|
break;
|
|
|
|
case 'right':
|
|
gradient.x0 = bounds.width;
|
|
gradient.x1 = 0;
|
|
break;
|
|
|
|
case 'bottom':
|
|
gradient.y0 = bounds.height;
|
|
gradient.y1 = 0;
|
|
break;
|
|
|
|
case 'left':
|
|
gradient.x0 = 0;
|
|
gradient.x1 = bounds.width;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(gradient.x0 === null && gradient.x1 === null){ // center
|
|
gradient.x0 = gradient.x1 = bounds.width / 2;
|
|
}
|
|
if(gradient.y0 === null && gradient.y1 === null){ // center
|
|
gradient.y0 = gradient.y1 = bounds.height / 2;
|
|
}
|
|
|
|
// get colors and stops
|
|
m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
|
|
if(m2){
|
|
m2Len = m2.length;
|
|
step = 1 / Math.max(m2Len - 1, 1);
|
|
for(i = 0; i < m2Len; i+=1){
|
|
m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
|
|
if(m3[2]){
|
|
stop = parseFloat(m3[2]);
|
|
if(m3[3] === '%'){
|
|
stop /= 100;
|
|
} else { // px - stupid opera
|
|
stop /= bounds.width;
|
|
}
|
|
} else {
|
|
stop = i * step;
|
|
}
|
|
gradient.colorStops.push({
|
|
color: m3[1],
|
|
stop: stop
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '-webkit-gradient':
|
|
|
|
gradient = {
|
|
type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions
|
|
x0: 0,
|
|
y0: 0,
|
|
x1: 0,
|
|
y1: 0,
|
|
colorStops: []
|
|
};
|
|
|
|
// get coordinates
|
|
m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
|
|
if(m2){
|
|
gradient.x0 = (m2[1] * bounds.width) / 100;
|
|
gradient.y0 = (m2[2] * bounds.height) / 100;
|
|
gradient.x1 = (m2[3] * bounds.width) / 100;
|
|
gradient.y1 = (m2[4] * bounds.height) / 100;
|
|
}
|
|
|
|
// get colors and stops
|
|
m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g);
|
|
if(m2){
|
|
m2Len = m2.length;
|
|
for(i = 0; i < m2Len; i+=1){
|
|
m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/);
|
|
stop = parseFloat(m3[2]);
|
|
if(m3[1] === 'from') {
|
|
stop = 0.0;
|
|
}
|
|
if(m3[1] === 'to') {
|
|
stop = 1.0;
|
|
}
|
|
gradient.colorStops.push({
|
|
color: m3[3],
|
|
stop: stop
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '-moz-linear-gradient':
|
|
|
|
gradient = {
|
|
type: 'linear',
|
|
x0: 0,
|
|
y0: 0,
|
|
x1: 0,
|
|
y1: 0,
|
|
colorStops: []
|
|
};
|
|
|
|
// get coordinates
|
|
m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
|
|
|
|
// m2[1] == 0% -> left
|
|
// m2[1] == 50% -> center
|
|
// m2[1] == 100% -> right
|
|
|
|
// m2[2] == 0% -> top
|
|
// m2[2] == 50% -> center
|
|
// m2[2] == 100% -> bottom
|
|
|
|
if(m2){
|
|
gradient.x0 = (m2[1] * bounds.width) / 100;
|
|
gradient.y0 = (m2[2] * bounds.height) / 100;
|
|
gradient.x1 = bounds.width - gradient.x0;
|
|
gradient.y1 = bounds.height - gradient.y0;
|
|
}
|
|
|
|
// get colors and stops
|
|
m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
|
|
if(m2){
|
|
m2Len = m2.length;
|
|
step = 1 / Math.max(m2Len - 1, 1);
|
|
for(i = 0; i < m2Len; i+=1){
|
|
m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
|
|
if(m3[2]){
|
|
stop = parseFloat(m3[2]);
|
|
if(m3[3]){ // percentage
|
|
stop /= 100;
|
|
}
|
|
} else {
|
|
stop = i * step;
|
|
}
|
|
gradient.colorStops.push({
|
|
color: m3[1],
|
|
stop: stop
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '-webkit-radial-gradient':
|
|
case '-moz-radial-gradient':
|
|
case '-o-radial-gradient':
|
|
|
|
gradient = {
|
|
type: 'circle',
|
|
x0: 0,
|
|
y0: 0,
|
|
x1: bounds.width,
|
|
y1: bounds.height,
|
|
cx: 0,
|
|
cy: 0,
|
|
rx: 0,
|
|
ry: 0,
|
|
colorStops: []
|
|
};
|
|
|
|
// center
|
|
m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
|
|
if(m2){
|
|
gradient.cx = (m2[1] * bounds.width) / 100;
|
|
gradient.cy = (m2[2] * bounds.height) / 100;
|
|
}
|
|
|
|
// size
|
|
m2 = m1[3].match(/\w+/);
|
|
m3 = m1[4].match(/[a-z\-]*/);
|
|
if(m2 && m3){
|
|
switch(m3[0]){
|
|
case 'farthest-corner':
|
|
case 'cover': // is equivalent to farthest-corner
|
|
case '': // mozilla removes "cover" from definition :(
|
|
tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
|
|
tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
|
|
br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
|
|
bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
|
|
gradient.rx = gradient.ry = Math.max(tl, tr, br, bl);
|
|
break;
|
|
case 'closest-corner':
|
|
tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
|
|
tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
|
|
br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
|
|
bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
|
|
gradient.rx = gradient.ry = Math.min(tl, tr, br, bl);
|
|
break;
|
|
case 'farthest-side':
|
|
if(m2[0] === 'circle'){
|
|
gradient.rx = gradient.ry = Math.max(
|
|
gradient.cx,
|
|
gradient.cy,
|
|
gradient.x1 - gradient.cx,
|
|
gradient.y1 - gradient.cy
|
|
);
|
|
} else { // ellipse
|
|
|
|
gradient.type = m2[0];
|
|
|
|
gradient.rx = Math.max(
|
|
gradient.cx,
|
|
gradient.x1 - gradient.cx
|
|
);
|
|
gradient.ry = Math.max(
|
|
gradient.cy,
|
|
gradient.y1 - gradient.cy
|
|
);
|
|
}
|
|
break;
|
|
case 'closest-side':
|
|
case 'contain': // is equivalent to closest-side
|
|
if(m2[0] === 'circle'){
|
|
gradient.rx = gradient.ry = Math.min(
|
|
gradient.cx,
|
|
gradient.cy,
|
|
gradient.x1 - gradient.cx,
|
|
gradient.y1 - gradient.cy
|
|
);
|
|
} else { // ellipse
|
|
|
|
gradient.type = m2[0];
|
|
|
|
gradient.rx = Math.min(
|
|
gradient.cx,
|
|
gradient.x1 - gradient.cx
|
|
);
|
|
gradient.ry = Math.min(
|
|
gradient.cy,
|
|
gradient.y1 - gradient.cy
|
|
);
|
|
}
|
|
break;
|
|
|
|
// TODO: add support for "30px 40px" sizes (webkit only)
|
|
}
|
|
}
|
|
|
|
// color stops
|
|
m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
|
|
if(m2){
|
|
m2Len = m2.length;
|
|
step = 1 / Math.max(m2Len - 1, 1);
|
|
for(i = 0; i < m2Len; i+=1){
|
|
m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
|
|
if(m3[2]){
|
|
stop = parseFloat(m3[2]);
|
|
if(m3[3] === '%'){
|
|
stop /= 100;
|
|
} else { // px - stupid opera
|
|
stop /= bounds.width;
|
|
}
|
|
} else {
|
|
stop = i * step;
|
|
}
|
|
gradient.colorStops.push({
|
|
color: m3[1],
|
|
stop: stop
|
|
});
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return gradient;
|
|
};
|
|
|
|
function addScrollStops(grad) {
|
|
return function(colorStop) {
|
|
try {
|
|
grad.addColorStop(colorStop.stop, colorStop.color);
|
|
}
|
|
catch(e) {
|
|
Util.log(['failed to add color stop: ', e, '; tried to add: ', colorStop]);
|
|
}
|
|
};
|
|
}
|
|
|
|
Generate.Gradient = function(src, bounds) {
|
|
if(bounds.width === 0 || bounds.height === 0) {
|
|
return;
|
|
}
|
|
|
|
var canvas = document.createElement('canvas'),
|
|
ctx = canvas.getContext('2d'),
|
|
gradient, grad;
|
|
|
|
canvas.width = bounds.width;
|
|
canvas.height = bounds.height;
|
|
|
|
// TODO: add support for multi defined background gradients
|
|
gradient = _html2canvas.Generate.parseGradient(src, bounds);
|
|
|
|
if(gradient) {
|
|
switch(gradient.type) {
|
|
case 'linear':
|
|
grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);
|
|
gradient.colorStops.forEach(addScrollStops(grad));
|
|
ctx.fillStyle = grad;
|
|
ctx.fillRect(0, 0, bounds.width, bounds.height);
|
|
break;
|
|
|
|
case 'circle':
|
|
grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx);
|
|
gradient.colorStops.forEach(addScrollStops(grad));
|
|
ctx.fillStyle = grad;
|
|
ctx.fillRect(0, 0, bounds.width, bounds.height);
|
|
break;
|
|
|
|
case 'ellipse':
|
|
var canvasRadial = document.createElement('canvas'),
|
|
ctxRadial = canvasRadial.getContext('2d'),
|
|
ri = Math.max(gradient.rx, gradient.ry),
|
|
di = ri * 2;
|
|
|
|
canvasRadial.width = canvasRadial.height = di;
|
|
|
|
grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri);
|
|
gradient.colorStops.forEach(addScrollStops(grad));
|
|
|
|
ctxRadial.fillStyle = grad;
|
|
ctxRadial.fillRect(0, 0, di, di);
|
|
|
|
ctx.fillStyle = gradient.colorStops[gradient.colorStops.length - 1].color;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return canvas;
|
|
};
|
|
|
|
Generate.ListAlpha = function(number) {
|
|
var tmp = "",
|
|
modulus;
|
|
|
|
do {
|
|
modulus = number % 26;
|
|
tmp = String.fromCharCode((modulus) + 64) + tmp;
|
|
number = number / 26;
|
|
}while((number*26) > 26);
|
|
|
|
return tmp;
|
|
};
|
|
|
|
Generate.ListRoman = function(number) {
|
|
var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
|
|
decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
|
|
roman = "",
|
|
v,
|
|
len = romanArray.length;
|
|
|
|
if (number <= 0 || number >= 4000) {
|
|
return number;
|
|
}
|
|
|
|
for (v=0; v < len; v+=1) {
|
|
while (number >= decimal[v]) {
|
|
number -= decimal[v];
|
|
roman += romanArray[v];
|
|
}
|
|
}
|
|
|
|
return roman;
|
|
};
|
|
})(); |