parseSVG.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. import Group from '../container/Group';
  2. import ZImage from '../graphic/Image';
  3. import Text from '../graphic/Text';
  4. import Circle from '../graphic/shape/Circle';
  5. import Rect from '../graphic/shape/Rect';
  6. import Ellipse from '../graphic/shape/Ellipse';
  7. import Line from '../graphic/shape/Line';
  8. import Path from '../graphic/Path';
  9. import Polygon from '../graphic/shape/Polygon';
  10. import Polyline from '../graphic/shape/Polyline';
  11. import LinearGradient from '../graphic/LinearGradient';
  12. // import RadialGradient from '../graphic/RadialGradient';
  13. // import Pattern from '../graphic/Pattern';
  14. import Style from '../graphic/Style';
  15. // import * as vector from '../core/vector';
  16. import * as matrix from '../core/matrix';
  17. import { createFromString } from './path';
  18. import { isString, extend, defaults, trim, each } from '../core/util';
  19. // Most of the values can be separated by comma and/or white space.
  20. var DILIMITER_REG = /[\s,]+/;
  21. /**
  22. * For big svg string, this method might be time consuming.
  23. *
  24. * @param {string} svg xml string
  25. * @return {Object} xml root.
  26. */
  27. export function parseXML(svg) {
  28. if (isString(svg)) {
  29. var parser = new DOMParser();
  30. svg = parser.parseFromString(svg, 'text/xml');
  31. }
  32. // Document node. If using $.get, doc node may be input.
  33. if (svg.nodeType === 9) {
  34. svg = svg.firstChild;
  35. }
  36. // nodeName of <!DOCTYPE svg> is also 'svg'.
  37. while (svg.nodeName.toLowerCase() !== 'svg' || svg.nodeType !== 1) {
  38. svg = svg.nextSibling;
  39. }
  40. return svg;
  41. }
  42. function SVGParser() {
  43. this._defs = {};
  44. this._root = null;
  45. this._isDefine = false;
  46. this._isText = false;
  47. }
  48. SVGParser.prototype.parse = function (xml, opt) {
  49. opt = opt || {};
  50. var svg = parseXML(xml);
  51. if (!svg) {
  52. throw new Error('Illegal svg');
  53. }
  54. var root = new Group();
  55. this._root = root;
  56. // parse view port
  57. var viewBox = svg.getAttribute('viewBox') || '';
  58. // If width/height not specified, means "100%" of `opt.width/height`.
  59. // TODO: Other percent value not supported yet.
  60. var width = parseFloat(svg.getAttribute('width') || opt.width);
  61. var height = parseFloat(svg.getAttribute('height') || opt.height);
  62. // If width/height not specified, set as null for output.
  63. isNaN(width) && (width = null);
  64. isNaN(height) && (height = null);
  65. // Apply inline style on svg element.
  66. parseAttributes(svg, root, null, true);
  67. var child = svg.firstChild;
  68. while (child) {
  69. this._parseNode(child, root);
  70. child = child.nextSibling;
  71. }
  72. var viewBoxRect;
  73. var viewBoxTransform;
  74. if (viewBox) {
  75. var viewBoxArr = trim(viewBox).split(DILIMITER_REG);
  76. // Some invalid case like viewBox: 'none'.
  77. if (viewBoxArr.length >= 4) {
  78. viewBoxRect = {
  79. x: parseFloat(viewBoxArr[0] || 0),
  80. y: parseFloat(viewBoxArr[1] || 0),
  81. width: parseFloat(viewBoxArr[2]),
  82. height: parseFloat(viewBoxArr[3])
  83. };
  84. }
  85. }
  86. if (viewBoxRect && width != null && height != null) {
  87. viewBoxTransform = makeViewBoxTransform(viewBoxRect, width, height);
  88. if (!opt.ignoreViewBox) {
  89. // If set transform on the output group, it probably bring trouble when
  90. // some users only intend to show the clipped content inside the viewBox,
  91. // but not intend to transform the output group. So we keep the output
  92. // group no transform. If the user intend to use the viewBox as a
  93. // camera, just set `opt.ignoreViewBox` as `true` and set transfrom
  94. // manually according to the viewBox info in the output of this method.
  95. var elRoot = root;
  96. root = new Group();
  97. root.add(elRoot);
  98. elRoot.scale = viewBoxTransform.scale.slice();
  99. elRoot.position = viewBoxTransform.position.slice();
  100. }
  101. }
  102. // Some shapes might be overflow the viewport, which should be
  103. // clipped despite whether the viewBox is used, as the SVG does.
  104. if (!opt.ignoreRootClip && width != null && height != null) {
  105. root.setClipPath(new Rect({
  106. shape: {x: 0, y: 0, width: width, height: height}
  107. }));
  108. }
  109. // Set width/height on group just for output the viewport size.
  110. return {
  111. root: root,
  112. width: width,
  113. height: height,
  114. viewBoxRect: viewBoxRect,
  115. viewBoxTransform: viewBoxTransform
  116. };
  117. };
  118. SVGParser.prototype._parseNode = function (xmlNode, parentGroup) {
  119. var nodeName = xmlNode.nodeName.toLowerCase();
  120. // TODO
  121. // support <style>...</style> in svg, where nodeName is 'style',
  122. // CSS classes is defined globally wherever the style tags are declared.
  123. if (nodeName === 'defs') {
  124. // define flag
  125. this._isDefine = true;
  126. }
  127. else if (nodeName === 'text') {
  128. this._isText = true;
  129. }
  130. var el;
  131. if (this._isDefine) {
  132. var parser = defineParsers[nodeName];
  133. if (parser) {
  134. var def = parser.call(this, xmlNode);
  135. var id = xmlNode.getAttribute('id');
  136. if (id) {
  137. this._defs[id] = def;
  138. }
  139. }
  140. }
  141. else {
  142. var parser = nodeParsers[nodeName];
  143. if (parser) {
  144. el = parser.call(this, xmlNode, parentGroup);
  145. parentGroup.add(el);
  146. }
  147. }
  148. var child = xmlNode.firstChild;
  149. while (child) {
  150. if (child.nodeType === 1) {
  151. this._parseNode(child, el);
  152. }
  153. // Is text
  154. if (child.nodeType === 3 && this._isText) {
  155. this._parseText(child, el);
  156. }
  157. child = child.nextSibling;
  158. }
  159. // Quit define
  160. if (nodeName === 'defs') {
  161. this._isDefine = false;
  162. }
  163. else if (nodeName === 'text') {
  164. this._isText = false;
  165. }
  166. };
  167. SVGParser.prototype._parseText = function (xmlNode, parentGroup) {
  168. if (xmlNode.nodeType === 1) {
  169. var dx = xmlNode.getAttribute('dx') || 0;
  170. var dy = xmlNode.getAttribute('dy') || 0;
  171. this._textX += parseFloat(dx);
  172. this._textY += parseFloat(dy);
  173. }
  174. var text = new Text({
  175. style: {
  176. text: xmlNode.textContent,
  177. transformText: true
  178. },
  179. position: [this._textX || 0, this._textY || 0]
  180. });
  181. inheritStyle(parentGroup, text);
  182. parseAttributes(xmlNode, text, this._defs);
  183. var fontSize = text.style.fontSize;
  184. if (fontSize && fontSize < 9) {
  185. // PENDING
  186. text.style.fontSize = 9;
  187. text.scale = text.scale || [1, 1];
  188. text.scale[0] *= fontSize / 9;
  189. text.scale[1] *= fontSize / 9;
  190. }
  191. var rect = text.getBoundingRect();
  192. this._textX += rect.width;
  193. parentGroup.add(text);
  194. return text;
  195. };
  196. var nodeParsers = {
  197. 'g': function (xmlNode, parentGroup) {
  198. var g = new Group();
  199. inheritStyle(parentGroup, g);
  200. parseAttributes(xmlNode, g, this._defs);
  201. return g;
  202. },
  203. 'rect': function (xmlNode, parentGroup) {
  204. var rect = new Rect();
  205. inheritStyle(parentGroup, rect);
  206. parseAttributes(xmlNode, rect, this._defs);
  207. rect.setShape({
  208. x: parseFloat(xmlNode.getAttribute('x') || 0),
  209. y: parseFloat(xmlNode.getAttribute('y') || 0),
  210. width: parseFloat(xmlNode.getAttribute('width') || 0),
  211. height: parseFloat(xmlNode.getAttribute('height') || 0)
  212. });
  213. // console.log(xmlNode.getAttribute('transform'));
  214. // console.log(rect.transform);
  215. return rect;
  216. },
  217. 'circle': function (xmlNode, parentGroup) {
  218. var circle = new Circle();
  219. inheritStyle(parentGroup, circle);
  220. parseAttributes(xmlNode, circle, this._defs);
  221. circle.setShape({
  222. cx: parseFloat(xmlNode.getAttribute('cx') || 0),
  223. cy: parseFloat(xmlNode.getAttribute('cy') || 0),
  224. r: parseFloat(xmlNode.getAttribute('r') || 0)
  225. });
  226. return circle;
  227. },
  228. 'line': function (xmlNode, parentGroup) {
  229. var line = new Line();
  230. inheritStyle(parentGroup, line);
  231. parseAttributes(xmlNode, line, this._defs);
  232. line.setShape({
  233. x1: parseFloat(xmlNode.getAttribute('x1') || 0),
  234. y1: parseFloat(xmlNode.getAttribute('y1') || 0),
  235. x2: parseFloat(xmlNode.getAttribute('x2') || 0),
  236. y2: parseFloat(xmlNode.getAttribute('y2') || 0)
  237. });
  238. return line;
  239. },
  240. 'ellipse': function (xmlNode, parentGroup) {
  241. var ellipse = new Ellipse();
  242. inheritStyle(parentGroup, ellipse);
  243. parseAttributes(xmlNode, ellipse, this._defs);
  244. ellipse.setShape({
  245. cx: parseFloat(xmlNode.getAttribute('cx') || 0),
  246. cy: parseFloat(xmlNode.getAttribute('cy') || 0),
  247. rx: parseFloat(xmlNode.getAttribute('rx') || 0),
  248. ry: parseFloat(xmlNode.getAttribute('ry') || 0)
  249. });
  250. return ellipse;
  251. },
  252. 'polygon': function (xmlNode, parentGroup) {
  253. var points = xmlNode.getAttribute('points');
  254. if (points) {
  255. points = parsePoints(points);
  256. }
  257. var polygon = new Polygon({
  258. shape: {
  259. points: points || []
  260. }
  261. });
  262. inheritStyle(parentGroup, polygon);
  263. parseAttributes(xmlNode, polygon, this._defs);
  264. return polygon;
  265. },
  266. 'polyline': function (xmlNode, parentGroup) {
  267. var path = new Path();
  268. inheritStyle(parentGroup, path);
  269. parseAttributes(xmlNode, path, this._defs);
  270. var points = xmlNode.getAttribute('points');
  271. if (points) {
  272. points = parsePoints(points);
  273. }
  274. var polyline = new Polyline({
  275. shape: {
  276. points: points || []
  277. }
  278. });
  279. return polyline;
  280. },
  281. 'image': function (xmlNode, parentGroup) {
  282. var img = new ZImage();
  283. inheritStyle(parentGroup, img);
  284. parseAttributes(xmlNode, img, this._defs);
  285. img.setStyle({
  286. image: xmlNode.getAttribute('xlink:href'),
  287. x: xmlNode.getAttribute('x'),
  288. y: xmlNode.getAttribute('y'),
  289. width: xmlNode.getAttribute('width'),
  290. height: xmlNode.getAttribute('height')
  291. });
  292. return img;
  293. },
  294. 'text': function (xmlNode, parentGroup) {
  295. var x = xmlNode.getAttribute('x') || 0;
  296. var y = xmlNode.getAttribute('y') || 0;
  297. var dx = xmlNode.getAttribute('dx') || 0;
  298. var dy = xmlNode.getAttribute('dy') || 0;
  299. this._textX = parseFloat(x) + parseFloat(dx);
  300. this._textY = parseFloat(y) + parseFloat(dy);
  301. var g = new Group();
  302. inheritStyle(parentGroup, g);
  303. parseAttributes(xmlNode, g, this._defs);
  304. return g;
  305. },
  306. 'tspan': function (xmlNode, parentGroup) {
  307. var x = xmlNode.getAttribute('x');
  308. var y = xmlNode.getAttribute('y');
  309. if (x != null) {
  310. // new offset x
  311. this._textX = parseFloat(x);
  312. }
  313. if (y != null) {
  314. // new offset y
  315. this._textY = parseFloat(y);
  316. }
  317. var dx = xmlNode.getAttribute('dx') || 0;
  318. var dy = xmlNode.getAttribute('dy') || 0;
  319. var g = new Group();
  320. inheritStyle(parentGroup, g);
  321. parseAttributes(xmlNode, g, this._defs);
  322. this._textX += dx;
  323. this._textY += dy;
  324. return g;
  325. },
  326. 'path': function (xmlNode, parentGroup) {
  327. // TODO svg fill rule
  328. // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule
  329. // path.style.globalCompositeOperation = 'xor';
  330. var d = xmlNode.getAttribute('d') || '';
  331. // Performance sensitive.
  332. var path = createFromString(d);
  333. inheritStyle(parentGroup, path);
  334. parseAttributes(xmlNode, path, this._defs);
  335. return path;
  336. }
  337. };
  338. var defineParsers = {
  339. 'lineargradient': function (xmlNode) {
  340. var x1 = parseInt(xmlNode.getAttribute('x1') || 0, 10);
  341. var y1 = parseInt(xmlNode.getAttribute('y1') || 0, 10);
  342. var x2 = parseInt(xmlNode.getAttribute('x2') || 10, 10);
  343. var y2 = parseInt(xmlNode.getAttribute('y2') || 0, 10);
  344. var gradient = new LinearGradient(x1, y1, x2, y2);
  345. _parseGradientColorStops(xmlNode, gradient);
  346. return gradient;
  347. },
  348. 'radialgradient': function (xmlNode) {
  349. }
  350. };
  351. function _parseGradientColorStops(xmlNode, gradient) {
  352. var stop = xmlNode.firstChild;
  353. while (stop) {
  354. if (stop.nodeType === 1) {
  355. var offset = stop.getAttribute('offset');
  356. if (offset.indexOf('%') > 0) { // percentage
  357. offset = parseInt(offset, 10) / 100;
  358. }
  359. else if (offset) { // number from 0 to 1
  360. offset = parseFloat(offset);
  361. }
  362. else {
  363. offset = 0;
  364. }
  365. var stopColor = stop.getAttribute('stop-color') || '#000000';
  366. gradient.addColorStop(offset, stopColor);
  367. }
  368. stop = stop.nextSibling;
  369. }
  370. }
  371. function inheritStyle(parent, child) {
  372. if (parent && parent.__inheritedStyle) {
  373. if (!child.__inheritedStyle) {
  374. child.__inheritedStyle = {};
  375. }
  376. defaults(child.__inheritedStyle, parent.__inheritedStyle);
  377. }
  378. }
  379. function parsePoints(pointsString) {
  380. var list = trim(pointsString).split(DILIMITER_REG);
  381. var points = [];
  382. for (var i = 0; i < list.length; i += 2) {
  383. var x = parseFloat(list[i]);
  384. var y = parseFloat(list[i + 1]);
  385. points.push([x, y]);
  386. }
  387. return points;
  388. }
  389. var attributesMap = {
  390. 'fill': 'fill',
  391. 'stroke': 'stroke',
  392. 'stroke-width': 'lineWidth',
  393. 'opacity': 'opacity',
  394. 'fill-opacity': 'fillOpacity',
  395. 'stroke-opacity': 'strokeOpacity',
  396. 'stroke-dasharray': 'lineDash',
  397. 'stroke-dashoffset': 'lineDashOffset',
  398. 'stroke-linecap': 'lineCap',
  399. 'stroke-linejoin': 'lineJoin',
  400. 'stroke-miterlimit': 'miterLimit',
  401. 'font-family': 'fontFamily',
  402. 'font-size': 'fontSize',
  403. 'font-style': 'fontStyle',
  404. 'font-weight': 'fontWeight',
  405. 'text-align': 'textAlign',
  406. 'alignment-baseline': 'textBaseline'
  407. };
  408. function parseAttributes(xmlNode, el, defs, onlyInlineStyle) {
  409. var zrStyle = el.__inheritedStyle || {};
  410. var isTextEl = el.type === 'text';
  411. // TODO Shadow
  412. if (xmlNode.nodeType === 1) {
  413. parseTransformAttribute(xmlNode, el);
  414. extend(zrStyle, parseStyleAttribute(xmlNode));
  415. if (!onlyInlineStyle) {
  416. for (var svgAttrName in attributesMap) {
  417. if (attributesMap.hasOwnProperty(svgAttrName)) {
  418. var attrValue = xmlNode.getAttribute(svgAttrName);
  419. if (attrValue != null) {
  420. zrStyle[attributesMap[svgAttrName]] = attrValue;
  421. }
  422. }
  423. }
  424. }
  425. }
  426. var elFillProp = isTextEl ? 'textFill' : 'fill';
  427. var elStrokeProp = isTextEl ? 'textStroke' : 'stroke';
  428. el.style = el.style || new Style();
  429. var elStyle = el.style;
  430. zrStyle.fill != null && elStyle.set(elFillProp, getPaint(zrStyle.fill, defs));
  431. zrStyle.stroke != null && elStyle.set(elStrokeProp, getPaint(zrStyle.stroke, defs));
  432. each([
  433. 'lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize'
  434. ], function (propName) {
  435. var elPropName = (propName === 'lineWidth' && isTextEl) ? 'textStrokeWidth' : propName;
  436. zrStyle[propName] != null && elStyle.set(elPropName, parseFloat(zrStyle[propName]));
  437. });
  438. if (!zrStyle.textBaseline || zrStyle.textBaseline === 'auto') {
  439. zrStyle.textBaseline = 'alphabetic';
  440. }
  441. if (zrStyle.textBaseline === 'alphabetic') {
  442. zrStyle.textBaseline = 'bottom';
  443. }
  444. if (zrStyle.textAlign === 'start') {
  445. zrStyle.textAlign = 'left';
  446. }
  447. if (zrStyle.textAlign === 'end') {
  448. zrStyle.textAlign = 'right';
  449. }
  450. each(['lineDashOffset', 'lineCap', 'lineJoin',
  451. 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign', 'textBaseline'
  452. ], function (propName) {
  453. zrStyle[propName] != null && elStyle.set(propName, zrStyle[propName]);
  454. });
  455. if (zrStyle.lineDash) {
  456. el.style.lineDash = trim(zrStyle.lineDash).split(DILIMITER_REG);
  457. }
  458. if (elStyle[elStrokeProp] && elStyle[elStrokeProp] !== 'none') {
  459. // enable stroke
  460. el[elStrokeProp] = true;
  461. }
  462. el.__inheritedStyle = zrStyle;
  463. }
  464. var urlRegex = /url\(\s*#(.*?)\)/;
  465. function getPaint(str, defs) {
  466. // if (str === 'none') {
  467. // return;
  468. // }
  469. var urlMatch = defs && str && str.match(urlRegex);
  470. if (urlMatch) {
  471. var url = trim(urlMatch[1]);
  472. var def = defs[url];
  473. return def;
  474. }
  475. return str;
  476. }
  477. var transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\(([\-\s0-9\.e,]*)\)/g;
  478. function parseTransformAttribute(xmlNode, node) {
  479. var transform = xmlNode.getAttribute('transform');
  480. if (transform) {
  481. transform = transform.replace(/,/g, ' ');
  482. var m = null;
  483. var transformOps = [];
  484. transform.replace(transformRegex, function (str, type, value) {
  485. transformOps.push(type, value);
  486. });
  487. for (var i = transformOps.length - 1; i > 0; i -= 2) {
  488. var value = transformOps[i];
  489. var type = transformOps[i - 1];
  490. m = m || matrix.create();
  491. switch (type) {
  492. case 'translate':
  493. value = trim(value).split(DILIMITER_REG);
  494. matrix.translate(m, m, [parseFloat(value[0]), parseFloat(value[1] || 0)]);
  495. break;
  496. case 'scale':
  497. value = trim(value).split(DILIMITER_REG);
  498. matrix.scale(m, m, [parseFloat(value[0]), parseFloat(value[1] || value[0])]);
  499. break;
  500. case 'rotate':
  501. value = trim(value).split(DILIMITER_REG);
  502. matrix.rotate(m, m, parseFloat(value[0]));
  503. break;
  504. case 'skew':
  505. value = trim(value).split(DILIMITER_REG);
  506. console.warn('Skew transform is not supported yet');
  507. break;
  508. case 'matrix':
  509. var value = trim(value).split(DILIMITER_REG);
  510. m[0] = parseFloat(value[0]);
  511. m[1] = parseFloat(value[1]);
  512. m[2] = parseFloat(value[2]);
  513. m[3] = parseFloat(value[3]);
  514. m[4] = parseFloat(value[4]);
  515. m[5] = parseFloat(value[5]);
  516. break;
  517. }
  518. }
  519. node.setLocalTransform(m);
  520. }
  521. }
  522. // Value may contain space.
  523. var styleRegex = /([^\s:;]+)\s*:\s*([^:;]+)/g;
  524. function parseStyleAttribute(xmlNode) {
  525. var style = xmlNode.getAttribute('style');
  526. var result = {};
  527. if (!style) {
  528. return result;
  529. }
  530. var styleList = {};
  531. styleRegex.lastIndex = 0;
  532. var styleRegResult;
  533. while ((styleRegResult = styleRegex.exec(style)) != null) {
  534. styleList[styleRegResult[1]] = styleRegResult[2];
  535. }
  536. for (var svgAttrName in attributesMap) {
  537. if (attributesMap.hasOwnProperty(svgAttrName) && styleList[svgAttrName] != null) {
  538. result[attributesMap[svgAttrName]] = styleList[svgAttrName];
  539. }
  540. }
  541. return result;
  542. }
  543. /**
  544. * @param {Array.<number>} viewBoxRect
  545. * @param {number} width
  546. * @param {number} height
  547. * @return {Object} {scale, position}
  548. */
  549. export function makeViewBoxTransform(viewBoxRect, width, height) {
  550. var scaleX = width / viewBoxRect.width;
  551. var scaleY = height / viewBoxRect.height;
  552. var scale = Math.min(scaleX, scaleY);
  553. // preserveAspectRatio 'xMidYMid'
  554. var viewBoxScale = [scale, scale];
  555. var viewBoxPosition = [
  556. -(viewBoxRect.x + viewBoxRect.width / 2) * scale + width / 2,
  557. -(viewBoxRect.y + viewBoxRect.height / 2) * scale + height / 2
  558. ];
  559. return {
  560. scale: viewBoxScale,
  561. position: viewBoxPosition
  562. };
  563. }
  564. /**
  565. * @param {string|XMLElement} xml
  566. * @param {Object} [opt]
  567. * @param {number} [opt.width] Default width if svg width not specified or is a percent value.
  568. * @param {number} [opt.height] Default height if svg height not specified or is a percent value.
  569. * @param {boolean} [opt.ignoreViewBox]
  570. * @param {boolean} [opt.ignoreRootClip]
  571. * @return {Object} result:
  572. * {
  573. * root: Group, The root of the the result tree of zrender shapes,
  574. * width: number, the viewport width of the SVG,
  575. * height: number, the viewport height of the SVG,
  576. * viewBoxRect: {x, y, width, height}, the declared viewBox rect of the SVG, if exists,
  577. * viewBoxTransform: the {scale, position} calculated by viewBox and viewport, is exists.
  578. * }
  579. */
  580. export function parseSVG(xml, opt) {
  581. var parser = new SVGParser();
  582. return parser.parse(xml, opt);
  583. }