2012-09-30 17:51:50 -04:00
|
|
|
/*
|
2013-01-28 21:03:27 -05:00
|
|
|
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
|
2012-11-02 20:47:14 -04:00
|
|
|
* http://paperjs.org/
|
|
|
|
*
|
2013-01-28 21:03:27 -05:00
|
|
|
* Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
|
2012-11-02 20:47:14 -04:00
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
|
|
|
*
|
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2012-09-13 20:45:27 -04:00
|
|
|
/**
|
2012-11-06 16:07:18 -05:00
|
|
|
* A function scope holding all the functionality needed to convert a SVG DOM
|
|
|
|
* to a Paper.js DOM.
|
2012-11-02 20:47:14 -04:00
|
|
|
*/
|
2012-11-06 16:07:18 -05:00
|
|
|
new function() {
|
2012-11-02 22:16:23 -04:00
|
|
|
// Define a couple of helper functions to easily read values from SVG
|
|
|
|
// objects, dealing with baseVal, and item lists.
|
|
|
|
// index is option, and if passed, causes a lookup in a list.
|
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function getValue(node, key, allowNull, index) {
|
|
|
|
// node[key].baseVal will even be set if the node did not define the
|
2012-11-10 14:19:06 -05:00
|
|
|
// attribute, so if allowNull is true, we need to also check
|
2013-02-10 22:37:19 -05:00
|
|
|
// node.getAttribute(key) == null
|
|
|
|
var base = (!allowNull || node.getAttribute(key) != null)
|
|
|
|
&& node[key] && node[key].baseVal;
|
2012-11-10 14:19:06 -05:00
|
|
|
// Note: String values are unfortunately not stored in base.value, but
|
|
|
|
// in base directly, so we need to check both, also on item lists, using
|
|
|
|
// Base.pick(base.value, base)
|
|
|
|
return base
|
2012-11-10 09:55:09 -05:00
|
|
|
? index !== undefined
|
2013-02-28 17:32:34 -05:00
|
|
|
// Item list? Look up by index:
|
2012-11-10 14:19:06 -05:00
|
|
|
? index < base.numberOfItems
|
|
|
|
? Base.pick((base = base.getItem(index)).value, base)
|
|
|
|
: null
|
|
|
|
: Base.pick(base.value, base)
|
|
|
|
: null;
|
2012-11-02 22:16:23 -04:00
|
|
|
}
|
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function getPoint(node, x, y, allowNull, index) {
|
|
|
|
x = getValue(node, x, allowNull, index);
|
|
|
|
y = getValue(node, y, allowNull, index);
|
2012-11-10 14:30:34 -05:00
|
|
|
return allowNull && x == null && y == null ? null
|
|
|
|
: Point.create(x || 0, y || 0);
|
2012-11-02 22:16:23 -04:00
|
|
|
}
|
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function getSize(node, w, h, allowNull, index) {
|
|
|
|
w = getValue(node, w, allowNull, index);
|
|
|
|
h = getValue(node, h, allowNull, index);
|
2012-11-10 14:30:34 -05:00
|
|
|
return allowNull && w == null && h == null ? null
|
|
|
|
: Size.create(w || 0, h || 0);
|
2012-11-02 22:16:23 -04:00
|
|
|
}
|
|
|
|
|
2012-11-08 12:38:42 -05:00
|
|
|
// Converts a string attribute value to the specified type
|
|
|
|
function convertValue(value, type) {
|
|
|
|
return value === 'none'
|
2012-11-08 11:31:23 -05:00
|
|
|
? null
|
|
|
|
: type === 'number'
|
2013-02-28 17:32:34 -05:00
|
|
|
? parseFloat(value)
|
2012-11-08 11:31:23 -05:00
|
|
|
: type === 'array'
|
2013-02-15 00:09:44 -05:00
|
|
|
? value ? value.split(/[\s,]+/g).map(parseFloat) : []
|
2012-11-08 11:31:23 -05:00
|
|
|
: type === 'color' && getDefinition(value)
|
2012-11-08 12:38:42 -05:00
|
|
|
|| value;
|
2012-11-08 08:32:09 -05:00
|
|
|
}
|
|
|
|
|
2012-11-10 18:05:13 -05:00
|
|
|
function createClipGroup(item, clip) {
|
|
|
|
clip.setClipMask(true);
|
|
|
|
return new Group(clip, item);
|
2012-11-10 09:56:56 -05:00
|
|
|
}
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
// Importer functions for various SVG node types
|
2012-11-02 22:16:23 -04:00
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function importGroup(node, type) {
|
|
|
|
var nodes = node.childNodes,
|
2012-11-06 23:28:20 -05:00
|
|
|
compound = type === 'clippath',
|
2013-02-15 00:42:50 -05:00
|
|
|
group = compound ? new CompoundPath() : new Group(),
|
|
|
|
project = group._project,
|
|
|
|
currentStyle = project._currentStyle;
|
|
|
|
// Style on groups needs to be handled differently than all other items:
|
|
|
|
// We first apply the style to the group, then use it as the project's
|
|
|
|
// currentStyle, so it is used as a default for the creation of all
|
|
|
|
// nested items. importSvg then needs to check for groups and avoid
|
|
|
|
// calling applyAttributes() again.
|
|
|
|
applyAttributes(group, node);
|
|
|
|
project._currentStyle = group._style.clone();
|
2012-11-06 13:29:14 -05:00
|
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
2012-11-06 23:28:20 -05:00
|
|
|
var child = nodes[i],
|
|
|
|
item;
|
|
|
|
if (child.nodeType == 1 && (item = importSvg(child))) {
|
|
|
|
// If adding CompoundPaths to other CompoundPaths,
|
2012-11-06 21:38:09 -05:00
|
|
|
// we need to "unbox" them first:
|
2012-11-06 23:28:20 -05:00
|
|
|
if (compound && item instanceof CompoundPath) {
|
|
|
|
group.addChildren(item.removeChildren());
|
|
|
|
item.remove();
|
2012-11-08 11:22:32 -05:00
|
|
|
} else if (!(item instanceof Symbol)) {
|
2012-11-06 23:28:20 -05:00
|
|
|
group.addChild(item);
|
2012-11-06 12:14:17 -05:00
|
|
|
}
|
2012-11-02 19:51:42 -04:00
|
|
|
}
|
2012-09-30 17:51:50 -04:00
|
|
|
}
|
2013-02-15 00:42:50 -05:00
|
|
|
// Restore currentStyle
|
|
|
|
project._currentStyle = currentStyle;
|
2012-11-07 11:21:02 -05:00
|
|
|
if (type == 'defs') {
|
|
|
|
// I don't think we need to add defs to the DOM. But we might want
|
|
|
|
// to use Symbols for them?
|
|
|
|
group.remove();
|
2012-11-07 11:28:09 -05:00
|
|
|
group = null;
|
2012-11-07 11:21:02 -05:00
|
|
|
}
|
2012-11-06 23:28:20 -05:00
|
|
|
return group;
|
2012-11-02 21:40:41 -04:00
|
|
|
}
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function importPoly(node, type) {
|
2012-11-03 01:53:33 -04:00
|
|
|
var path = new Path(),
|
2013-02-10 22:37:19 -05:00
|
|
|
points = node.points;
|
2012-11-06 14:28:50 -05:00
|
|
|
path.moveTo(points.getItem(0));
|
2012-11-02 22:10:58 -04:00
|
|
|
for (var i = 1, l = points.numberOfItems; i < l; i++)
|
2012-11-03 01:53:33 -04:00
|
|
|
path.lineTo(points.getItem(i));
|
2012-11-05 21:27:13 -05:00
|
|
|
if (type === 'polygon')
|
2012-11-03 01:53:33 -04:00
|
|
|
path.closePath();
|
|
|
|
return path;
|
2012-11-02 21:40:41 -04:00
|
|
|
}
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function importPath(node) {
|
2013-02-28 22:13:46 -05:00
|
|
|
// Get the path data, and determine wether it is a compound path or a
|
|
|
|
// normal path based on the amount of moveTo commands inside it.
|
|
|
|
var data = node.getAttribute('d'),
|
|
|
|
path = data.match(/m/gi).length > 1
|
|
|
|
? new CompoundPath()
|
|
|
|
: new Path();
|
|
|
|
path.setPathData(data);
|
|
|
|
return path;
|
2012-11-06 13:28:34 -05:00
|
|
|
}
|
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function importGradient(node, type) {
|
|
|
|
var nodes = node.childNodes,
|
2012-11-07 13:31:36 -05:00
|
|
|
stops = [];
|
|
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
2013-02-11 17:36:44 -05:00
|
|
|
var child = nodes[i];
|
|
|
|
if (child.nodeType == 1)
|
|
|
|
stops.push(applyAttributes(new GradientStop(), child));
|
2012-11-07 13:31:36 -05:00
|
|
|
}
|
|
|
|
var gradient = new Gradient(stops),
|
|
|
|
isRadial = type == 'radialgradient',
|
|
|
|
origin, destination, highlight;
|
|
|
|
if (isRadial) {
|
|
|
|
gradient.type = 'radial';
|
2013-02-10 22:37:19 -05:00
|
|
|
origin = getPoint(node, 'cx', 'cy');
|
|
|
|
destination = origin.add(getValue(node, 'r'), 0);
|
|
|
|
highlight = getPoint(node, 'fx', 'fy', true);
|
2012-11-07 13:31:36 -05:00
|
|
|
} else {
|
2013-02-10 22:37:19 -05:00
|
|
|
origin = getPoint(node, 'x1', 'y1');
|
|
|
|
destination = getPoint(node, 'x2', 'y2');
|
2012-11-07 13:31:36 -05:00
|
|
|
}
|
2012-11-10 18:48:15 -05:00
|
|
|
// We don't return the GradientColor, since we only need a reference to
|
|
|
|
// it in definitions, which is created in applyAttributes()
|
|
|
|
applyAttributes(
|
2013-02-10 22:37:19 -05:00
|
|
|
new GradientColor(gradient, origin, destination, highlight), node);
|
2012-11-10 18:48:15 -05:00
|
|
|
return null;
|
2012-11-07 13:31:36 -05:00
|
|
|
}
|
|
|
|
|
2012-11-02 21:40:41 -04:00
|
|
|
var importers = {
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/struct.html#Groups
|
2012-11-02 21:40:41 -04:00
|
|
|
g: importGroup,
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/struct.html#NewDocument
|
2012-11-02 21:40:41 -04:00
|
|
|
svg: importGroup,
|
2012-11-06 13:29:14 -05:00
|
|
|
clippath: importGroup,
|
2012-11-06 13:28:34 -05:00
|
|
|
// http://www.w3.org/TR/SVG/shapes.html#PolygonElement
|
|
|
|
polygon: importPoly,
|
|
|
|
// http://www.w3.org/TR/SVG/shapes.html#PolylineElement
|
|
|
|
polyline: importPoly,
|
|
|
|
// http://www.w3.org/TR/SVG/paths.html
|
|
|
|
path: importPath,
|
2012-11-08 18:01:13 -05:00
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#LinearGradients
|
|
|
|
lineargradient: importGradient,
|
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#RadialGradients
|
|
|
|
radialgradient: importGradient,
|
2012-11-06 12:14:17 -05:00
|
|
|
|
2012-12-02 13:41:29 -05:00
|
|
|
// http://www.w3.org/TR/SVG/struct.html#ImageElement
|
2013-02-10 22:37:19 -05:00
|
|
|
image: function (node) {
|
|
|
|
var raster = new Raster(getValue(node, 'href'));
|
2012-12-03 00:08:57 -05:00
|
|
|
raster.attach('load', function() {
|
2013-02-10 22:37:19 -05:00
|
|
|
var size = getSize(node, 'width', 'height');
|
2012-12-02 13:41:29 -05:00
|
|
|
this.setSize(size);
|
|
|
|
// Since x and y start from the top left of an image, add
|
|
|
|
// half of its size:
|
2013-02-10 22:37:19 -05:00
|
|
|
this.translate(getPoint(node, 'x', 'y').add(size.divide(2)));
|
2012-12-03 00:08:57 -05:00
|
|
|
});
|
2012-12-02 13:41:29 -05:00
|
|
|
return raster;
|
|
|
|
},
|
|
|
|
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/struct.html#SymbolElement
|
2013-02-10 22:37:19 -05:00
|
|
|
symbol: function(node, type) {
|
2013-02-15 00:42:50 -05:00
|
|
|
return new Symbol(importGroup(node, type));
|
2012-11-06 12:11:54 -05:00
|
|
|
},
|
2012-11-06 12:14:17 -05:00
|
|
|
|
|
|
|
// http://www.w3.org/TR/SVG/struct.html#DefsElement
|
2012-11-07 11:21:02 -05:00
|
|
|
defs: importGroup,
|
2012-11-07 11:21:35 -05:00
|
|
|
|
|
|
|
// http://www.w3.org/TR/SVG/struct.html#UseElement
|
2013-02-10 22:37:19 -05:00
|
|
|
use: function(node, type) {
|
2012-11-10 14:19:06 -05:00
|
|
|
// Note the namespaced xlink:href attribute is just called href
|
2013-02-10 22:37:19 -05:00
|
|
|
// as a property on node.
|
|
|
|
var id = (getValue(node, 'href') || '').substring(1),
|
2012-11-10 09:56:56 -05:00
|
|
|
definition = definitions[id];
|
|
|
|
// Use place if we're dealing with a symbol:
|
2012-11-10 14:19:06 -05:00
|
|
|
return definition
|
|
|
|
? definition instanceof Symbol
|
|
|
|
? definition.place()
|
|
|
|
: definition.clone()
|
|
|
|
: null;
|
2012-11-06 12:14:17 -05:00
|
|
|
},
|
|
|
|
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/shapes.html#InterfaceSVGCircleElement
|
2013-02-10 22:37:19 -05:00
|
|
|
circle: function(node) {
|
|
|
|
return new Path.Circle(getPoint(node, 'cx', 'cy'),
|
|
|
|
getValue(node, 'r'));
|
2012-11-02 21:40:41 -04:00
|
|
|
},
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/shapes.html#InterfaceSVGEllipseElement
|
2013-02-10 22:37:19 -05:00
|
|
|
ellipse: function(node) {
|
|
|
|
var center = getPoint(node, 'cx', 'cy'),
|
|
|
|
radius = getSize(node, 'rx', 'ry');
|
2012-11-06 14:37:00 -05:00
|
|
|
return new Path.Ellipse(new Rectangle(center.subtract(radius),
|
2012-11-02 22:04:29 -04:00
|
|
|
center.add(radius)));
|
2012-11-02 21:40:41 -04:00
|
|
|
},
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/shapes.html#RectElement
|
2013-02-10 22:37:19 -05:00
|
|
|
rect: function(node) {
|
|
|
|
var point = getPoint(node, 'x', 'y'),
|
|
|
|
size = getSize(node, 'width', 'height'),
|
|
|
|
radius = getSize(node, 'rx', 'ry');
|
2012-11-04 12:01:11 -05:00
|
|
|
// If radius is 0, Path.RoundRectangle automatically produces a
|
|
|
|
// normal rectangle for us.
|
2012-11-02 22:04:29 -04:00
|
|
|
return new Path.RoundRectangle(new Rectangle(point, size), radius);
|
2012-11-02 21:40:41 -04:00
|
|
|
},
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/shapes.html#LineElement
|
2013-02-10 22:37:19 -05:00
|
|
|
line: function(node) {
|
|
|
|
return new Path.Line(getPoint(node, 'x1', 'y1'),
|
|
|
|
getPoint(node, 'x2', 'y2'));
|
2012-11-02 21:40:41 -04:00
|
|
|
},
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
text: function(node) {
|
2012-11-02 21:40:41 -04:00
|
|
|
// Not supported by Paper.js
|
|
|
|
// x: multiple values for x
|
|
|
|
// y: multiple values for y
|
|
|
|
// dx: multiple values for x
|
|
|
|
// dy: multiple values for y
|
2012-11-06 13:37:03 -05:00
|
|
|
// TODO: Support for these is missing in Paper.js right now
|
2012-11-02 21:40:41 -04:00
|
|
|
// rotate: character rotation
|
|
|
|
// lengthAdjust:
|
2013-02-10 22:37:19 -05:00
|
|
|
var text = new PointText(getPoint(node, 'x', 'y', false, 0)
|
|
|
|
.add(getPoint(node, 'dx', 'dy', false, 0)));
|
|
|
|
text.setContent(node.textContent || '');
|
2012-11-02 21:40:41 -04:00
|
|
|
return text;
|
2012-11-08 18:01:13 -05:00
|
|
|
}
|
2012-11-02 21:40:41 -04:00
|
|
|
};
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
// Attributes and Styles
|
2013-02-28 18:40:05 -05:00
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
// NOTE: Parmeter sequence for all apply*() functions is:
|
|
|
|
// (item, value, name, node) rather than (item, node, name, value),
|
|
|
|
// so we can ommit the less likely parameters from right to left.
|
|
|
|
|
|
|
|
function applyTransform(item, value, name, node) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/types.html#DataTypeTransformList
|
|
|
|
var transforms = node[name].baseVal,
|
|
|
|
matrix = new Matrix();
|
|
|
|
for (var i = 0, l = transforms.numberOfItems; i < l; i++) {
|
|
|
|
var mx = transforms.getItem(i).matrix;
|
|
|
|
matrix.concatenate(
|
|
|
|
new Matrix(mx.a, mx.b, mx.c, mx.d, mx.e, mx.f));
|
|
|
|
}
|
|
|
|
item.transform(matrix);
|
|
|
|
}
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
function applyOpacity(item, value, name) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/painting.html#FillOpacityProperty
|
|
|
|
// http://www.w3.org/TR/SVG/painting.html#StrokeOpacityProperty
|
2013-02-28 20:22:57 -05:00
|
|
|
var color = item[name === 'fill-opacity' ? 'getFillColor'
|
2013-02-28 18:40:05 -05:00
|
|
|
: 'getStrokeColor']();
|
|
|
|
if (color)
|
|
|
|
color.setAlpha(parseFloat(value));
|
|
|
|
}
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
function applyTextAttribute(item, value, name, node) {
|
2013-02-28 18:40:05 -05:00
|
|
|
if (item instanceof TextItem) {
|
|
|
|
switch (name) {
|
|
|
|
case 'font':
|
|
|
|
// TODO: Verify if there is not another way?
|
|
|
|
var text = document.createElement('span');
|
|
|
|
text.style.font = value;
|
|
|
|
for (var i = 0; i < text.style.length; i++) {
|
|
|
|
var name = text.style[i];
|
2013-02-28 20:22:57 -05:00
|
|
|
item = applyAttribute(item, text.style[name], name, node);
|
2013-02-28 18:40:05 -05:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'font-family':
|
|
|
|
item.setFont(value.split(',')[0].replace(/^\s+|\s+$/g, ''));
|
|
|
|
break;
|
|
|
|
case 'font-size':
|
|
|
|
item.setFontSize(parseFloat(value));
|
|
|
|
break;
|
|
|
|
case 'text-anchor':
|
|
|
|
// http://www.w3.org/TR/SVG/text.html#TextAnchorProperty
|
|
|
|
item.setJustification({
|
|
|
|
start: 'left',
|
|
|
|
middle: 'center',
|
|
|
|
end: 'right'
|
|
|
|
}[value]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (item instanceof Group) {
|
|
|
|
// Text styles need to be recursively passed down to children that
|
|
|
|
// might be TextItems explicitely.
|
|
|
|
var children = item._children;
|
|
|
|
for (var i = 0, l = children.length; i < l; i++) {
|
|
|
|
applyTextAttribute(children[i], node, name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-28 20:30:19 -05:00
|
|
|
// Create apply-functions for attributes, and merge in those for SvgStlyes:
|
2013-02-28 18:57:47 -05:00
|
|
|
var attributes = Base.each(SvgStyles, function(entry) {
|
2013-02-28 20:22:57 -05:00
|
|
|
this[entry.attribute] = function(item, value, name, node) {
|
2013-02-28 18:57:47 -05:00
|
|
|
item._style[entry.set](convertValue(value, entry.type));
|
|
|
|
};
|
|
|
|
}, {
|
2013-02-28 20:22:57 -05:00
|
|
|
id: function(item, value) {
|
2013-02-28 18:40:05 -05:00
|
|
|
definitions[value] = item;
|
|
|
|
if (item.setName)
|
|
|
|
item.setName(value);
|
|
|
|
},
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
'clip-path': function(item, value) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/masking.html#ClipPathProperty
|
2013-02-28 20:30:19 -05:00
|
|
|
var def = getDefinition(value);
|
2013-02-28 20:15:03 -05:00
|
|
|
return def && createClipGroup(item, def.clone().reduce());
|
2013-02-28 18:40:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
gradientTransform: applyTransform,
|
|
|
|
transform: applyTransform,
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
opacity: function(item, value) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/masking.html#OpacityProperty
|
|
|
|
item.setOpacity(parseFloat(value));
|
|
|
|
},
|
|
|
|
|
|
|
|
'fill-opacity': applyOpacity,
|
|
|
|
'stroke-opacity': applyOpacity,
|
|
|
|
|
|
|
|
font: applyTextAttribute,
|
|
|
|
'font-family': applyTextAttribute,
|
|
|
|
'font-size': applyTextAttribute,
|
|
|
|
'text-anchor': applyTextAttribute,
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
visibility: function(item, value) {
|
2013-02-28 18:40:05 -05:00
|
|
|
item.setVisible(value === 'visible');
|
|
|
|
},
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
'stop-color': function(item, value) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#StopColorProperty
|
|
|
|
item.setColor(value);
|
|
|
|
},
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
'stop-opacity': function(item, value) {
|
2013-02-28 20:15:03 -05:00
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#StopOpacityProperty
|
|
|
|
// NOTE: It is important that this is applied after stop-color!
|
|
|
|
if (item._color)
|
|
|
|
item._color.setAlpha(parseFloat(value));
|
|
|
|
},
|
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
offset: function(item, value) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#StopElementOffsetAttribute
|
|
|
|
var percentage = value.match(/(.*)%$/);
|
|
|
|
item.setRampPoint(percentage ? percentage[1] / 100 : value);
|
|
|
|
},
|
|
|
|
|
2013-02-28 20:26:10 -05:00
|
|
|
viewBox: function(item, value, name, node) {
|
2013-02-28 18:40:05 -05:00
|
|
|
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
|
|
|
// TODO: implement preserveAspectRatio attribute
|
|
|
|
var values = convertValue(value, 'array'),
|
|
|
|
rectangle = Rectangle.create.apply(this, values),
|
|
|
|
size = getSize(node, 'width', 'height', true),
|
|
|
|
scale = size ? rectangle.getSize().divide(size) : 1,
|
|
|
|
offset = rectangle.getPoint(),
|
|
|
|
matrix = new Matrix().translate(offset).scale(scale);
|
|
|
|
if (size)
|
|
|
|
rectangle.setSize(size);
|
2013-03-01 13:08:17 -05:00
|
|
|
if (item instanceof Symbol) {
|
|
|
|
matrix.translate(rectangle.getSize().divide(-2));
|
|
|
|
item._definition.transform(matrix);
|
|
|
|
} else {
|
|
|
|
item.transform(matrix.inverted());
|
|
|
|
rectangle.setPoint(0);
|
|
|
|
// TODO: the viewBox does not always need to be clipped
|
|
|
|
return createClipGroup(item, new Path.Rectangle(rectangle));
|
|
|
|
}
|
2013-02-28 18:40:05 -05:00
|
|
|
}
|
2013-02-28 18:57:47 -05:00
|
|
|
});
|
2013-02-28 18:40:05 -05:00
|
|
|
|
2013-02-28 20:22:57 -05:00
|
|
|
/**
|
|
|
|
* Parses an SVG style attibute and applies it to a Paper.js item.
|
|
|
|
*
|
|
|
|
* @param {SVGSVGElement} node an SVG node
|
|
|
|
* @param {Item} item the item to apply the style or attribute to.
|
|
|
|
* @param {String} name an SVG style name
|
|
|
|
* @param value the value of the SVG style
|
|
|
|
*/
|
|
|
|
function applyAttribute(item, value, name, node) {
|
|
|
|
var attribute;
|
|
|
|
if (value != null && (attribute = attributes[name])) {
|
|
|
|
var res = attribute(item, value, name, node);
|
|
|
|
if (res !== undefined)
|
|
|
|
item = res;
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2012-09-15 23:58:39 -04:00
|
|
|
/**
|
2012-09-30 17:51:50 -04:00
|
|
|
* Converts various SVG styles and attributes into Paper.js styles and
|
2012-11-02 21:14:20 -04:00
|
|
|
* attributes and applies them to the passed item.
|
2012-09-30 17:51:50 -04:00
|
|
|
*
|
2013-02-10 22:37:19 -05:00
|
|
|
* @param {SVGSVGElement} node an SVG node to read style and attributes from.
|
2012-11-02 21:14:20 -04:00
|
|
|
* @param {Item} item the item to apply the style and attributes to.
|
2012-09-15 23:58:39 -04:00
|
|
|
*/
|
2013-02-10 22:37:19 -05:00
|
|
|
function applyAttributes(item, node) {
|
2012-11-05 21:26:29 -05:00
|
|
|
// SVG attributes can be set both as styles and direct node attributes,
|
2013-02-28 20:22:57 -05:00
|
|
|
// so we need to handle both.
|
2013-02-28 20:15:03 -05:00
|
|
|
var styles = DomElement.getStyles(node),
|
|
|
|
parentStyles = DomElement.getStyles(node.parentNode);
|
|
|
|
Base.each(attributes, function(apply, key) {
|
2013-02-28 20:22:57 -05:00
|
|
|
// First see if the given attribute is defined.
|
2013-02-28 20:15:03 -05:00
|
|
|
var attr = node.attributes[key];
|
|
|
|
if (attr) {
|
2013-02-28 20:22:57 -05:00
|
|
|
item = applyAttribute(item, attr.value, attr.name, node);
|
2013-02-28 20:15:03 -05:00
|
|
|
} else {
|
|
|
|
// Fallback to using styles. See if there is a style, either set
|
|
|
|
// directly on the object or applied to it through CSS rules.
|
|
|
|
// We also need to filter out inheritance from their parents.
|
|
|
|
var name = Base.camelize(key),
|
|
|
|
value = node.style[name];
|
|
|
|
if (!value && styles[name] !== parentStyles[name])
|
|
|
|
value = styles[name];
|
|
|
|
if (value && value != 'none')
|
2013-02-28 20:22:57 -05:00
|
|
|
item = applyAttribute(item, value, key, node);
|
2013-02-28 20:15:03 -05:00
|
|
|
}
|
|
|
|
});
|
2012-11-06 23:13:29 -05:00
|
|
|
return item;
|
2012-11-02 21:40:41 -04:00
|
|
|
}
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2013-02-10 13:23:49 -05:00
|
|
|
var definitions = {};
|
|
|
|
function getDefinition(value) {
|
2013-02-28 20:15:03 -05:00
|
|
|
// When url() comes from a style property, '#'' seems to be missing on
|
|
|
|
// WebKit, so let's make it optional here:
|
|
|
|
var match = value.match(/\((?:#|)([^)']+)/);
|
2013-02-10 13:23:49 -05:00
|
|
|
return match && definitions[match[1]];
|
|
|
|
}
|
|
|
|
|
2013-02-10 22:37:19 -05:00
|
|
|
function importSvg(node, clearDefs) {
|
|
|
|
var type = node.nodeName.toLowerCase(),
|
2012-11-07 13:31:36 -05:00
|
|
|
importer = importers[type],
|
2013-02-10 22:37:19 -05:00
|
|
|
item = importer && importer(node, type);
|
2013-02-15 00:42:50 -05:00
|
|
|
// See importGroup() for an explanation of this filtering:
|
|
|
|
if (item && item._type !== 'group')
|
|
|
|
item = applyAttributes(item, node);
|
2013-02-09 16:59:04 -05:00
|
|
|
// Clear definitions at the end of import?
|
|
|
|
if (clearDefs)
|
|
|
|
definitions = {};
|
|
|
|
return item;
|
2012-11-06 16:07:18 -05:00
|
|
|
}
|
|
|
|
|
2012-11-06 16:34:46 -05:00
|
|
|
Item.inject(/** @lends Item# */{
|
2012-11-02 21:40:41 -04:00
|
|
|
/**
|
2013-02-10 22:37:19 -05:00
|
|
|
* Converts the passed node node into a Paper.js item and adds it to the
|
2012-11-06 16:07:18 -05:00
|
|
|
* children of this item.
|
2012-11-02 21:40:41 -04:00
|
|
|
*
|
2013-02-10 22:37:19 -05:00
|
|
|
* @param {SVGSVGElement} node the SVG DOM node to convert
|
2012-11-02 21:40:41 -04:00
|
|
|
* @return {Item} the converted Paper.js item
|
|
|
|
*/
|
2013-02-10 22:37:19 -05:00
|
|
|
importSvg: function(node) {
|
|
|
|
return this.addChild(importSvg(node, true));
|
2012-11-02 21:40:41 -04:00
|
|
|
}
|
2012-11-06 16:07:18 -05:00
|
|
|
});
|
2012-11-06 15:49:12 -05:00
|
|
|
|
2012-11-06 16:34:46 -05:00
|
|
|
Project.inject(/** @lends Project# */{
|
2012-11-06 16:07:18 -05:00
|
|
|
/**
|
2013-02-10 22:37:19 -05:00
|
|
|
* Converts the passed node node into a Paper.js item and adds it to the
|
2012-11-06 16:07:18 -05:00
|
|
|
* active layer of this project.
|
|
|
|
*
|
2013-02-10 22:37:19 -05:00
|
|
|
* @param {SVGSVGElement} node the SVG DOM node to convert
|
2012-11-06 16:07:18 -05:00
|
|
|
* @return {Item} the converted Paper.js item
|
|
|
|
*/
|
2013-02-10 22:37:19 -05:00
|
|
|
importSvg: function(node) {
|
2012-11-06 16:07:18 -05:00
|
|
|
this.activate();
|
2013-02-10 22:37:19 -05:00
|
|
|
return importSvg(node, true);
|
2012-11-06 16:07:18 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|