GT2/GT2-Android/node_modules/art/parsers/svg/paints.js

151 lines
4.9 KiB
JavaScript
Raw Normal View History

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
}
});