diff --git a/src/parser.js b/src/parser.js index 23e41489..3c71c44b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -41,7 +41,8 @@ 'stroke-opacity': 'strokeOpacity', 'stroke-width': 'strokeWidth', 'text-decoration': 'textDecoration', - 'text-anchor': 'originX' + 'text-anchor': 'originX', + opacity: 'opacity' }, colorAttributes = { @@ -93,6 +94,12 @@ value = false; } } + else if (attr === 'opacity') { + value = parseFloat(value); + if (parentAttributes && typeof parentAttributes.opacity !== 'undefined') { + value *= parentAttributes.opacity; + } + } else if (attr === 'originX' /* text-anchor */) { value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center'; } @@ -311,8 +318,8 @@ style.replace(/;\s*$/, '').split(';').forEach(function (chunk) { var pair = chunk.split(':'); - attr = normalizeAttr(pair[0].trim().toLowerCase()); - value = normalizeValue(attr, pair[1].trim()); + attr = pair[0].trim().toLowerCase(); + value = pair[1].trim(); oStyle[attr] = value; }); @@ -328,8 +335,8 @@ continue; } - attr = normalizeAttr(prop.toLowerCase()); - value = normalizeValue(attr, style[prop]); + attr = prop.toLowerCase(); + value = style[prop]; oStyle[attr] = value; } @@ -758,23 +765,27 @@ var ownAttributes = attributes.reduce(function(memo, attr) { value = element.getAttribute(attr); - if (value) { - attr = normalizeAttr(attr); - value = normalizeValue(attr, value, parentAttributes, fontSize); - + if (value) { // eslint-disable-line memo[attr] = value; } return memo; }, { }); - // add values parsed from style, which take precedence over attributes // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes) ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element))); - if (ownAttributes.font) { - fabric.parseFontDeclaration(ownAttributes.font, ownAttributes); + + var normalizedAttr, normalizedValue, normalizedStyle = {}; + for (var attr in ownAttributes) { + normalizedAttr = normalizeAttr(attr); + normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize); + normalizedStyle[normalizedAttr] = normalizedValue; } - return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes)); + if (normalizedStyle && normalizedStyle.font) { + fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle); + } + var mergedAttrs = extend(parentAttributes, normalizedStyle); + return reAllowedParents.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs); }, /** @@ -884,8 +895,8 @@ for (var i = 0, len = propertyValuePairs.length; i < len; i++) { var pair = propertyValuePairs[i].split(/\s*:\s*/), - property = normalizeAttr(pair[0]), - value = normalizeValue(property, pair[1], pair[0]); + property = pair[0], + value = pair[1]; ruleObj[property] = value; } rule = match[1]; diff --git a/test/unit/parser.js b/test/unit/parser.js index e675037e..49a739e4 100644 --- a/test/unit/parser.js +++ b/test/unit/parser.js @@ -145,10 +145,10 @@ var styleObj = fabric.parseStyleAttribute(element); // TODO: looks like this still fails with % values var expectedObject = { - 'left': 10, - 'top': 356.8, - 'width': 137.93333333333334, - 'height': 20 + left: '10px', + top: '22.3em', + width: '103.45pt', + height: '20%' }; deepEqual(styleObj, expectedObject); }); @@ -158,7 +158,7 @@ element.setAttribute('style', 'left:10px'); var expectedObject = { - 'left': 10 + left: '10px' }; deepEqual(fabric.parseStyleAttribute(element), expectedObject); }); @@ -168,8 +168,8 @@ element.setAttribute('style', 'left:10px; top:5px; '); var expectedObject = { - 'left': 10, - 'top': 5 + left: '10px', + top: '5px' }; deepEqual(fabric.parseStyleAttribute(element), expectedObject); }); @@ -179,8 +179,8 @@ element.setAttribute('style', 'fill:none; stroke-dasharray: 2 0.4;'); var expectedObject = { - 'fill': '', - 'strokeDashArray': [2, 0.4] + fill: 'none', + 'stroke-dasharray': '2 0.4' }; deepEqual(fabric.parseStyleAttribute(element), expectedObject); }); @@ -244,6 +244,16 @@ deepEqual(fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES), expectedObject); }); + test('parse 0 attribute', function() { + var element = fabric.document.createElement('path'); + element.setAttribute('opacity', 0); + + var expectedObject = { + opacity: 0, + }; + deepEqual(fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES), expectedObject); + }); + test('parsePointsAttribute', function() { ok(fabric.parsePointsAttribute); @@ -348,6 +358,50 @@ }); }); + asyncTest('parseSVGFromString nested opacity', function() { + var string = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + fabric.loadSVGFromString(string, function(objects) { + equal(objects[0].fill, 'rgba(255,0,0,0.3)', 'first circle has opacity 0.3 from rgba'); + equal(objects[0].fillOpacity, 1,'first circle has fill-opacity 1'); + equal(objects[1].fill, 'rgba(0,255,0,0.25)', 'first circle has opacity 0.5 from rgba and 0.5 from gtoup fill opacity'); + equal(objects[1].fillOpacity, 0.5,'first circle has fill-opacity 0.5'); + equal(objects[2].fill, 'rgba(255,0,0,0.5)', 'first circle has opacity 0.5 from group fill opacity'); + equal(objects[2].fillOpacity, 0.5,'first circle has fill-opacity 0.5'); + equal(objects[3].fill, 'rgba(0,0,255,0.5)', 'first circle has opacity 0.5 from fill opacity'); + equal(objects[3].fillOpacity, 0.5,'first circle has fill-opacity 1'); + equal(objects[4].fill, 'rgba(0,0,255,0.25)', 'first circle has opacity 0.5 from rgba and 0.5 from fill opacity'); + equal(objects[4].fillOpacity, 0.5,'first circle has fill-opacity 0.5'); + equal(objects[5].fill, 'rgba(0,0,255,1)', 'first circle has opacity 1 from rgba'); + equal(objects[5].fillOpacity, 1,'first circle has fill-opacity 1'); + equal(objects[6].opacity, 0.25, 'opacity is 0.25 for cls-3 * cls-4'); + equal(objects[7].opacity, 0.5,'opacity is 0.5 from cls-3'); + start(); + }); + }); + asyncTest('parseSVGFromString with svg:namespace', function() { var string = '' + '' + @@ -523,15 +577,15 @@ 'g polygon.cls': { 'fill': '#FF0000', 'stroke': '#000000', - 'strokeWidth': 0.25 + 'stroke-width': '0.25px' }, 'rect': { 'fill': '#FF0000', 'stroke': '#000000', - 'strokeWidth': 0.25 + 'stroke-width': '0.25px' }, 'polygon.cls': { - 'fill': '', + 'fill': 'none', 'stroke': '#0000FF' } }; @@ -575,13 +629,13 @@ '.cls1': { 'fill': '#FF0000', 'stroke': '#00FF00', - 'strokeWidth': 3 + 'stroke-width': '3' }, '.cls2': { 'fill': '#FF0000' }, '.cls3': { - 'strokeWidth': 3 + 'stroke-width': '3' } };