Refactor SvgImporter to new structure using real private functions and importers lookup-table.

This commit is contained in:
Jürg Lehni 2012-11-02 18:40:41 -07:00
parent ebadef8b1d
commit 95b9b81c81

View file

@ -25,275 +25,23 @@
* within groups.
*
*/
var SvgImporter = this.SvgImporter = new function() {
var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
/**
* Creates a Paper.js item using data parsed from the selected
* SVG DOM node.
*
* @param {SVGSVGElement} svg the SVG DOM node to convert
* @return {Item} the converted Paper.js item
*/
importSvg: function(svg) {
var item;
var symbol;
switch (svg.nodeName.toLowerCase()) {
case 'line':
item = this._importLine(svg);
break;
case 'rect':
item = this._importRectangle(svg);
break;
case 'circle':
item = this._importCircle(svg);
break;
case 'ellipse':
item = this._importOval(svg);
break;
case 'g':
case 'svg':
item = this._importGroup(svg);
break;
case 'text':
item = this._importText(svg);
break;
case 'path':
item = this._importPath(svg);
break;
case 'polygon':
case 'polyline':
item = this._importPoly(svg);
break;
case 'symbol':
item = this._importGroup(svg);
this._importAttributesAndStyles(svg, item);
// TODO: We're no returning symbol. How to handle this?
symbol = new Symbol(item);
item = null;
break;
default:
// TODO: Not supported yet.
}
if (item)
this._importAttributesAndStyles(svg, item);
return item;
},
_importGroup: function(svg) {
function importGroup(svg) {
var group = new Group();
var nodes = svg.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
var child = nodes[i];
if (child.nodeType == 1) {
var item = this.importSvg(child);
var item = SvgImporter.importSvg(child);
if (item)
group.addChild(item);
}
}
return group;
},
}
_importCircle: function(svgCircle) {
var cx = svgCircle.cx.baseVal.value || 0;
var cy = svgCircle.cy.baseVal.value || 0;
var r = svgCircle.r.baseVal.value || 0;
var center = new Point(cx, cy);
var circle = new Path.Circle(center, r);
return circle;
},
_importOval: function(svgOval) {
var cx = svgOval.cx.baseVal.value || 0;
var cy = svgOval.cy.baseVal.value || 0;
var rx = svgOval.rx.baseVal.value || 0;
var ry = svgOval.ry.baseVal.value || 0;
var center = new Point(cx, cy);
var offset = new Point(rx, ry);
var topLeft = center.subtract(offset);
var bottomRight = center.add(offset);
var rect = new Rectangle(topLeft, bottomRight);
var oval = new Path.Oval(rect);
return oval;
},
_importRectangle: function(svgRectangle) {
var x = svgRectangle.x.baseVal.value || 0;
var y = svgRectangle.y.baseVal.value || 0;
var rx = svgRectangle.rx.baseVal.value || 0;
var ry = svgRectangle.ry.baseVal.value || 0;
var width = svgRectangle.width.baseVal.value || 0;
var height = svgRectangle.height.baseVal.value || 0;
var topLeft = new Point(x, y);
var size = new Size(width, height);
var rectangle = new Rectangle(topLeft, size);
if (rx && ry) {
rectangle = new Path.RoundRectangle(rectangle, new Size(rx, ry));
} else {
rectangle = new Path.Rectangle(rectangle);
}
return rectangle;
},
_importLine: function(svgLine) {
var x1 = svgLine.x1.baseVal.value || 0;
var y1 = svgLine.y1.baseVal.value || 0;
var x2 = svgLine.x2.baseVal.value || 0;
var y2 = svgLine.y2.baseVal.value || 0;
var from = new Point(x1, y1);
var to = new Point(x2, y2);
var line = new Path.Line(from, to);
return line;
},
_importText: function(svgText) {
var x = svgText.x.baseVal.getItem(0).value || 0;
var y = svgText.y.baseVal.getItem(0).value || 0;
var dx = 0;
var dy = 0;
if (svgText.dx.baseVal.numberOfItems) {
dx = svgText.dx.baseVal.getItem(0).value || 0;
}
if (svgText.dy.baseVal.numberOfItems) {
dy = svgText.dy.baseVal.getItem(0).value || 0;
}
var textLength = svgText.textLength.baseVal.value || 0;
/* 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
var rotate; //character rotation
var lengthAdjust;
*/
var textContent = svgText.textContent || "";
var bottomLeft = new Point(x, y);
bottomLeft = bottomLeft.add([dx, dy]);
bottomLeft = bottomLeft.subtract([textLength / 2, 0]);
var text = new PointText(bottomLeft);
text.content = textContent;
return text;
},
/**
* Creates a Path item by parsing a SVG node (rectangle, path, circle,
* polygon, etc.) and creating the right Path item based on the SVG type.
*
* @param {SVGSVGElement) an SVG node
* @return {Item} the converted item
*/
_importPath: function(svgPath) {
var path = new Path();
var segments = svgPath.pathSegList;
var segment;
var j;
var relativeToPoint;
var controlPoint;
var prevCommand;
var segmentTo;
for (var i = 0; i < segments.numberOfItems; i++) {
segment = segments.getItem(i);
if (segment.pathSegType == SVGPathSeg.PATHSEG_UNKNOWN) {
continue;
}
if (segment.pathSegType % 2 == 1 && path.segments.length > 0) {
relativeToPoint = path.lastSegment.point;
} else {
relativeToPoint = new Point(0, 0);
}
segmentTo = new Point(segment.x, segment.y);
segmentTo = segmentTo.add(relativeToPoint);
switch (segment.pathSegType) {
case SVGPathSeg.PATHSEG_CLOSEPATH:
path.closePath();
break;
case SVGPathSeg.PATHSEG_MOVETO_ABS:
case SVGPathSeg.PATHSEG_MOVETO_REL:
path.moveTo(segmentTo);
break;
case SVGPathSeg.PATHSEG_LINETO_ABS:
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
case SVGPathSeg.PATHSEG_LINETO_REL:
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
path.lineTo(segmentTo);
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
path.cubicCurveTo(
relativeToPoint.add([segment.x1, segment.y1]),
relativeToPoint.add([segment.x2, segment.y2]),
segmentTo
);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
path.quadraticCurveTo(
relativeToPoint.add([segment.x1, segment.y1]),
segmentTo
);
break;
case SVGPathSeg.PATHSEG_ARC_ABS:
case SVGPathSeg.PATHSEG_ARC_REL:
//TODO: Implement Arcs.
//TODO: Requires changes in Paper.js's Path to do.
//TODO: http://www.w3.org/TR/SVG/implnote.html
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
prevCommand = segments.getItem(i - 1);
controlPoint = new Point(prevCommand.x2, prevCommand.y2);
controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
controlPoint = controlPoint.add(path.lastSegment.point);
controlPoint = path.lastSegment.point.subtract(controlPoint);
controlPoint = path.lastSegment.point.add(controlPoint);
path.cubicCurveTo(
controlPoint,
relativeToPoint.add([segment.x2, segment.y2]),
segmentTo
);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
for (j = i; j >= 0; --j) {
prevCommand = segments.getItem(j);
if (prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL
) {
controlPoint = new Point(prevCommand.x1, prevCommand.y1);
controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
controlPoint = controlPoint.add(path.segments[j].point);
break;
}
}
for (j; j < i; ++j) {
controlPoint = path.segments[j].point.subtract(controlPoint);
controlPoint = path.segments[j].point.add(controlPoint);
}
path.quadraticCurveTo(controlPoint, segmentTo);
break;
}
}
return path;
},
_importPoly: function(svgPoly) {
function importPoly(svgPoly) {
var poly = new Path();
var points = svgPoly.points;
var start = points.getItem(0);
@ -309,7 +57,205 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
}
return poly;
},
}
var importers = {
g: importGroup,
svg: importGroup,
polygon: importPoly,
polyline: importPoly,
circle: function(svgCircle) {
var cx = svgCircle.cx.baseVal.value || 0;
var cy = svgCircle.cy.baseVal.value || 0;
var r = svgCircle.r.baseVal.value || 0;
var center = new Point(cx, cy);
var circle = new Path.Circle(center, r);
return circle;
},
ellipse: function(svgOval) {
var cx = svgOval.cx.baseVal.value || 0;
var cy = svgOval.cy.baseVal.value || 0;
var rx = svgOval.rx.baseVal.value || 0;
var ry = svgOval.ry.baseVal.value || 0;
var center = new Point(cx, cy);
var offset = new Point(rx, ry);
var topLeft = center.subtract(offset);
var bottomRight = center.add(offset);
var rect = new Rectangle(topLeft, bottomRight);
var oval = new Path.Oval(rect);
return oval;
},
rect: function(svgRectangle) {
var x = svgRectangle.x.baseVal.value || 0;
var y = svgRectangle.y.baseVal.value || 0;
var rx = svgRectangle.rx.baseVal.value || 0;
var ry = svgRectangle.ry.baseVal.value || 0;
var width = svgRectangle.width.baseVal.value || 0;
var height = svgRectangle.height.baseVal.value || 0;
var topLeft = new Point(x, y);
var size = new Size(width, height);
var rectangle = new Rectangle(topLeft, size);
return new Path.RoundRectangle(rectangle, new Size(rx, ry));
},
line: function(svgLine) {
var x1 = svgLine.x1.baseVal.value || 0;
var y1 = svgLine.y1.baseVal.value || 0;
var x2 = svgLine.x2.baseVal.value || 0;
var y2 = svgLine.y2.baseVal.value || 0;
var from = new Point(x1, y1);
var to = new Point(x2, y2);
var line = new Path.Line(from, to);
return line;
},
text: function(svgText) {
var x = svgText.x.baseVal.getItem(0).value || 0;
var y = svgText.y.baseVal.getItem(0).value || 0;
var dx = 0;
var dy = 0;
if (svgText.dx.baseVal.numberOfItems) {
dx = svgText.dx.baseVal.getItem(0).value || 0;
}
if (svgText.dy.baseVal.numberOfItems) {
dy = svgText.dy.baseVal.getItem(0).value || 0;
}
var textLength = svgText.textLength.baseVal.value || 0;
// 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
// rotate: character rotation
// lengthAdjust:
var textContent = svgText.textContent || "";
var bottomLeft = new Point(x, y);
bottomLeft = bottomLeft.add([dx, dy]);
bottomLeft = bottomLeft.subtract([textLength / 2, 0]);
var text = new PointText(bottomLeft);
text.content = textContent;
return text;
},
path: function(svgPath) {
var path = new Path();
var segments = svgPath.pathSegList;
var segment;
var j;
var relativeToPoint;
var controlPoint;
var prevCommand;
var segmentTo;
for (var i = 0; i < segments.numberOfItems; i++) {
segment = segments.getItem(i);
if (segment.pathSegType == SVGPathSeg.PATHSEG_UNKNOWN) {
continue;
}
if (segment.pathSegType % 2 == 1 && path.segments.length > 0) {
relativeToPoint = path.lastSegment.point;
} else {
relativeToPoint = new Point(0, 0);
}
segmentTo = new Point(segment.x, segment.y);
segmentTo = segmentTo.add(relativeToPoint);
switch (segment.pathSegType) {
case SVGPathSeg.PATHSEG_CLOSEPATH:
path.closePath();
break;
case SVGPathSeg.PATHSEG_MOVETO_ABS:
case SVGPathSeg.PATHSEG_MOVETO_REL:
path.moveTo(segmentTo);
break;
case SVGPathSeg.PATHSEG_LINETO_ABS:
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
case SVGPathSeg.PATHSEG_LINETO_REL:
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
path.lineTo(segmentTo);
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
path.cubicCurveTo(
relativeToPoint.add([segment.x1, segment.y1]),
relativeToPoint.add([segment.x2, segment.y2]),
segmentTo
);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
path.quadraticCurveTo(
relativeToPoint.add([segment.x1, segment.y1]),
segmentTo
);
break;
case SVGPathSeg.PATHSEG_ARC_ABS:
case SVGPathSeg.PATHSEG_ARC_REL:
//TODO: Implement Arcs.
//TODO: Requires changes in Paper.js's Path to do.
//TODO: http://www.w3.org/TR/SVG/implnote.html
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
prevCommand = segments.getItem(i - 1);
controlPoint = new Point(prevCommand.x2, prevCommand.y2);
controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
controlPoint = controlPoint.add(path.lastSegment.point);
controlPoint = path.lastSegment.point.subtract(controlPoint);
controlPoint = path.lastSegment.point.add(controlPoint);
path.cubicCurveTo(
controlPoint,
relativeToPoint.add([segment.x2, segment.y2]),
segmentTo
);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
for (j = i; j >= 0; --j) {
prevCommand = segments.getItem(j);
if (prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL
) {
controlPoint = new Point(prevCommand.x1, prevCommand.y1);
controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
controlPoint = controlPoint.add(path.segments[j].point);
break;
}
}
for (j; j < i; ++j) {
controlPoint = path.segments[j].point.subtract(controlPoint);
controlPoint = path.segments[j].point.add(controlPoint);
}
path.quadraticCurveTo(controlPoint, segmentTo);
break;
}
}
return path;
},
symbol: function(svg) {
var item = importGroup(svg);
importAttributesAndStyles(svg, item);
// TODO: We're returning a symbol. How to handle this?
return new Symbol(item);
}
};
/**
* Converts various SVG styles and attributes into Paper.js styles and
@ -318,7 +264,7 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
* @param {SVGSVGElement} svg an SVG node to read style and attributes from.
* @param {Item} item the item to apply the style and attributes to.
*/
_importAttributesAndStyles: function(svg, item) {
function importAttributesAndStyles(svg, item) {
var name,
value,
cssName;
@ -328,14 +274,14 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
return p.toUpperCase();
});
value = svg.style[cssName];
this._applyAttributeOrStyle(name, value, item, svg);
applyAttributeOrStyle(name, value, item, svg);
}
for (var i = 0; i < svg.attributes.length; i++) {
name = svg.attributes[i].name;
value = svg.attributes[i].value;
this._applyAttributeOrStyle(name, value, item, svg);
applyAttributeOrStyle(name, value, item, svg);
}
},
}
/**
* Parses an SVG style attibute and applies it to a Paper.js item.
@ -345,7 +291,7 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
* @param {Item} item the item to apply the style or attribute to.
* @param {SVGSVGElement} svg an SVG node
*/
_applyAttributeOrStyle: function(name, value, item, svg) {
function applyAttributeOrStyle(name, value, item, svg) {
if (!value) {
return;
}
@ -389,7 +335,7 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
item.miterLimit = parseFloat(value, 10);
break;
case 'transform':
this._applyTransform(item, svg);
applyTransform(item, svg);
break;
case 'opacity':
item.opacity = parseFloat(value, 10);
@ -413,7 +359,7 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
text.style.font = value;
for (var i = 0; i < text.style.length; i++) {
var n = text.style[i];
this._applyAttributeOrStyle(n, text.style[n], item, svg);
applyAttributeOrStyle(n, text.style[n], item, svg);
}
break;
case 'font-family':
@ -424,9 +370,9 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
case 'font-size':
item.characterStyle.fontSize = parseFloat(value, 10);
break;
}
}
}
},
}
/**
* Applies the transformations specified on the SVG node to a Paper.js item
@ -434,7 +380,7 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
* @param {Item} item a Paper.js item
* @param {SVGSVGElement} svg an SVG node
*/
_applyTransform: function(item, svg) {
function applyTransform(item, svg) {
var transforms = svg.transform.baseVal;
var transform;
var matrix = new Matrix();
@ -480,4 +426,22 @@ var SvgImporter = this.SvgImporter = /** @Lends SvgImporter */{
}
item.transform(matrix);
}
return /** @Lends SvgImporter */{
/**
* Creates a Paper.js item using data parsed from the selected
* SVG DOM node.
*
* @param {SVGSVGElement} svg the SVG DOM node to convert
* @return {Item} the converted Paper.js item
*/
importSvg: function(svg) {
var importer = importers[svg.nodeName.toLowerCase()];
// TODO: importer == null: Not supported yet.
var item = importer && importer(svg);
if (item)
importAttributesAndStyles(svg, item);
return item;
}
};
};