151 lines
4.9 KiB
JavaScript
151 lines
4.9 KiB
JavaScript
|
var SVGParser = require('./core');
|
||
|
|
||
|
SVGParser.implement({
|
||
|
|
||
|
findLinkedAttributes: function(element, callback){
|
||
|
var self = this;
|
||
|
|
||
|
var cb = function(result){
|
||
|
var attributes = element.attributes;
|
||
|
for (var i = 0, l = attributes.length; i < l; i++){
|
||
|
var attribute = attributes[i];
|
||
|
result[attribute.nodeName] = attribute.nodeValue;
|
||
|
}
|
||
|
if (element.childNodes.length > 0) result.container = element;
|
||
|
callback.call(self, result);
|
||
|
};
|
||
|
|
||
|
var href = element.getAttribute('xlink:href') || element.getAttribute('href');
|
||
|
if (!href){ cb.call(this, {}); return; }
|
||
|
|
||
|
this.findByURL(element.ownerDocument, href, function(parent){
|
||
|
if (!parent) cb.call(this, {});
|
||
|
else this.findLinkedAttributes(parent, function(parentResult){
|
||
|
cb.call(this, parentResult);
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
|
||
|
getBBox: function(graphic){
|
||
|
var path = graphic.getPath && graphic.getPath();
|
||
|
return path ? path.measure() : graphic;
|
||
|
},
|
||
|
|
||
|
parseBBLength: function(value, bbox, dimension){
|
||
|
value = /%/.test(value) ? parseFloat(value) / 100 : parseFloat(value);
|
||
|
if (dimension == 'x') return (bbox.left || 0) + (bbox.width || 0) * value;
|
||
|
if (dimension == 'y') return (bbox.top || 0) + (bbox.height || 0) * value;
|
||
|
},
|
||
|
|
||
|
getGradientStops: function(element, styles){
|
||
|
var stops = null, node = element.firstChild;
|
||
|
while (node){
|
||
|
if (node.nodeName == 'stop'){
|
||
|
var stopStyles = this.parseStyles(node, styles);
|
||
|
var color = this.parseColor(stopStyles['stop-color'] || 'black', stopStyles['stop-opacity'], stopStyles),
|
||
|
offset = node.getAttribute('offset');
|
||
|
if (color && offset){
|
||
|
offset = /%/.test(offset) ? parseFloat(offset) / 100 : parseFloat(offset);
|
||
|
if (offset < 0) offset = 0;
|
||
|
if (offset > 1) offset = 1;
|
||
|
if (!stops) stops = {};
|
||
|
stops[offset.toFixed(4)] = color;
|
||
|
}
|
||
|
}
|
||
|
node = node.nextSibling;
|
||
|
}
|
||
|
return stops;
|
||
|
},
|
||
|
|
||
|
radialGradientFill: function(element, styles, target, x, y){
|
||
|
this.findLinkedAttributes(element, function(attrs){
|
||
|
if (!attrs.container) return;
|
||
|
|
||
|
var stops = this.getGradientStops(attrs.container, styles);
|
||
|
if (!stops) return;
|
||
|
|
||
|
// TODO: Transform
|
||
|
|
||
|
var cx = attrs.cx || '50%',
|
||
|
cy = attrs.cy || '50%',
|
||
|
rx = attrs.r || '50%', ry,
|
||
|
fx = attrs.fx || cx,
|
||
|
fy = attrs.fy || cy;
|
||
|
|
||
|
if (attrs['gradientUnits'] == 'userSpaceOnUse'){
|
||
|
cx = this.parseLength(cx, styles, 'x') - x;
|
||
|
cy = this.parseLength(cy, styles, 'y') - y;
|
||
|
rx = ry = this.parseLength(rx, styles);
|
||
|
fx = this.parseLength(fx, styles, 'x') - x;
|
||
|
fy = this.parseLength(fy, styles, 'y') - y;
|
||
|
} else {
|
||
|
var bb = this.getBBox(target);
|
||
|
cx = this.parseBBLength(cx, bb, 'x');
|
||
|
cy = this.parseBBLength(cy, bb, 'y');
|
||
|
rx = this.parseBBLength(rx, bb, 'x');
|
||
|
ry = rx * (bb.height / bb.width);
|
||
|
fx = this.parseBBLength(fx, bb, 'x');
|
||
|
fy = this.parseBBLength(fy, bb, 'y');
|
||
|
}
|
||
|
|
||
|
target.fillRadial(stops, fx, fy, rx, ry, cx, cy);
|
||
|
|
||
|
});
|
||
|
},
|
||
|
|
||
|
linearGradientFill: function(element, styles, target, x, y){
|
||
|
this.findLinkedAttributes(element, function(attrs){
|
||
|
if (!attrs.container) return;
|
||
|
|
||
|
var stops = this.getGradientStops(attrs.container, styles);
|
||
|
if (!stops) return;
|
||
|
|
||
|
var x1 = attrs.x1 || 0,
|
||
|
y1 = attrs.y1 || 0,
|
||
|
x2 = attrs.x2 || '100%',
|
||
|
y2 = attrs.y2 || 0;
|
||
|
|
||
|
// TODO: Transform
|
||
|
|
||
|
if (attrs['gradientUnits'] == 'userSpaceOnUse'){
|
||
|
x1 = this.parseLength(x1, styles, 'x') - x;
|
||
|
y1 = this.parseLength(y1, styles, 'y') - y;
|
||
|
x2 = this.parseLength(x2, styles, 'x') - x;
|
||
|
y2 = this.parseLength(y2, styles, 'y') - y;
|
||
|
} else {
|
||
|
x1 = /%/.test(x1) ? parseFloat(x1) / 100 : parseFloat(x1);
|
||
|
y1 = /%/.test(y1) ? parseFloat(y1) / 100 : parseFloat(y1);
|
||
|
x2 = /%/.test(x2) ? parseFloat(x2) / 100 : parseFloat(x2);
|
||
|
y2 = /%/.test(y2) ? parseFloat(y2) / 100 : parseFloat(y2);
|
||
|
|
||
|
// If both points are close to the opposite edges, use rotation angle instead
|
||
|
var closeToEdge =
|
||
|
(x1 > -0.01 && x1 < 0.01 && x2 > 0.99 && x2 < 1.01) ||
|
||
|
(x1 > 0.99 && x1 < 1.01 && x2 > -0.01 && x2 < 0.01) ||
|
||
|
(y1 > -0.01 && y1 < 0.01 && y2 > 0.99 && y2 < 1.01) ||
|
||
|
(y1 > 0.99 && y1 < 1.01 && y2 > -0.01 && y2 < 0.01);
|
||
|
|
||
|
if (closeToEdge){
|
||
|
var angle = Math.atan2(y1 - y2, x2 - x1) * 180 / Math.PI;
|
||
|
target.fillLinear(stops, angle);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// TODO: Always use rotation angle, but offset stops instead to adjust
|
||
|
// TODO2: Never use rotation angle because measuring is deprecated
|
||
|
var bb = this.getBBox(target);
|
||
|
x1 = this.parseBBLength(x1, bb, 'x');
|
||
|
y1 = this.parseBBLength(y1, bb, 'y');
|
||
|
x2 = this.parseBBLength(x2, bb, 'x');
|
||
|
y2 = this.parseBBLength(y2, bb, 'y');
|
||
|
}
|
||
|
target.fillLinear(stops, x1, y1, x2, y2);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
patternFill: function(element, styles, target, x, y){
|
||
|
// TODO: If the pattern is an image, fillImage
|
||
|
}
|
||
|
|
||
|
});
|