parseSVG.js 19 KB

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