2012-09-30 17:51:50 -04:00
|
|
|
/*
|
2012-11-02 20:47:14 -04:00
|
|
|
* Paper.js
|
|
|
|
*
|
|
|
|
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
|
|
|
|
* based on Scriptographer.org and designed to be largely API compatible.
|
|
|
|
* http://paperjs.org/
|
|
|
|
* http://scriptographer.org/
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
|
|
|
*
|
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2012-11-02 21:14:20 -04:00
|
|
|
* The base for this code was donated by Stetson-Team-Alpha.
|
2012-11-02 20:47:14 -04:00
|
|
|
*/
|
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.
|
|
|
|
|
2012-11-08 18:01:32 -05:00
|
|
|
function getValue(svg, key, allowNull, index) {
|
|
|
|
var base = svg[key].baseVal,
|
|
|
|
value = index !== undefined
|
|
|
|
? index < base.numberOfItems ? base.getItem(index).value : null
|
|
|
|
: base.value;
|
|
|
|
return !allowNull && value == null ? 0 : value;
|
2012-11-02 22:16:23 -04:00
|
|
|
}
|
|
|
|
|
2012-11-08 18:01:32 -05:00
|
|
|
function getPoint(svg, x, y, allowNull, index) {
|
|
|
|
x = getValue(svg, x, allowNull, index);
|
|
|
|
y = getValue(svg, y, allowNull, index);
|
|
|
|
return x == null && y == null ? null : Point.create(x || 0, y || 0);
|
2012-11-02 22:16:23 -04:00
|
|
|
}
|
|
|
|
|
2012-11-08 18:01:32 -05:00
|
|
|
function getSize(svg, w, h, allowNull, index) {
|
|
|
|
w = getValue(svg, w, allowNull, index);
|
|
|
|
h = getValue(svg, h, allowNull, index);
|
|
|
|
return 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'
|
|
|
|
? parseFloat(value, 10)
|
|
|
|
: type === 'array'
|
|
|
|
? value.split(/[\s,]+/g).map(parseFloat)
|
|
|
|
: type === 'color' && getDefinition(value)
|
2012-11-08 12:38:42 -05:00
|
|
|
|| value;
|
2012-11-08 11:31:23 -05:00
|
|
|
}
|
|
|
|
|
2012-11-02 22:16:23 -04:00
|
|
|
// Define importer functions for various SVG node types
|
|
|
|
|
2012-11-06 13:29:14 -05:00
|
|
|
function importGroup(svg, type) {
|
2012-11-06 23:28:20 -05:00
|
|
|
var nodes = svg.childNodes,
|
|
|
|
compound = type === 'clippath',
|
|
|
|
group = compound ? new CompoundPath() : new Group();
|
2012-11-06 21:38:09 -05:00
|
|
|
|
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
|
|
|
}
|
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
|
|
|
|
2012-11-05 21:27:13 -05:00
|
|
|
function importPoly(svg, type) {
|
2012-11-03 01:53:33 -04:00
|
|
|
var path = new Path(),
|
2012-11-06 14:28:50 -05:00
|
|
|
points = svg.points;
|
|
|
|
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
|
|
|
|
2012-11-06 13:28:34 -05:00
|
|
|
function importPath(svg) {
|
|
|
|
var path = new Path(),
|
|
|
|
list = svg.pathSegList,
|
|
|
|
compoundPath, lastPoint;
|
|
|
|
for (var i = 0, l = list.numberOfItems; i < l; i++) {
|
|
|
|
var segment = list.getItem(i),
|
|
|
|
segType = segment.pathSegType,
|
|
|
|
isRelative = segType % 2 == 1;
|
|
|
|
if (segType === /*#=*/ SVGPathSeg.PATHSEG_UNKNOWN)
|
|
|
|
continue;
|
|
|
|
if (!path.isEmpty())
|
|
|
|
lastPoint = path.getLastSegment().getPoint();
|
|
|
|
var relative = isRelative && !path.isEmpty()
|
|
|
|
? lastPoint
|
|
|
|
: Point.create(0, 0);
|
|
|
|
// Horizontal or vertical lineto commands, so fill in the
|
|
|
|
// missing x or y value:
|
|
|
|
var coord = (segType == /*#=*/ SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS
|
|
|
|
|| segType == /*#=*/ SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL) && 'y'
|
|
|
|
|| (segType == /*#=*/ SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS
|
|
|
|
|| segType == /*#=*/ SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL) && 'x';
|
|
|
|
if (coord)
|
|
|
|
segment[coord] = isRelative ? 0 : lastPoint[coord];
|
|
|
|
var point = Point.create(segment.x, segment.y).add(relative);
|
|
|
|
switch (segType) {
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CLOSEPATH:
|
|
|
|
path.closePath();
|
|
|
|
break;
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_MOVETO_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_MOVETO_REL:
|
|
|
|
if (!path.isEmpty() && !compoundPath) {
|
|
|
|
compoundPath = new CompoundPath([path]);
|
|
|
|
}
|
|
|
|
if (compoundPath) {
|
|
|
|
path = new Path();
|
|
|
|
compoundPath.addChild(path);
|
|
|
|
}
|
|
|
|
path.moveTo(point);
|
|
|
|
break;
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_LINETO_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_LINETO_REL:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
|
|
|
|
path.lineTo(point);
|
|
|
|
break;
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
|
|
|
|
path.cubicCurveTo(
|
|
|
|
relative.add(segment.x1, segment.y1),
|
|
|
|
relative.add(segment.x2, segment.y2),
|
|
|
|
point
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
|
|
|
|
path.quadraticCurveTo(
|
|
|
|
relative.add(segment.x1, segment.y1),
|
|
|
|
point
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
// TODO: Implement Arcs: ttp://www.w3.org/TR/SVG/implnote.html
|
|
|
|
// case /*#=*/ SVGPathSeg.PATHSEG_ARC_ABS:
|
|
|
|
// case /*#=*/ SVGPathSeg.PATHSEG_ARC_REL:
|
|
|
|
// break;
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
|
|
|
var prev = list.getItem(i - 1),
|
|
|
|
control = lastPoint.add(lastPoint.subtract(
|
|
|
|
Point.create(prev.x2, prev.y2)
|
|
|
|
.subtract(prev.x, prev.y)
|
|
|
|
.add(lastPoint)));
|
|
|
|
path.cubicCurveTo(
|
|
|
|
control,
|
|
|
|
relative.add(segment.x2, segment.y2),
|
|
|
|
point);
|
|
|
|
break;
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
|
|
|
case /*#=*/ SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
|
|
|
var control,
|
|
|
|
j = i;
|
|
|
|
for (; j >= 0; j--) {
|
|
|
|
var prev = list.getItem(j);
|
|
|
|
if (prev.pathSegType === /*#=*/ SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
|
|
|
|
prev.pathSegType === /*#=*/ SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL) {
|
|
|
|
control = Point.create(prev.x1, prev.y1)
|
|
|
|
.subtract(prev.x, prev.y)
|
|
|
|
.add(path._segments[j].getPoint());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; j < i; ++j) {
|
|
|
|
var anchor = path._segments[j].getPoint();
|
|
|
|
control = anchor.add(anchor.subtract(control));
|
|
|
|
}
|
|
|
|
path.quadraticCurveTo(control, point);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return compoundPath || path;
|
|
|
|
}
|
|
|
|
|
2012-11-07 13:31:36 -05:00
|
|
|
function importGradient(svg, type) {
|
|
|
|
var nodes = svg.childNodes,
|
|
|
|
stops = [];
|
|
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
|
|
var node = nodes[i];
|
|
|
|
if (node.nodeType == 1) {
|
2012-11-08 08:32:09 -05:00
|
|
|
var stop = new GradientStop();
|
|
|
|
applyAttributes(stop, node);
|
|
|
|
stops.push(stop);
|
2012-11-07 13:31:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
var gradient = new Gradient(stops),
|
|
|
|
isRadial = type == 'radialgradient',
|
|
|
|
origin, destination, highlight;
|
|
|
|
if (isRadial) {
|
|
|
|
gradient.type = 'radial';
|
2012-11-08 12:47:07 -05:00
|
|
|
origin = getPoint(svg, 'cx', 'cy');
|
|
|
|
destination = origin.add(getValue(svg, 'r'), 0);
|
2012-11-08 18:01:32 -05:00
|
|
|
highlight = getPoint(svg, 'fx', 'fy', true);
|
2012-11-07 13:31:36 -05:00
|
|
|
} else {
|
2012-11-08 12:47:07 -05:00
|
|
|
origin = getPoint(svg, 'x1', 'y1');
|
|
|
|
destination = getPoint(svg, 'x2', 'y2');
|
2012-11-07 13:31:36 -05:00
|
|
|
}
|
|
|
|
var gradientColor = new GradientColor(gradient, origin, destination, highlight);
|
|
|
|
applyAttributes(gradientColor, svg);
|
|
|
|
}
|
|
|
|
|
2012-11-06 12:14:17 -05:00
|
|
|
var definitions = {};
|
|
|
|
function getDefinition(value) {
|
2012-11-08 08:29:48 -05:00
|
|
|
var match = value.match(/\(#([^)']+)/);
|
|
|
|
return match && definitions[match[1]];
|
2012-11-06 12:14:17 -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-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/struct.html#SymbolElement
|
2012-11-06 13:29:14 -05:00
|
|
|
symbol: function(svg, type) {
|
|
|
|
var item = importGroup(svg, type);
|
2012-11-06 23:13:29 -05:00
|
|
|
item = applyAttributes(item, svg);
|
2012-11-06 12:11:54 -05:00
|
|
|
// TODO: We're returning a symbol. How to handle this?
|
|
|
|
return new Symbol(item);
|
|
|
|
},
|
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
|
|
|
|
use: function(svg, type) {
|
2012-11-08 08:32:09 -05:00
|
|
|
return applyAttributes(null, svg);
|
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
|
2012-11-02 22:04:29 -04:00
|
|
|
circle: function(svg) {
|
2012-11-08 12:47:07 -05:00
|
|
|
return new Path.Circle(getPoint(svg, 'cx', 'cy'),
|
|
|
|
getValue(svg, '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
|
2012-11-02 22:04:29 -04:00
|
|
|
ellipse: function(svg) {
|
2012-11-08 12:47:07 -05:00
|
|
|
var center = getPoint(svg, 'cx', 'cy'),
|
|
|
|
radius = getSize(svg, '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
|
2012-11-02 22:04:29 -04:00
|
|
|
rect: function(svg) {
|
2012-11-08 12:47:07 -05:00
|
|
|
var point = getPoint(svg, 'x', 'y'),
|
|
|
|
size = getSize(svg, 'width', 'height'),
|
|
|
|
radius = getSize(svg, '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
|
2012-11-02 22:04:29 -04:00
|
|
|
line: function(svg) {
|
2012-11-08 12:47:07 -05:00
|
|
|
return new Path.Line(getPoint(svg, 'x1', 'y1'),
|
|
|
|
getPoint(svg, 'x2', 'y2'));
|
2012-11-02 21:40:41 -04:00
|
|
|
},
|
2012-09-30 17:51:50 -04:00
|
|
|
|
2012-11-02 22:04:29 -04:00
|
|
|
text: function(svg) {
|
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:
|
2012-11-08 18:01:32 -05:00
|
|
|
var text = new PointText(getPoint(svg, 'x', 'y', false, 0)
|
|
|
|
.add(getPoint(svg, 'dx', 'dy', false, 0)));
|
2012-11-02 22:04:29 -04:00
|
|
|
text.content = svg.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
|
|
|
|
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
|
|
|
*
|
2012-11-02 21:14:20 -04:00
|
|
|
* @param {SVGSVGElement} svg an SVG node to read style and attributes from.
|
|
|
|
* @param {Item} item the item to apply the style and attributes to.
|
2012-09-15 23:58:39 -04:00
|
|
|
*/
|
2012-11-06 14:14:08 -05:00
|
|
|
function applyAttributes(item, svg) {
|
2012-11-05 21:26:29 -05:00
|
|
|
// SVG attributes can be set both as styles and direct node attributes,
|
|
|
|
// so we need to parse both
|
2012-11-02 22:11:28 -04:00
|
|
|
for (var i = 0, l = svg.style.length; i < l; i++) {
|
|
|
|
var name = svg.style[i];
|
2012-11-06 23:13:29 -05:00
|
|
|
item = applyAttribute(item, svg, name, svg.style[Base.camelize(name)]);
|
2012-09-30 17:51:50 -04:00
|
|
|
}
|
2012-11-08 12:02:38 -05:00
|
|
|
// Path items receive a black fill color by default:
|
|
|
|
if (item instanceof Path && !item.getFillColor())
|
|
|
|
item.setFillColor(new GrayColor(1));
|
2012-11-02 22:11:28 -04:00
|
|
|
for (var i = 0, l = svg.attributes.length; i < l; i++) {
|
|
|
|
var attr = svg.attributes[i];
|
2012-11-06 23:13:29 -05:00
|
|
|
item = applyAttribute(item, svg, attr.name, attr.value);
|
2012-09-30 17:51:50 -04: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
|
|
|
|
|
|
|
/**
|
2012-11-02 21:14:20 -04:00
|
|
|
* Parses an SVG style attibute and applies it to a Paper.js item.
|
2012-09-30 17:51:50 -04:00
|
|
|
*
|
2012-11-02 22:16:23 -04:00
|
|
|
* @param {SVGSVGElement} svg an SVG node
|
|
|
|
* @param {Item} item the item to apply the style or attribute to.
|
2012-11-02 21:14:20 -04:00
|
|
|
* @param {String} name an SVG style name
|
|
|
|
* @param value the value of the SVG style
|
2012-09-16 01:02:23 -04:00
|
|
|
*/
|
2012-11-06 14:14:08 -05:00
|
|
|
function applyAttribute(item, svg, name, value) {
|
2012-11-02 22:26:15 -04:00
|
|
|
if (value == null)
|
2012-11-06 23:13:29 -05:00
|
|
|
return item;
|
2012-11-05 22:26:54 -05:00
|
|
|
var entry = SvgStyles.attributes[name];
|
|
|
|
if (entry) {
|
2012-11-08 12:38:42 -05:00
|
|
|
item._style[entry.set](convertValue(value, entry.type));
|
2012-11-05 22:26:54 -05:00
|
|
|
} else {
|
|
|
|
switch (name) {
|
|
|
|
case 'id':
|
2012-11-06 12:14:17 -05:00
|
|
|
definitions[value] = item;
|
2012-11-07 13:31:36 -05:00
|
|
|
if (item.setName)
|
|
|
|
item.setName(value);
|
2012-11-05 22:26:54 -05:00
|
|
|
break;
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/masking.html#ClipPathProperty
|
2012-11-06 12:14:17 -05:00
|
|
|
case 'clip-path':
|
2012-11-06 13:16:03 -05:00
|
|
|
var clipPath = getDefinition(value).clone().flatten(),
|
2012-11-06 23:37:50 -05:00
|
|
|
group = new Group(clipPath);
|
2012-11-06 23:13:29 -05:00
|
|
|
group.moveAbove(item);
|
|
|
|
group.addChild(item);
|
|
|
|
group.setClipped(true);
|
|
|
|
// item can be modified, since it gets returned from
|
|
|
|
// applyAttribute(). So let's have this change propagate back up
|
|
|
|
item = group;
|
2012-11-06 12:14:17 -05:00
|
|
|
break;
|
2012-11-08 08:32:09 -05:00
|
|
|
// http://www.w3.org/TR/SVG/types.html#DataTypeTransformList
|
2012-11-07 13:31:36 -05:00
|
|
|
case 'gradientTransform':
|
2012-11-05 22:26:54 -05:00
|
|
|
case 'transform':
|
2012-11-08 08:32:09 -05:00
|
|
|
applyTransform(item, svg, name);
|
2012-11-05 22:26:54 -05:00
|
|
|
break;
|
2012-11-08 08:32:09 -05:00
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#StopOpacityProperty
|
|
|
|
case 'stop-opacity':
|
|
|
|
// http://www.w3.org/TR/SVG/masking.html#OpacityProperty
|
2012-11-05 22:26:54 -05:00
|
|
|
case 'opacity':
|
2012-11-08 08:32:09 -05:00
|
|
|
var opacity = parseFloat(value, 10);
|
|
|
|
if (name === 'stop-opacity') {
|
|
|
|
item.color.setAlpha(opacity);
|
|
|
|
} else {
|
|
|
|
item.setOpacity(opacity);
|
|
|
|
}
|
2012-11-05 22:26:54 -05:00
|
|
|
break;
|
|
|
|
case 'visibility':
|
2012-11-06 12:12:55 -05:00
|
|
|
item.setVisible(value === 'visible');
|
2012-11-05 22:26:54 -05:00
|
|
|
break;
|
|
|
|
case 'font':
|
|
|
|
case 'font-family':
|
|
|
|
case 'font-size':
|
2012-11-06 12:11:54 -05:00
|
|
|
// http://www.w3.org/TR/SVG/text.html#TextAnchorProperty
|
2012-11-05 22:26:54 -05:00
|
|
|
case 'text-anchor':
|
2012-11-06 14:14:08 -05:00
|
|
|
applyTextAttribute(item, svg, name, value);
|
2012-11-05 22:26:54 -05:00
|
|
|
break;
|
2012-11-08 08:32:09 -05:00
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#StopColorProperty
|
|
|
|
case 'stop-color':
|
|
|
|
item.setColor(value);
|
|
|
|
break;
|
|
|
|
// http://www.w3.org/TR/SVG/pservers.html#StopElementOffsetAttribute
|
|
|
|
case 'offset':
|
2012-11-08 12:41:58 -05:00
|
|
|
var percentage = value.match(/(.*)%$/);
|
|
|
|
item.setRampPoint(percentage ? percentage[1] / 100 : value);
|
2012-11-08 08:32:09 -05:00
|
|
|
break;
|
|
|
|
case 'xlink:href':
|
2012-11-08 12:42:51 -05:00
|
|
|
var definition = definitions[value.substring(1)];
|
2012-11-08 11:22:32 -05:00
|
|
|
// Use place if we're dealing with a symbol:
|
|
|
|
item = definition.place ? definition.place() : definition.clone();
|
|
|
|
break;
|
2012-11-08 11:32:07 -05:00
|
|
|
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
|
|
|
case 'viewBox':
|
2012-11-08 12:38:42 -05:00
|
|
|
var values = convertValue(value, 'array'),
|
2012-11-08 11:32:07 -05:00
|
|
|
rectangle = Rectangle.create.apply(this, values);
|
2012-11-08 11:48:21 -05:00
|
|
|
// TODO: how to deal with the svg element?
|
2012-11-08 12:38:42 -05:00
|
|
|
if (name !== 'svg')
|
2012-11-08 11:48:21 -05:00
|
|
|
(item.getDefinition ? item.getDefinition() : item).setBounds(rectangle);
|
2012-11-05 22:26:54 -05:00
|
|
|
break;
|
|
|
|
}
|
2012-10-22 19:31:08 -04: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
|
|
|
|
2012-11-06 14:14:08 -05:00
|
|
|
function applyTextAttribute(item, svg, name, value) {
|
2012-11-03 00:11:30 -04:00
|
|
|
if (item instanceof TextItem) {
|
|
|
|
switch (name) {
|
|
|
|
case 'font':
|
2012-11-03 01:53:33 -04:00
|
|
|
// TODO: Verify if there is not another way?
|
2012-11-03 00:11:30 -04:00
|
|
|
var text = document.createElement('span');
|
|
|
|
text.style.font = value;
|
|
|
|
for (var i = 0; i < text.style.length; i++) {
|
2012-11-05 22:48:00 -05:00
|
|
|
var name = text.style[i];
|
2012-11-06 23:13:29 -05:00
|
|
|
item = applyAttribute(item, svg, name, text.style[name]);
|
2012-11-03 00:11:30 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'font-family':
|
2012-11-05 22:48:00 -05:00
|
|
|
item.setFont(value.split(',')[0].replace(/^\s+|\s+$/g, ''));
|
2012-11-03 00:11:30 -04:00
|
|
|
break;
|
|
|
|
case 'font-size':
|
|
|
|
item.setFontSize(parseFloat(value, 10));
|
|
|
|
break;
|
|
|
|
case 'text-anchor':
|
|
|
|
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++) {
|
2012-11-06 14:14:08 -05:00
|
|
|
applyTextAttribute(children[i], svg, name, value);
|
2012-11-03 00:11:30 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-30 17:51:50 -04:00
|
|
|
/**
|
2012-11-02 21:14:20 -04:00
|
|
|
* Applies the transformations specified on the SVG node to a Paper.js item
|
2012-09-30 17:51:50 -04:00
|
|
|
*
|
2012-11-02 21:14:20 -04:00
|
|
|
* @param {SVGSVGElement} svg an SVG node
|
2012-11-02 22:16:23 -04:00
|
|
|
* @param {Item} item a Paper.js item
|
2012-09-30 17:51:50 -04:00
|
|
|
*/
|
2012-11-07 13:31:36 -05:00
|
|
|
function applyTransform(item, svg, name) {
|
2012-11-08 08:32:09 -05:00
|
|
|
var svgTransform = svg[name],
|
2012-11-07 13:31:36 -05:00
|
|
|
transforms = svgTransform.baseVal,
|
2012-11-02 22:39:59 -04:00
|
|
|
matrix = new Matrix();
|
2012-11-02 22:11:16 -04:00
|
|
|
for (var i = 0, l = transforms.numberOfItems; i < l; i++) {
|
2012-11-02 22:39:59 -04:00
|
|
|
var transform = transforms.getItem(i);
|
2012-11-05 21:26:29 -05:00
|
|
|
if (transform.type === /*#=*/ SVGTransform.SVG_TRANSFORM_UNKNOWN)
|
2012-09-30 17:51:50 -04:00
|
|
|
continue;
|
2012-11-02 22:39:59 -04:00
|
|
|
// Convert SVG Matrix to Paper Matrix.
|
|
|
|
// TODO: Should this be moved to our Matrix constructor?
|
|
|
|
var mx = transform.matrix,
|
|
|
|
a = mx.a,
|
|
|
|
b = mx.b,
|
|
|
|
c = mx.c,
|
|
|
|
d = mx.d;
|
2012-09-30 17:51:50 -04:00
|
|
|
switch (transform.type) {
|
2012-11-02 22:39:59 -04:00
|
|
|
// Compensate for SVG's theta rotation going the opposite direction
|
2012-11-05 21:27:50 -05:00
|
|
|
case /*#=*/ SVGTransform.SVG_TRANSFORM_MATRIX:
|
2012-11-02 22:39:59 -04:00
|
|
|
var tmp = b;
|
|
|
|
b = c;
|
|
|
|
c = tmp;
|
2012-10-22 19:31:08 -04:00
|
|
|
break;
|
2012-11-05 21:26:29 -05:00
|
|
|
case /*#=*/ SVGTransform.SVG_TRANSFORM_SKEWX:
|
2012-11-02 22:39:59 -04:00
|
|
|
b = c;
|
|
|
|
c = 0;
|
2012-10-22 19:31:08 -04:00
|
|
|
break;
|
2012-11-05 21:26:29 -05:00
|
|
|
case /*#=*/ SVGTransform.SVG_TRANSFORM_SKEWY:
|
2012-11-02 22:39:59 -04:00
|
|
|
c = b;
|
|
|
|
b = 0;
|
2012-10-22 19:31:08 -04:00
|
|
|
break;
|
2012-11-05 21:26:29 -05:00
|
|
|
case /*#=*/ SVGTransform.SVG_TRANSFORM_ROTATE:
|
2012-11-02 22:39:59 -04:00
|
|
|
b = -b;
|
|
|
|
c = -c;
|
2012-10-22 19:31:08 -04:00
|
|
|
break;
|
2012-09-30 17:51:50 -04:00
|
|
|
}
|
2012-11-02 22:39:59 -04:00
|
|
|
matrix.concatenate(new Matrix(a, c, b, d, mx.e, mx.f));
|
2012-09-30 17:51:50 -04:00
|
|
|
}
|
|
|
|
item.transform(matrix);
|
|
|
|
}
|
2012-11-02 21:40:41 -04:00
|
|
|
|
2012-11-06 16:07:18 -05:00
|
|
|
function importSvg(svg) {
|
|
|
|
var type = svg.nodeName.toLowerCase(),
|
2012-11-07 13:31:36 -05:00
|
|
|
importer = importers[type],
|
|
|
|
item = importer && importer(svg, type);
|
2012-11-06 23:13:29 -05:00
|
|
|
return item ? applyAttributes(item, svg) : 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
|
|
|
/**
|
2012-11-06 16:07:18 -05:00
|
|
|
* Converts the passed svg node into a Paper.js item and adds it to the
|
|
|
|
* children of this item.
|
2012-11-02 21:40:41 -04:00
|
|
|
*
|
|
|
|
* @param {SVGSVGElement} svg the SVG DOM node to convert
|
|
|
|
* @return {Item} the converted Paper.js item
|
|
|
|
*/
|
|
|
|
importSvg: function(svg) {
|
2012-11-06 16:07:18 -05:00
|
|
|
return this.addChild(importSvg(svg));
|
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
|
|
|
/**
|
|
|
|
* Converts the passed svg node into a Paper.js item and adds it to the
|
|
|
|
* active layer of this project.
|
|
|
|
*
|
|
|
|
* @param {SVGSVGElement} svg the SVG DOM node to convert
|
|
|
|
* @return {Item} the converted Paper.js item
|
|
|
|
*/
|
|
|
|
importSvg: function(svg) {
|
|
|
|
this.activate();
|
|
|
|
return importSvg(svg);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|