Skip to content

Commit

Permalink
code added
Browse files Browse the repository at this point in the history
  • Loading branch information
fudini committed Mar 10, 2012
1 parent 3c43650 commit 52f3b45
Show file tree
Hide file tree
Showing 12 changed files with 646 additions and 0 deletions.
335 changes: 335 additions & 0 deletions src/Savage.Parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
/*
TODO:
parse all color values (hex, rgb(), rgba(), red, blue, green, etc..) into some sort of rgba object
*/
Savage.Parser = {
parse: function(svg) {
var elements = svg.getElementsByTagName('*');
var savageElements = this.parseElements(elements);
var savageObject = new Savage.object();
savageObject.add(savageElements.elements);
savageObject.originalWidth = parseFloat(svg.attributes["width"].nodeValue);
savageObject.originalHeight = parseFloat(svg.attributes["height"].nodeValue);
savageObject.width = savageObject.originalWidth;
savageObject.height = savageObject.originalHeight;
return savageObject;
},
parseElements: function(elements) {
var result = [];
for(var i = 0; i < elements.length; i++) {
var element = elements[i];
var tag = element.tagName.toLowerCase();
// Illustrator doesn't apply transformations to groups so we don't care about it just yet.
if(tag != "g") {
var attributes = this.parseAttributes(element.attributes);
var savage = new Savage[tag](attributes);
result.push(savage);
}
}
return {elements: result, hitmap: hitmap, center:center};
},
parseElement: function(element) {
var tag = element.tagName.toLowerCase();
var attributes = this.parseAttributes(element.attributes);
var savage = new Savage[tag](attributes);
savage.id = element.id;
return savage;
},
parseAttributes: function(attributes) {
var result = {};
for(var i = 0; i < attributes.length; i ++) {
var attributeName = attributes[i].nodeName;
var attributeValue = attributes[i].nodeValue;
if(this.attributeMap[attributeName]) attributeName = this.attributeMap[attributeName];
var parseFunctionName = this.attributeTypesMap[attributeName];
if(this.parseAttribute[parseFunctionName]) {
result[attributeName] = this.parseAttribute[parseFunctionName](attributeValue);
}
}
return result;
},
// translate to something more usable
attributeMap: {
"cx" : "x",
"cy" : "y",
"r" : "radius",
"d" : "commands",
"stroke-width": "strokeWidth"
},
// allowed attributes names and their corresponding parse methods
attributeTypes: {
"width,height,x,y": "float",
"rx,ry,radius,opacity,strokeWidth": "float",
"fill,stroke": "color",
"transform": "transform",
"commands": "d",
"points": "points",
"id": "string"
},
attributeTypesMap: null,
// get the attributeTypes object and convert to name-value pairs for a quick lookup
parseAttributeTypesMap: function() {
if(this.attributeTypesMap == null) {
this.attributeTypesMap = {};
for(var i in this.attributeTypes) {
if(this.attributeTypes.hasOwnProperty(i)) {
var keys = i.split(',');
var value = this.attributeTypes[i];
for(var j in keys) {
if(keys.hasOwnProperty(j)) {
this.attributeTypesMap[keys[j]] = value;
}
}
}
}
}
},
parseAttribute: {
'string': function(value) {
return value;
},
'color': function(value) {
if(value == "none") return null;
return value;
},
'float': function(value) {
return parseFloat(value);
},
'int': function(value) {
return parseInt(value);
},
'transform': function(transform) {
transform = transform.replace(/\,/g, '');
var match = transform.match(/matrix\(([0-9\-\.\s]+)\)/);
if(match) {
var matrix = match[1].split(" ");
for(var i = 0, l = matrix.length; i < l; i ++) {
matrix[i] = parseFloat(matrix[i]);
}
return matrix;
} else {
Savage.error("transform command not implemented"); // only matrix for now
return null;
}
},
'd': function(path) {
path = Savage.Parser.pathToArray_d(path);
var commands = [];
var item = null;
var command = null;
var x = 0;
var y = 0;
function parseCommand(item) {
switch(item) {
case "M":
x = parseFloat(path.shift());
y = parseFloat(path.shift());
commands.push({
type:'m',
data: {x: x, y: y}
});
command = item;
break;
case "h":
x += parseFloat(path.shift());
commands.push({
type: 'l',
data: {x: x, y: y}
});
command = item;
break;
case "H":
x = parseFloat(path.shift());
commands.push({
type: 'l',
data: {x: x, y: y}
});
command = item;
break;
case "v":
y += parseFloat(path.shift());
commands.push({
type: 'l',
data: {x: x, y: y}
});
command = item;
break;
case "V":
y = parseFloat(path.shift());
commands.push({
type: 'l',
data: {x: x, y: y}
});
command = item;
break;
case "l":
x += parseFloat(path.shift());
y += parseFloat(path.shift());
commands.push({
type: 'l',
data: {x: x, y: y}
});
command = item;
break;
case "L":
x = parseFloat(path.shift());
y = parseFloat(path.shift());
commands.push({
type: 'l',
data: {x: x, y: y}
});
command = item;
break;
case "c":
var data = {
x: x + parseFloat(path.shift()),
y: y + parseFloat(path.shift()),
x2: x + parseFloat(path.shift()),
y2: y + parseFloat(path.shift()),
x3: x + parseFloat(path.shift()),
y3: y + parseFloat(path.shift())
};
commands.push({
type: 'c',
data: data
});
x = data.x3;
y = data.y3;
command = item;
break;
case "C":
var data = {
x: parseFloat(path.shift()),
y: parseFloat(path.shift()),
x2: parseFloat(path.shift()),
y2: parseFloat(path.shift()),
x3: parseFloat(path.shift()),
y3: parseFloat(path.shift())
};
commands.push({
type: 'c',
data: data
});
x = data.x3;
y = data.y3;
command = item;
break;
case "s":
var previousCommand = commands[commands.length - 1];
if("cCsS".indexOf(previousCommand.type) != -1) {
var data = {
x: x + x - previousCommand.data.x2,
y: y + y - previousCommand.data.y2,
x2: x + parseFloat(path.shift()),
y2: y + parseFloat(path.shift()),
x3: x + parseFloat(path.shift()),
y3: y + parseFloat(path.shift())
};
commands.push({
type: 'c',
data: data
});
x = data.x3;
y = data.y3;
command = item;
} else {
Savage.error("previous command not a bezier");
}
break;
case "S":
var previousCommand = commands[commands.length - 1];
if("cCsS".indexOf(previousCommand.type) != -1) {
var data = {
x: x + x - previousCommand.data.x2,
y: y + y - previousCommand.data.y2,
x2: parseFloat(path.shift()),
y2: parseFloat(path.shift()),
x3: parseFloat(path.shift()),
y3: parseFloat(path.shift())
};
commands.push({
type: 'c',
data: data
});
x = data.x3;
y = data.y3;
command = item;
} else {
Savage.error("previous command not a bezier");
}
break;
case "z":
break;
default:
// numeric value, use the previous command to process
path.unshift(item);
parseCommand(command);
break;
}
}
while(item = path.shift()) {
parseCommand(item);
}
return commands;
},
'points': function(path) {
path = Savage.Parser.pathToArray_points(path);
var points = [];
var x = 0;
var y = 0;
while(x = path.shift()) {
y = path.shift();
points.push({
x: parseFloat(x),
y: parseFloat(y)
});
}
return points;
}
},
pathToArray_points: function (path) {
var result = path.replace(/[\n\t]/g, "").replace(/\s/g, ",").replace(/\-/, ",-").replace(/\,+/g, ",").replace(/^\,|\,$/g, "").split(",");
return result;
},
pathToArray_d: function (path) {
var result = path.replace(/[\n\t\-\smlhvcszqta]/gmi, function(match) {
var value = "," + match + ",";
switch(match) {
case "\t":
case "\n":
value = "";
break;
case " ":
value = ",";
break;
case "-":
value = ",-";
break;
}
return value;
}).replace(/\,+/g, ",").replace(/^\,|\,$/g, "").split(",");
return result;
},
pointsToBox: function(points) {
var box = {
x: null,
y: null,
x2: null,
y2: null,
width: null,
height: null
};
for(var i = 1, l = points.length; i < l; i++) {
var point = points[i];
if(point.x < box.x || box.x == null) box.x = point.x;
if(point.x > box.x2 || box.x2 == null) box.x2 = point.x;
if(point.y < box.y || box.y == null) box.y = point.y;
if(point.y > box.y2 || box.y2 == null) box.y2 = point.y;
}
box.width = box.x2 - box.x;
box.height = box.y2 - box.y;
return box;
}
}
Savage.Parser.parseAttributeTypesMap();
36 changes: 36 additions & 0 deletions src/Savage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Savage.js - SVG to canvas parser
Author: Daniel Fudala
What savage does:
parses svg shapes into canvas drawing commands:
supported shapes: rect, circle, ellipse, polygon, path
supported transform commands: matrix
It only supports SVG files created with Adobe Illustrator
Savage.Parser.parse(svg)
Savage.Parser.parseElement(svgNode) - converts svg node into a Savage.object
Savage.object - a collection of paths
*/

// not so cool if you want to run this thing on node.js
// var Savage = window.Savage || {};
var Savage = {};
// probably not the best way of checking if running on node
if(typeof window === 'undefined') {
exports.Savage = Savage;
}

Savage.error = function(message) {
console.log("SAVAGE ERROR: " + message);
};
Savage.debug = true;
Savage.extend = function(o1, o2) {
for(var key in o2) {
if(o2.hasOwnProperty(key)) {
o1[key] = o2[key];
}
}
};
Loading

0 comments on commit 52f3b45

Please sign in to comment.