diff --git a/dist/all.js b/dist/all.js index 6ffeba8d..6b2e5652 100644 --- a/dist/all.js +++ b/dist/all.js @@ -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); diff --git a/src/parser.js b/src/parser.js index e3e6e310..538a64b8 100644 --- a/src/parser.js +++ b/src/parser.js @@ -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); \ No newline at end of file diff --git a/test/demo/demo.js b/test/demo/demo.js index 107743ce..c6b77146 100644 --- a/test/demo/demo.js +++ b/test/demo/demo.js @@ -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); diff --git a/test/demo/index.html b/test/demo/index.html index 98a1cf4f..1271a16f 100644 --- a/test/demo/index.html +++ b/test/demo/index.html @@ -50,6 +50,13 @@
Add SVG shapes to canvas: