First stab at parsing + applying css rules to SVG elements. Needs unit tests!

This commit is contained in:
kangax 2011-01-19 02:30:35 -05:00
parent 1acbb04036
commit 580c0680f0
4 changed files with 161 additions and 13 deletions

73
dist/all.js vendored
View file

@ -2506,7 +2506,7 @@ fabric.util.animate = animate;
}, { });
ownAttributes = extend(ownAttributes, fabric.parseStyleAttribute(element));
ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
return extend(parentAttributes, ownAttributes);
};
@ -2778,6 +2778,72 @@ fabric.util.animate = animate;
});
};
/**
* Returns CSS rules for a given SVG document
* @static
* @function
* @memberOf fabric
* @method getCSSRules
* @param {SVGDocument} doc SVG document to parse
* @return {Object} CSS rules of this document
*/
function getCSSRules(doc) {
var styles = doc.getElementsByTagName('style'),
allRules = { },
rules;
for (var i = 0, len = styles.length; i < len; i++) {
var styleContents = styles[0].textContent;
styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
rules = rules.map(function(rule) { return rule.trim() });
rules.forEach(function(rule) {
var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
rule = match[1],
declaration = match[2].trim(),
propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
if (!allRules[rule]) {
allRules[rule] = { };
}
for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
var pair = propertyValuePairs[i].split(/\s*:\s*/),
property = pair[0],
value = pair[1];
allRules[rule][property] = value;
}
});
}
return allRules;
}
function getGlobalStylesForElement(element) {
var nodeName = element.nodeName,
className = element.getAttribute('class'),
id = element.getAttribute('id'),
styles = { };
for (var rule in fabric.cssRules) {
var ruleMatchesElement = (className && new RegExp('^\\.' + className).test(rule)) ||
(id && new RegExp('^#' + id).test(rule)) ||
(new RegExp('^' + nodeName).test(rule));
if (ruleMatchesElement) {
for (var property in fabric.cssRules[rule]) {
styles[property] = fabric.cssRules[rule][property];
}
}
}
return styles;
}
/**
* Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
* @static
@ -2848,6 +2914,8 @@ fabric.util.animate = animate;
};
fabric.gradientDefs = fabric.getGradientDefs(doc);
fabric.cssRules = getCSSRules(doc);
fabric.parseElements(elements, function(instances) {
if (callback) {
@ -2861,7 +2929,8 @@ fabric.util.animate = animate;
parseAttributes: parseAttributes,
parseElements: parseElements,
parseStyleAttribute: parseStyleAttribute,
parsePointsAttribute: parsePointsAttribute
parsePointsAttribute: parsePointsAttribute,
getCSSRules: getCSSRules
});
})(this);

View file

@ -75,7 +75,7 @@
// add values parsed from style, which take precedence over attributes
// (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
ownAttributes = extend(ownAttributes, fabric.parseStyleAttribute(element));
ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
return extend(parentAttributes, ownAttributes);
};
@ -359,6 +359,74 @@
});
};
/**
* Returns CSS rules for a given SVG document
* @static
* @function
* @memberOf fabric
* @method getCSSRules
* @param {SVGDocument} doc SVG document to parse
* @return {Object} CSS rules of this document
*/
function getCSSRules(doc) {
var styles = doc.getElementsByTagName('style'),
allRules = { },
rules;
// very crude parsing of style contents
for (var i = 0, len = styles.length; i < len; i++) {
var styleContents = styles[0].textContent;
// remove comments
styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
rules = rules.map(function(rule) { return rule.trim() });
rules.forEach(function(rule) {
var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
rule = match[1],
declaration = match[2].trim(),
propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
if (!allRules[rule]) {
allRules[rule] = { };
}
for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
var pair = propertyValuePairs[i].split(/\s*:\s*/),
property = pair[0],
value = pair[1];
allRules[rule][property] = value;
}
});
}
return allRules;
}
function getGlobalStylesForElement(element) {
var nodeName = element.nodeName,
className = element.getAttribute('class'),
id = element.getAttribute('id'),
styles = { };
for (var rule in fabric.cssRules) {
var ruleMatchesElement = (className && new RegExp('^\\.' + className).test(rule)) ||
(id && new RegExp('^#' + id).test(rule)) ||
(new RegExp('^' + nodeName).test(rule));
if (ruleMatchesElement) {
for (var property in fabric.cssRules[rule]) {
styles[property] = fabric.cssRules[rule][property];
}
}
}
return styles;
}
/**
* Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
* @static
@ -433,7 +501,10 @@
};
fabric.gradientDefs = fabric.getGradientDefs(doc);
fabric.cssRules = getCSSRules(doc);
// Precedence of rules: style > class > attribute
fabric.parseElements(elements, function(instances) {
if (callback) {
callback(instances, options);
@ -446,7 +517,8 @@
parseAttributes: parseAttributes,
parseElements: parseElements,
parseStyleAttribute: parseStyleAttribute,
parsePointsAttribute: parsePointsAttribute
parsePointsAttribute: parsePointsAttribute,
getCSSRules: getCSSRules
});
})(this);

View file

@ -28,7 +28,12 @@
if (!xml) return;
var doc = xml.documentElement;
if (!doc) return;
fabric.parseSVGDocument(doc, callback);
var startTime = new Date();
fabric.parseSVGDocument(doc, function() {
console.log((new Date() - startTime) + 'ms');
callback.apply(this, arguments);
});
}
})
}
@ -121,7 +126,7 @@
.set('top', top)
.set('angle', angle)
.set('fill', '#' + getRandomColor())
.scale(getRandomNum(0.75, 1.25))
.scaleToWidth(300)
.setCoords();
canvas.add(pathGroup);

View file

@ -50,6 +50,13 @@
<p>Add <strong>SVG shapes</strong> to canvas:</p>
<ul class="svg-shapes">
<!-- <li><button class="shape" id="shape28"><strong>5378</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape52"><strong>11285</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape56"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape60"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape68"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape70"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape73"><strong>xxx</strong> paths</button></li> -->
<li><button class="shape" id="shape54"><strong>1</strong> path</button></li>
<li><button class="shape" id="shape66"><strong>1</strong> path</button></li>
<li><button class="shape" id="shape25"><strong>36</strong> paths</button></li>
@ -95,10 +102,8 @@
<li><button class="shape" id="shape23"><strong>4418</strong> paths</button></li>
<li><button class="shape" id="shape42"><strong>4583</strong> paths</button></li>
<li><button class="shape" id="shape31"><strong>4768</strong> paths</button></li>
<!-- <li><button class="shape" id="shape28"><strong>5378</strong> paths</button></li> -->
<li><button class="shape" id="shape15"><strong>8325</strong> paths</button></li>
<li><button class="shape" id="shape22"><strong>9663</strong> paths</button></li>
<!-- <li><button class="shape" id="shape52"><strong>11285</strong> paths</button></li> -->
<li><button class="shape" id="shape41"><strong>12361</strong> paths</button></li>
<li><button class="shape" id="shape67"><strong>12604</strong> paths</button></li>
<li><button class="shape" id="shape24"><strong>12866</strong> paths</button></li>
@ -108,11 +113,8 @@
<li><button class="shape" id="shape20"><strong>19035</strong> paths</button></li>
<li><button class="shape" id="shape35"><strong>19271</strong> paths</button></li>
<li><button class="shape" id="shape44"><strong>22375</strong> paths</button></li>
<li><button class="shape" id="shape72"><strong>29303</strong> paths</button></li>
<li><button class="shape" id="shape48"><strong>41787</strong> paths</button></li>
<!-- <li><button class="shape" id="shape56"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape60"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape68"><strong>xxx</strong> paths</button></li> -->
<!-- <li><button class="shape" id="shape70"><strong>xxx</strong> paths</button></li> -->
</ul>
<ul>