You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							692 lines
						
					
					
						
							19 KiB
						
					
					
				
			
		
		
	
	
							692 lines
						
					
					
						
							19 KiB
						
					
					
				var Group = require("../container/Group"); | 
						|
 | 
						|
var ZImage = require("../graphic/Image"); | 
						|
 | 
						|
var Text = require("../graphic/Text"); | 
						|
 | 
						|
var Circle = require("../graphic/shape/Circle"); | 
						|
 | 
						|
var Rect = require("../graphic/shape/Rect"); | 
						|
 | 
						|
var Ellipse = require("../graphic/shape/Ellipse"); | 
						|
 | 
						|
var Line = require("../graphic/shape/Line"); | 
						|
 | 
						|
var Path = require("../graphic/Path"); | 
						|
 | 
						|
var Polygon = require("../graphic/shape/Polygon"); | 
						|
 | 
						|
var Polyline = require("../graphic/shape/Polyline"); | 
						|
 | 
						|
var LinearGradient = require("../graphic/LinearGradient"); | 
						|
 | 
						|
var Style = require("../graphic/Style"); | 
						|
 | 
						|
var matrix = require("../core/matrix"); | 
						|
 | 
						|
var _path = require("./path"); | 
						|
 | 
						|
var createFromString = _path.createFromString; | 
						|
 | 
						|
var _util = require("../core/util"); | 
						|
 | 
						|
var isString = _util.isString; | 
						|
var extend = _util.extend; | 
						|
var defaults = _util.defaults; | 
						|
var trim = _util.trim; | 
						|
var each = _util.each; | 
						|
// import RadialGradient from '../graphic/RadialGradient'; | 
						|
// import Pattern from '../graphic/Pattern'; | 
						|
// import * as vector from '../core/vector'; | 
						|
// Most of the values can be separated by comma and/or white space. | 
						|
var DILIMITER_REG = /[\s,]+/; | 
						|
/** | 
						|
 * For big svg string, this method might be time consuming. | 
						|
 * | 
						|
 * @param {string} svg xml string | 
						|
 * @return {Object} xml root. | 
						|
 */ | 
						|
 | 
						|
function parseXML(svg) { | 
						|
  if (isString(svg)) { | 
						|
    var parser = new DOMParser(); | 
						|
    svg = parser.parseFromString(svg, 'text/xml'); | 
						|
  } // Document node. If using $.get, doc node may be input. | 
						|
 | 
						|
 | 
						|
  if (svg.nodeType === 9) { | 
						|
    svg = svg.firstChild; | 
						|
  } // nodeName of <!DOCTYPE svg> is also 'svg'. | 
						|
 | 
						|
 | 
						|
  while (svg.nodeName.toLowerCase() !== 'svg' || svg.nodeType !== 1) { | 
						|
    svg = svg.nextSibling; | 
						|
  } | 
						|
 | 
						|
  return svg; | 
						|
} | 
						|
 | 
						|
function SVGParser() { | 
						|
  this._defs = {}; | 
						|
  this._root = null; | 
						|
  this._isDefine = false; | 
						|
  this._isText = false; | 
						|
} | 
						|
 | 
						|
SVGParser.prototype.parse = function (xml, opt) { | 
						|
  opt = opt || {}; | 
						|
  var svg = parseXML(xml); | 
						|
 | 
						|
  if (!svg) { | 
						|
    throw new Error('Illegal svg'); | 
						|
  } | 
						|
 | 
						|
  var root = new Group(); | 
						|
  this._root = root; // parse view port | 
						|
 | 
						|
  var viewBox = svg.getAttribute('viewBox') || ''; // If width/height not specified, means "100%" of `opt.width/height`. | 
						|
  // TODO: Other percent value not supported yet. | 
						|
 | 
						|
  var width = parseFloat(svg.getAttribute('width') || opt.width); | 
						|
  var height = parseFloat(svg.getAttribute('height') || opt.height); // If width/height not specified, set as null for output. | 
						|
 | 
						|
  isNaN(width) && (width = null); | 
						|
  isNaN(height) && (height = null); // Apply inline style on svg element. | 
						|
 | 
						|
  parseAttributes(svg, root, null, true); | 
						|
  var child = svg.firstChild; | 
						|
 | 
						|
  while (child) { | 
						|
    this._parseNode(child, root); | 
						|
 | 
						|
    child = child.nextSibling; | 
						|
  } | 
						|
 | 
						|
  var viewBoxRect; | 
						|
  var viewBoxTransform; | 
						|
 | 
						|
  if (viewBox) { | 
						|
    var viewBoxArr = trim(viewBox).split(DILIMITER_REG); // Some invalid case like viewBox: 'none'. | 
						|
 | 
						|
    if (viewBoxArr.length >= 4) { | 
						|
      viewBoxRect = { | 
						|
        x: parseFloat(viewBoxArr[0] || 0), | 
						|
        y: parseFloat(viewBoxArr[1] || 0), | 
						|
        width: parseFloat(viewBoxArr[2]), | 
						|
        height: parseFloat(viewBoxArr[3]) | 
						|
      }; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  if (viewBoxRect && width != null && height != null) { | 
						|
    viewBoxTransform = makeViewBoxTransform(viewBoxRect, width, height); | 
						|
 | 
						|
    if (!opt.ignoreViewBox) { | 
						|
      // If set transform on the output group, it probably bring trouble when | 
						|
      // some users only intend to show the clipped content inside the viewBox, | 
						|
      // but not intend to transform the output group. So we keep the output | 
						|
      // group no transform. If the user intend to use the viewBox as a | 
						|
      // camera, just set `opt.ignoreViewBox` as `true` and set transfrom | 
						|
      // manually according to the viewBox info in the output of this method. | 
						|
      var elRoot = root; | 
						|
      root = new Group(); | 
						|
      root.add(elRoot); | 
						|
      elRoot.scale = viewBoxTransform.scale.slice(); | 
						|
      elRoot.position = viewBoxTransform.position.slice(); | 
						|
    } | 
						|
  } // Some shapes might be overflow the viewport, which should be | 
						|
  // clipped despite whether the viewBox is used, as the SVG does. | 
						|
 | 
						|
 | 
						|
  if (!opt.ignoreRootClip && width != null && height != null) { | 
						|
    root.setClipPath(new Rect({ | 
						|
      shape: { | 
						|
        x: 0, | 
						|
        y: 0, | 
						|
        width: width, | 
						|
        height: height | 
						|
      } | 
						|
    })); | 
						|
  } // Set width/height on group just for output the viewport size. | 
						|
 | 
						|
 | 
						|
  return { | 
						|
    root: root, | 
						|
    width: width, | 
						|
    height: height, | 
						|
    viewBoxRect: viewBoxRect, | 
						|
    viewBoxTransform: viewBoxTransform | 
						|
  }; | 
						|
}; | 
						|
 | 
						|
SVGParser.prototype._parseNode = function (xmlNode, parentGroup) { | 
						|
  var nodeName = xmlNode.nodeName.toLowerCase(); // TODO | 
						|
  // support <style>...</style> in svg, where nodeName is 'style', | 
						|
  // CSS classes is defined globally wherever the style tags are declared. | 
						|
 | 
						|
  if (nodeName === 'defs') { | 
						|
    // define flag | 
						|
    this._isDefine = true; | 
						|
  } else if (nodeName === 'text') { | 
						|
    this._isText = true; | 
						|
  } | 
						|
 | 
						|
  var el; | 
						|
 | 
						|
  if (this._isDefine) { | 
						|
    var parser = defineParsers[nodeName]; | 
						|
 | 
						|
    if (parser) { | 
						|
      var def = parser.call(this, xmlNode); | 
						|
      var id = xmlNode.getAttribute('id'); | 
						|
 | 
						|
      if (id) { | 
						|
        this._defs[id] = def; | 
						|
      } | 
						|
    } | 
						|
  } else { | 
						|
    var parser = nodeParsers[nodeName]; | 
						|
 | 
						|
    if (parser) { | 
						|
      el = parser.call(this, xmlNode, parentGroup); | 
						|
      parentGroup.add(el); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  var child = xmlNode.firstChild; | 
						|
 | 
						|
  while (child) { | 
						|
    if (child.nodeType === 1) { | 
						|
      this._parseNode(child, el); | 
						|
    } // Is text | 
						|
 | 
						|
 | 
						|
    if (child.nodeType === 3 && this._isText) { | 
						|
      this._parseText(child, el); | 
						|
    } | 
						|
 | 
						|
    child = child.nextSibling; | 
						|
  } // Quit define | 
						|
 | 
						|
 | 
						|
  if (nodeName === 'defs') { | 
						|
    this._isDefine = false; | 
						|
  } else if (nodeName === 'text') { | 
						|
    this._isText = false; | 
						|
  } | 
						|
}; | 
						|
 | 
						|
SVGParser.prototype._parseText = function (xmlNode, parentGroup) { | 
						|
  if (xmlNode.nodeType === 1) { | 
						|
    var dx = xmlNode.getAttribute('dx') || 0; | 
						|
    var dy = xmlNode.getAttribute('dy') || 0; | 
						|
    this._textX += parseFloat(dx); | 
						|
    this._textY += parseFloat(dy); | 
						|
  } | 
						|
 | 
						|
  var text = new Text({ | 
						|
    style: { | 
						|
      text: xmlNode.textContent, | 
						|
      transformText: true | 
						|
    }, | 
						|
    position: [this._textX || 0, this._textY || 0] | 
						|
  }); | 
						|
  inheritStyle(parentGroup, text); | 
						|
  parseAttributes(xmlNode, text, this._defs); | 
						|
  var fontSize = text.style.fontSize; | 
						|
 | 
						|
  if (fontSize && fontSize < 9) { | 
						|
    // PENDING | 
						|
    text.style.fontSize = 9; | 
						|
    text.scale = text.scale || [1, 1]; | 
						|
    text.scale[0] *= fontSize / 9; | 
						|
    text.scale[1] *= fontSize / 9; | 
						|
  } | 
						|
 | 
						|
  var rect = text.getBoundingRect(); | 
						|
  this._textX += rect.width; | 
						|
  parentGroup.add(text); | 
						|
  return text; | 
						|
}; | 
						|
 | 
						|
var nodeParsers = { | 
						|
  'g': function (xmlNode, parentGroup) { | 
						|
    var g = new Group(); | 
						|
    inheritStyle(parentGroup, g); | 
						|
    parseAttributes(xmlNode, g, this._defs); | 
						|
    return g; | 
						|
  }, | 
						|
  'rect': function (xmlNode, parentGroup) { | 
						|
    var rect = new Rect(); | 
						|
    inheritStyle(parentGroup, rect); | 
						|
    parseAttributes(xmlNode, rect, this._defs); | 
						|
    rect.setShape({ | 
						|
      x: parseFloat(xmlNode.getAttribute('x') || 0), | 
						|
      y: parseFloat(xmlNode.getAttribute('y') || 0), | 
						|
      width: parseFloat(xmlNode.getAttribute('width') || 0), | 
						|
      height: parseFloat(xmlNode.getAttribute('height') || 0) | 
						|
    }); // console.log(xmlNode.getAttribute('transform')); | 
						|
    // console.log(rect.transform); | 
						|
 | 
						|
    return rect; | 
						|
  }, | 
						|
  'circle': function (xmlNode, parentGroup) { | 
						|
    var circle = new Circle(); | 
						|
    inheritStyle(parentGroup, circle); | 
						|
    parseAttributes(xmlNode, circle, this._defs); | 
						|
    circle.setShape({ | 
						|
      cx: parseFloat(xmlNode.getAttribute('cx') || 0), | 
						|
      cy: parseFloat(xmlNode.getAttribute('cy') || 0), | 
						|
      r: parseFloat(xmlNode.getAttribute('r') || 0) | 
						|
    }); | 
						|
    return circle; | 
						|
  }, | 
						|
  'line': function (xmlNode, parentGroup) { | 
						|
    var line = new Line(); | 
						|
    inheritStyle(parentGroup, line); | 
						|
    parseAttributes(xmlNode, line, this._defs); | 
						|
    line.setShape({ | 
						|
      x1: parseFloat(xmlNode.getAttribute('x1') || 0), | 
						|
      y1: parseFloat(xmlNode.getAttribute('y1') || 0), | 
						|
      x2: parseFloat(xmlNode.getAttribute('x2') || 0), | 
						|
      y2: parseFloat(xmlNode.getAttribute('y2') || 0) | 
						|
    }); | 
						|
    return line; | 
						|
  }, | 
						|
  'ellipse': function (xmlNode, parentGroup) { | 
						|
    var ellipse = new Ellipse(); | 
						|
    inheritStyle(parentGroup, ellipse); | 
						|
    parseAttributes(xmlNode, ellipse, this._defs); | 
						|
    ellipse.setShape({ | 
						|
      cx: parseFloat(xmlNode.getAttribute('cx') || 0), | 
						|
      cy: parseFloat(xmlNode.getAttribute('cy') || 0), | 
						|
      rx: parseFloat(xmlNode.getAttribute('rx') || 0), | 
						|
      ry: parseFloat(xmlNode.getAttribute('ry') || 0) | 
						|
    }); | 
						|
    return ellipse; | 
						|
  }, | 
						|
  'polygon': function (xmlNode, parentGroup) { | 
						|
    var points = xmlNode.getAttribute('points'); | 
						|
 | 
						|
    if (points) { | 
						|
      points = parsePoints(points); | 
						|
    } | 
						|
 | 
						|
    var polygon = new Polygon({ | 
						|
      shape: { | 
						|
        points: points || [] | 
						|
      } | 
						|
    }); | 
						|
    inheritStyle(parentGroup, polygon); | 
						|
    parseAttributes(xmlNode, polygon, this._defs); | 
						|
    return polygon; | 
						|
  }, | 
						|
  'polyline': function (xmlNode, parentGroup) { | 
						|
    var path = new Path(); | 
						|
    inheritStyle(parentGroup, path); | 
						|
    parseAttributes(xmlNode, path, this._defs); | 
						|
    var points = xmlNode.getAttribute('points'); | 
						|
 | 
						|
    if (points) { | 
						|
      points = parsePoints(points); | 
						|
    } | 
						|
 | 
						|
    var polyline = new Polyline({ | 
						|
      shape: { | 
						|
        points: points || [] | 
						|
      } | 
						|
    }); | 
						|
    return polyline; | 
						|
  }, | 
						|
  'image': function (xmlNode, parentGroup) { | 
						|
    var img = new ZImage(); | 
						|
    inheritStyle(parentGroup, img); | 
						|
    parseAttributes(xmlNode, img, this._defs); | 
						|
    img.setStyle({ | 
						|
      image: xmlNode.getAttribute('xlink:href'), | 
						|
      x: xmlNode.getAttribute('x'), | 
						|
      y: xmlNode.getAttribute('y'), | 
						|
      width: xmlNode.getAttribute('width'), | 
						|
      height: xmlNode.getAttribute('height') | 
						|
    }); | 
						|
    return img; | 
						|
  }, | 
						|
  'text': function (xmlNode, parentGroup) { | 
						|
    var x = xmlNode.getAttribute('x') || 0; | 
						|
    var y = xmlNode.getAttribute('y') || 0; | 
						|
    var dx = xmlNode.getAttribute('dx') || 0; | 
						|
    var dy = xmlNode.getAttribute('dy') || 0; | 
						|
    this._textX = parseFloat(x) + parseFloat(dx); | 
						|
    this._textY = parseFloat(y) + parseFloat(dy); | 
						|
    var g = new Group(); | 
						|
    inheritStyle(parentGroup, g); | 
						|
    parseAttributes(xmlNode, g, this._defs); | 
						|
    return g; | 
						|
  }, | 
						|
  'tspan': function (xmlNode, parentGroup) { | 
						|
    var x = xmlNode.getAttribute('x'); | 
						|
    var y = xmlNode.getAttribute('y'); | 
						|
 | 
						|
    if (x != null) { | 
						|
      // new offset x | 
						|
      this._textX = parseFloat(x); | 
						|
    } | 
						|
 | 
						|
    if (y != null) { | 
						|
      // new offset y | 
						|
      this._textY = parseFloat(y); | 
						|
    } | 
						|
 | 
						|
    var dx = xmlNode.getAttribute('dx') || 0; | 
						|
    var dy = xmlNode.getAttribute('dy') || 0; | 
						|
    var g = new Group(); | 
						|
    inheritStyle(parentGroup, g); | 
						|
    parseAttributes(xmlNode, g, this._defs); | 
						|
    this._textX += dx; | 
						|
    this._textY += dy; | 
						|
    return g; | 
						|
  }, | 
						|
  'path': function (xmlNode, parentGroup) { | 
						|
    // TODO svg fill rule | 
						|
    // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule | 
						|
    // path.style.globalCompositeOperation = 'xor'; | 
						|
    var d = xmlNode.getAttribute('d') || ''; // Performance sensitive. | 
						|
 | 
						|
    var path = createFromString(d); | 
						|
    inheritStyle(parentGroup, path); | 
						|
    parseAttributes(xmlNode, path, this._defs); | 
						|
    return path; | 
						|
  } | 
						|
}; | 
						|
var defineParsers = { | 
						|
  'lineargradient': function (xmlNode) { | 
						|
    var x1 = parseInt(xmlNode.getAttribute('x1') || 0, 10); | 
						|
    var y1 = parseInt(xmlNode.getAttribute('y1') || 0, 10); | 
						|
    var x2 = parseInt(xmlNode.getAttribute('x2') || 10, 10); | 
						|
    var y2 = parseInt(xmlNode.getAttribute('y2') || 0, 10); | 
						|
    var gradient = new LinearGradient(x1, y1, x2, y2); | 
						|
 | 
						|
    _parseGradientColorStops(xmlNode, gradient); | 
						|
 | 
						|
    return gradient; | 
						|
  }, | 
						|
  'radialgradient': function (xmlNode) {} | 
						|
}; | 
						|
 | 
						|
function _parseGradientColorStops(xmlNode, gradient) { | 
						|
  var stop = xmlNode.firstChild; | 
						|
 | 
						|
  while (stop) { | 
						|
    if (stop.nodeType === 1) { | 
						|
      var offset = stop.getAttribute('offset'); | 
						|
 | 
						|
      if (offset.indexOf('%') > 0) { | 
						|
        // percentage | 
						|
        offset = parseInt(offset, 10) / 100; | 
						|
      } else if (offset) { | 
						|
        // number from 0 to 1 | 
						|
        offset = parseFloat(offset); | 
						|
      } else { | 
						|
        offset = 0; | 
						|
      } | 
						|
 | 
						|
      var stopColor = stop.getAttribute('stop-color') || '#000000'; | 
						|
      gradient.addColorStop(offset, stopColor); | 
						|
    } | 
						|
 | 
						|
    stop = stop.nextSibling; | 
						|
  } | 
						|
} | 
						|
 | 
						|
function inheritStyle(parent, child) { | 
						|
  if (parent && parent.__inheritedStyle) { | 
						|
    if (!child.__inheritedStyle) { | 
						|
      child.__inheritedStyle = {}; | 
						|
    } | 
						|
 | 
						|
    defaults(child.__inheritedStyle, parent.__inheritedStyle); | 
						|
  } | 
						|
} | 
						|
 | 
						|
function parsePoints(pointsString) { | 
						|
  var list = trim(pointsString).split(DILIMITER_REG); | 
						|
  var points = []; | 
						|
 | 
						|
  for (var i = 0; i < list.length; i += 2) { | 
						|
    var x = parseFloat(list[i]); | 
						|
    var y = parseFloat(list[i + 1]); | 
						|
    points.push([x, y]); | 
						|
  } | 
						|
 | 
						|
  return points; | 
						|
} | 
						|
 | 
						|
var attributesMap = { | 
						|
  'fill': 'fill', | 
						|
  'stroke': 'stroke', | 
						|
  'stroke-width': 'lineWidth', | 
						|
  'opacity': 'opacity', | 
						|
  'fill-opacity': 'fillOpacity', | 
						|
  'stroke-opacity': 'strokeOpacity', | 
						|
  'stroke-dasharray': 'lineDash', | 
						|
  'stroke-dashoffset': 'lineDashOffset', | 
						|
  'stroke-linecap': 'lineCap', | 
						|
  'stroke-linejoin': 'lineJoin', | 
						|
  'stroke-miterlimit': 'miterLimit', | 
						|
  'font-family': 'fontFamily', | 
						|
  'font-size': 'fontSize', | 
						|
  'font-style': 'fontStyle', | 
						|
  'font-weight': 'fontWeight', | 
						|
  'text-align': 'textAlign', | 
						|
  'alignment-baseline': 'textBaseline' | 
						|
}; | 
						|
 | 
						|
function parseAttributes(xmlNode, el, defs, onlyInlineStyle) { | 
						|
  var zrStyle = el.__inheritedStyle || {}; | 
						|
  var isTextEl = el.type === 'text'; // TODO Shadow | 
						|
 | 
						|
  if (xmlNode.nodeType === 1) { | 
						|
    parseTransformAttribute(xmlNode, el); | 
						|
    extend(zrStyle, parseStyleAttribute(xmlNode)); | 
						|
 | 
						|
    if (!onlyInlineStyle) { | 
						|
      for (var svgAttrName in attributesMap) { | 
						|
        if (attributesMap.hasOwnProperty(svgAttrName)) { | 
						|
          var attrValue = xmlNode.getAttribute(svgAttrName); | 
						|
 | 
						|
          if (attrValue != null) { | 
						|
            zrStyle[attributesMap[svgAttrName]] = attrValue; | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  var elFillProp = isTextEl ? 'textFill' : 'fill'; | 
						|
  var elStrokeProp = isTextEl ? 'textStroke' : 'stroke'; | 
						|
  el.style = el.style || new Style(); | 
						|
  var elStyle = el.style; | 
						|
  zrStyle.fill != null && elStyle.set(elFillProp, getPaint(zrStyle.fill, defs)); | 
						|
  zrStyle.stroke != null && elStyle.set(elStrokeProp, getPaint(zrStyle.stroke, defs)); | 
						|
  each(['lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize'], function (propName) { | 
						|
    var elPropName = propName === 'lineWidth' && isTextEl ? 'textStrokeWidth' : propName; | 
						|
    zrStyle[propName] != null && elStyle.set(elPropName, parseFloat(zrStyle[propName])); | 
						|
  }); | 
						|
 | 
						|
  if (!zrStyle.textBaseline || zrStyle.textBaseline === 'auto') { | 
						|
    zrStyle.textBaseline = 'alphabetic'; | 
						|
  } | 
						|
 | 
						|
  if (zrStyle.textBaseline === 'alphabetic') { | 
						|
    zrStyle.textBaseline = 'bottom'; | 
						|
  } | 
						|
 | 
						|
  if (zrStyle.textAlign === 'start') { | 
						|
    zrStyle.textAlign = 'left'; | 
						|
  } | 
						|
 | 
						|
  if (zrStyle.textAlign === 'end') { | 
						|
    zrStyle.textAlign = 'right'; | 
						|
  } | 
						|
 | 
						|
  each(['lineDashOffset', 'lineCap', 'lineJoin', 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign', 'textBaseline'], function (propName) { | 
						|
    zrStyle[propName] != null && elStyle.set(propName, zrStyle[propName]); | 
						|
  }); | 
						|
 | 
						|
  if (zrStyle.lineDash) { | 
						|
    el.style.lineDash = trim(zrStyle.lineDash).split(DILIMITER_REG); | 
						|
  } | 
						|
 | 
						|
  if (elStyle[elStrokeProp] && elStyle[elStrokeProp] !== 'none') { | 
						|
    // enable stroke | 
						|
    el[elStrokeProp] = true; | 
						|
  } | 
						|
 | 
						|
  el.__inheritedStyle = zrStyle; | 
						|
} | 
						|
 | 
						|
var urlRegex = /url\(\s*#(.*?)\)/; | 
						|
 | 
						|
function getPaint(str, defs) { | 
						|
  // if (str === 'none') { | 
						|
  //     return; | 
						|
  // } | 
						|
  var urlMatch = defs && str && str.match(urlRegex); | 
						|
 | 
						|
  if (urlMatch) { | 
						|
    var url = trim(urlMatch[1]); | 
						|
    var def = defs[url]; | 
						|
    return def; | 
						|
  } | 
						|
 | 
						|
  return str; | 
						|
} | 
						|
 | 
						|
var transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\(([\-\s0-9\.e,]*)\)/g; | 
						|
 | 
						|
function parseTransformAttribute(xmlNode, node) { | 
						|
  var transform = xmlNode.getAttribute('transform'); | 
						|
 | 
						|
  if (transform) { | 
						|
    transform = transform.replace(/,/g, ' '); | 
						|
    var m = null; | 
						|
    var transformOps = []; | 
						|
    transform.replace(transformRegex, function (str, type, value) { | 
						|
      transformOps.push(type, value); | 
						|
    }); | 
						|
 | 
						|
    for (var i = transformOps.length - 1; i > 0; i -= 2) { | 
						|
      var value = transformOps[i]; | 
						|
      var type = transformOps[i - 1]; | 
						|
      m = m || matrix.create(); | 
						|
 | 
						|
      switch (type) { | 
						|
        case 'translate': | 
						|
          value = trim(value).split(DILIMITER_REG); | 
						|
          matrix.translate(m, m, [parseFloat(value[0]), parseFloat(value[1] || 0)]); | 
						|
          break; | 
						|
 | 
						|
        case 'scale': | 
						|
          value = trim(value).split(DILIMITER_REG); | 
						|
          matrix.scale(m, m, [parseFloat(value[0]), parseFloat(value[1] || value[0])]); | 
						|
          break; | 
						|
 | 
						|
        case 'rotate': | 
						|
          value = trim(value).split(DILIMITER_REG); | 
						|
          matrix.rotate(m, m, parseFloat(value[0])); | 
						|
          break; | 
						|
 | 
						|
        case 'skew': | 
						|
          value = trim(value).split(DILIMITER_REG); | 
						|
          console.warn('Skew transform is not supported yet'); | 
						|
          break; | 
						|
 | 
						|
        case 'matrix': | 
						|
          var value = trim(value).split(DILIMITER_REG); | 
						|
          m[0] = parseFloat(value[0]); | 
						|
          m[1] = parseFloat(value[1]); | 
						|
          m[2] = parseFloat(value[2]); | 
						|
          m[3] = parseFloat(value[3]); | 
						|
          m[4] = parseFloat(value[4]); | 
						|
          m[5] = parseFloat(value[5]); | 
						|
          break; | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    node.setLocalTransform(m); | 
						|
  } | 
						|
} // Value may contain space. | 
						|
 | 
						|
 | 
						|
var styleRegex = /([^\s:;]+)\s*:\s*([^:;]+)/g; | 
						|
 | 
						|
function parseStyleAttribute(xmlNode) { | 
						|
  var style = xmlNode.getAttribute('style'); | 
						|
  var result = {}; | 
						|
 | 
						|
  if (!style) { | 
						|
    return result; | 
						|
  } | 
						|
 | 
						|
  var styleList = {}; | 
						|
  styleRegex.lastIndex = 0; | 
						|
  var styleRegResult; | 
						|
 | 
						|
  while ((styleRegResult = styleRegex.exec(style)) != null) { | 
						|
    styleList[styleRegResult[1]] = styleRegResult[2]; | 
						|
  } | 
						|
 | 
						|
  for (var svgAttrName in attributesMap) { | 
						|
    if (attributesMap.hasOwnProperty(svgAttrName) && styleList[svgAttrName] != null) { | 
						|
      result[attributesMap[svgAttrName]] = styleList[svgAttrName]; | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return result; | 
						|
} | 
						|
/** | 
						|
 * @param {Array.<number>} viewBoxRect | 
						|
 * @param {number} width | 
						|
 * @param {number} height | 
						|
 * @return {Object} {scale, position} | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function makeViewBoxTransform(viewBoxRect, width, height) { | 
						|
  var scaleX = width / viewBoxRect.width; | 
						|
  var scaleY = height / viewBoxRect.height; | 
						|
  var scale = Math.min(scaleX, scaleY); // preserveAspectRatio 'xMidYMid' | 
						|
 | 
						|
  var viewBoxScale = [scale, scale]; | 
						|
  var viewBoxPosition = [-(viewBoxRect.x + viewBoxRect.width / 2) * scale + width / 2, -(viewBoxRect.y + viewBoxRect.height / 2) * scale + height / 2]; | 
						|
  return { | 
						|
    scale: viewBoxScale, | 
						|
    position: viewBoxPosition | 
						|
  }; | 
						|
} | 
						|
/** | 
						|
 * @param {string|XMLElement} xml | 
						|
 * @param {Object} [opt] | 
						|
 * @param {number} [opt.width] Default width if svg width not specified or is a percent value. | 
						|
 * @param {number} [opt.height] Default height if svg height not specified or is a percent value. | 
						|
 * @param {boolean} [opt.ignoreViewBox] | 
						|
 * @param {boolean} [opt.ignoreRootClip] | 
						|
 * @return {Object} result: | 
						|
 * { | 
						|
 *     root: Group, The root of the the result tree of zrender shapes, | 
						|
 *     width: number, the viewport width of the SVG, | 
						|
 *     height: number, the viewport height of the SVG, | 
						|
 *     viewBoxRect: {x, y, width, height}, the declared viewBox rect of the SVG, if exists, | 
						|
 *     viewBoxTransform: the {scale, position} calculated by viewBox and viewport, is exists. | 
						|
 * } | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function parseSVG(xml, opt) { | 
						|
  var parser = new SVGParser(); | 
						|
  return parser.parse(xml, opt); | 
						|
} | 
						|
 | 
						|
exports.parseXML = parseXML; | 
						|
exports.makeViewBoxTransform = makeViewBoxTransform; | 
						|
exports.parseSVG = parseSVG; |