/Assets/SVG_Importer/Plugins/Core/Implementation/XML Parser/SVGParser.cs

https://gitlab.com/wyattgable/Turtle_Time · C# · 697 lines · 611 code · 54 blank · 32 comment · 78 complexity · d730683e62abd39d75ac6473055ac2c4 MD5 · raw file

  1. // Copyright (C) 2015 Jaroslav Stehlik - All Rights Reserved
  2. // This code can only be used under the standard Unity Asset Store End User License Agreement
  3. // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
  4. using System.IO;
  5. using System.Text;
  6. using System.Collections.Generic;
  7. using UnityEngine;
  8. namespace SVGImporter.Document
  9. {
  10. using Rendering;
  11. using Utils;
  12. public enum SVGNodeName
  13. {
  14. Rect,
  15. Line,
  16. Circle,
  17. Ellipse,
  18. PolyLine,
  19. Polygon,
  20. Path,
  21. SVG,
  22. G,
  23. LinearGradient,
  24. RadialGradient,
  25. ConicalGradient,
  26. Defs,
  27. Title,
  28. Desc,
  29. Stop,
  30. Symbol,
  31. ClipPath,
  32. Mask,
  33. Image,
  34. Use,
  35. Style
  36. }
  37. public class Node
  38. {
  39. public Node parent;
  40. public List<Node> children;
  41. public SVGNodeName name;
  42. public AttributeList attributes;
  43. public int depth;
  44. public string content;
  45. public Node(SVGNodeName name, AttributeList attributes, int depth)
  46. {
  47. this.parent = null;
  48. this.children = new List<Node>();
  49. this.name = name;
  50. this.attributes = attributes;
  51. this.depth = depth;
  52. }
  53. public List<Node> GetNodes()
  54. {
  55. List<Node> nodes = new List<Node>();
  56. GetNodesInternal(this, nodes);
  57. return nodes;
  58. }
  59. protected void GetNodesInternal(Node node, List<Node> nodes)
  60. {
  61. if (node == null)
  62. return;
  63. nodes.Add(node);
  64. int nodeChildrenCount = node.children.Count;
  65. for (int i = 0; i < nodeChildrenCount; i++)
  66. {
  67. GetNodesInternal(node.children [i], nodes);
  68. }
  69. if (node is BlockOpenNode)
  70. {
  71. //Lookup(name), )
  72. Node endNode = new BlockCloseNode(node.name, new AttributeList(), node.depth);
  73. nodes.Add(endNode);
  74. }
  75. }
  76. }
  77. public class InlineNode : Node
  78. {
  79. public InlineNode(SVGNodeName name, AttributeList attributes, int depth) : base(name, attributes, depth)
  80. {
  81. }
  82. }
  83. public class BlockOpenNode : Node
  84. {
  85. public BlockOpenNode(SVGNodeName name, AttributeList attributes, int depth) : base(name, attributes, depth)
  86. {
  87. }
  88. }
  89. public class BlockCloseNode : Node
  90. {
  91. public BlockCloseNode(SVGNodeName name, AttributeList attributes, int depth) : base(name, attributes, depth)
  92. {
  93. }
  94. }
  95. public class SVGParser : SmallXmlParser.IContentHandler
  96. {
  97. public static Dictionary<string, Node> _defs;
  98. private SmallXmlParser _parser = new SmallXmlParser();
  99. private int _currentDepth = 0;
  100. private Node _lastParent;
  101. private static string STYLE_BLOCK;
  102. public SVGParser()
  103. {
  104. }
  105. public static void Clear()
  106. {
  107. if (_defs != null)
  108. {
  109. _defs.Clear();
  110. _defs = null;
  111. }
  112. if(SVGAssetImport.errors != null)
  113. {
  114. SVGAssetImport.errors.Clear();
  115. SVGAssetImport.errors = null;
  116. }
  117. }
  118. public static void Init()
  119. {
  120. if(SVGAssetImport.errors == null)
  121. {
  122. SVGAssetImport.errors = new List<SVGError>();
  123. } else {
  124. SVGAssetImport.errors.Clear();
  125. }
  126. if (_defs == null)
  127. {
  128. _defs = new Dictionary<string, Node>();
  129. } else {
  130. _defs.Clear();
  131. }
  132. }
  133. public SVGParser(string text)
  134. {
  135. _parser.Parse(new StringReader(text), this);
  136. }
  137. public List<Node> nodes = new List<Node>();
  138. public void AddNode(Node node)
  139. {
  140. // Debug.Log("Add Node: "+node.name+", "+node);
  141. nodes.Add(node);
  142. }
  143. private int idx = 0;
  144. public Node node
  145. {
  146. get { return nodes [idx]; }
  147. }
  148. public bool Next()
  149. {
  150. idx++;
  151. return !isEOF;
  152. }
  153. public bool isEOF
  154. {
  155. get
  156. {
  157. return idx >= nodes.Count;
  158. }
  159. }
  160. public void OnStartParsing(SmallXmlParser parser)
  161. {
  162. idx = 0;
  163. _currentDepth = 0;
  164. _lastParent = null;
  165. if (dontPutInNodes == null)
  166. {
  167. dontPutInNodes = new List<SVGNodeName>();
  168. } else
  169. {
  170. dontPutInNodes.Clear();
  171. }
  172. }
  173. static List<SVGNodeName> dontPutInNodes = new List<SVGNodeName>();
  174. void DontPutInNodesAdd(Node node)
  175. {
  176. if(node is InlineNode) return;
  177. dontPutInNodes.Add(node.name);
  178. }
  179. void DontPutInNodesRemove(Node node)
  180. {
  181. if(node is InlineNode) return;
  182. dontPutInNodes.RemoveAt(dontPutInNodes.Count - 1);
  183. }
  184. public void OnNode(Node node)
  185. {
  186. string definitionID = node.attributes.GetValue("id");
  187. if (!string.IsNullOrEmpty(definitionID))
  188. {
  189. if (_defs.ContainsKey(definitionID))
  190. {
  191. _defs [definitionID] = node;
  192. Debug.LogWarning("Element: " + node.name + ", ID: " + definitionID + " already exists! Overwriting with new element!");
  193. } else
  194. {
  195. _defs.Add(definitionID, node);
  196. }
  197. }
  198. switch (node.name)
  199. {
  200. case SVGNodeName.LinearGradient:
  201. case SVGNodeName.RadialGradient:
  202. case SVGNodeName.ConicalGradient:
  203. case SVGNodeName.Stop:
  204. AddNode(node);
  205. return;
  206. case SVGNodeName.Defs:
  207. DontPutInNodesAdd(node);
  208. break;
  209. case SVGNodeName.Symbol:
  210. DontPutInNodesAdd(node);
  211. // Debug.LogError ("Unsupported Element type Symbol");
  212. /*
  213. #if UNITY_EDITOR
  214. if(!SVGAssetImport.errors.Contains(SVGError.Symbol))
  215. SVGAssetImport.errors.Add(SVGError.Symbol);
  216. #endif
  217. */
  218. break;
  219. case SVGNodeName.Image:
  220. DontPutInNodesAdd(node);
  221. // Debug.LogError ("Unsupported Element type Image");
  222. #if UNITY_EDITOR
  223. if(!SVGAssetImport.errors.Contains(SVGError.Image))
  224. SVGAssetImport.errors.Add(SVGError.Image);
  225. #endif
  226. break;
  227. case SVGNodeName.ClipPath:
  228. DontPutInNodesAdd(node);
  229. // Debug.LogError ("Unsupported Element type Clip Path");
  230. #if UNITY_EDITOR
  231. if(!SVGAssetImport.errors.Contains(SVGError.ClipPath))
  232. SVGAssetImport.errors.Add(SVGError.ClipPath);
  233. #endif
  234. break;
  235. case SVGNodeName.Mask:
  236. DontPutInNodesAdd(node);
  237. // Debug.LogError ("Unsupported Element type Mask");
  238. #if UNITY_EDITOR
  239. if(!SVGAssetImport.errors.Contains(SVGError.Mask))
  240. SVGAssetImport.errors.Add(SVGError.Mask);
  241. #endif
  242. break;
  243. default:
  244. if (dontPutInNodes.Count == 0)
  245. {
  246. AddNode(node);
  247. }
  248. break;
  249. }
  250. }
  251. public void OnInlineElement(string name, AttributeList attrs)
  252. {
  253. Node node = new InlineNode(Lookup(name), new AttributeList(attrs), _currentDepth);
  254. // Debug.Log("OnInlineElement: "+name);
  255. node.parent = _lastParent;
  256. if (_lastParent != null)
  257. {
  258. _lastParent.children.Add(node);
  259. }
  260. OnNode(node);
  261. }
  262. public void OnStartElement(string name, AttributeList attrs)
  263. {
  264. Node node = new BlockOpenNode(Lookup(name), new AttributeList(attrs), _currentDepth++);
  265. // Debug.Log("OnStartElement: "+name);
  266. node.parent = _lastParent;
  267. if (_lastParent != null)
  268. {
  269. _lastParent.children.Add(node);
  270. }
  271. _lastParent = node;
  272. OnNode(node);
  273. //Debug.Log("OnStartElement: "+node.name+", depth: "+node.depth);
  274. }
  275. public void OnEndElement(string name)
  276. {
  277. Node node = new BlockCloseNode(Lookup(name), new AttributeList(), --_currentDepth);
  278. // Debug.Log("OnEndElement: "+name);
  279. if (_lastParent != null)
  280. {
  281. _lastParent = _lastParent.parent;
  282. } else
  283. {
  284. _lastParent = null;
  285. }
  286. node.parent = _lastParent;
  287. //Debug.Log("OnEndElement: "+node.name+", depth: "+node.depth);
  288. switch (node.name)
  289. {
  290. case SVGNodeName.LinearGradient:
  291. case SVGNodeName.RadialGradient:
  292. case SVGNodeName.ConicalGradient:
  293. AddNode(node);
  294. return;
  295. case SVGNodeName.Defs:
  296. DontPutInNodesRemove(node);
  297. break;
  298. case SVGNodeName.Symbol:
  299. DontPutInNodesRemove(node);
  300. break;
  301. case SVGNodeName.Image:
  302. DontPutInNodesRemove(node);
  303. break;
  304. case SVGNodeName.ClipPath:
  305. DontPutInNodesRemove(node);
  306. break;
  307. case SVGNodeName.Mask:
  308. DontPutInNodesRemove(node);
  309. break;
  310. default:
  311. if (dontPutInNodes.Count == 0)
  312. {
  313. AddNode(node);
  314. }
  315. break;
  316. }
  317. }
  318. public bool IsInlineElement(Node node)
  319. {
  320. switch (node.name)
  321. {
  322. case SVGNodeName.Circle:
  323. case SVGNodeName.Ellipse:
  324. case SVGNodeName.Line:
  325. case SVGNodeName.Path:
  326. case SVGNodeName.Polygon:
  327. case SVGNodeName.PolyLine:
  328. case SVGNodeName.Rect:
  329. case SVGNodeName.Stop:
  330. return true;
  331. }
  332. return false;
  333. }
  334. public void OnStyleElement(string name, AttributeList attrs, string style)
  335. {
  336. Node node = new InlineNode(Lookup(name), new AttributeList(attrs), _currentDepth);
  337. node.content = style;
  338. node.parent = _lastParent;
  339. if (_lastParent != null)
  340. {
  341. _lastParent.children.Add(node);
  342. }
  343. AddNode(node);
  344. }
  345. public void GetElementList(List<object> elementList, SVGPaintable paintable, SVGTransformList summaryTransformList)
  346. {
  347. bool exitFlag = false;
  348. while (!exitFlag && Next())
  349. {
  350. //while (Next())
  351. if (node is BlockCloseNode)
  352. {
  353. exitFlag = true;
  354. continue;
  355. }
  356. //Debug.Log(node.name);
  357. switch (node.name)
  358. {
  359. case SVGNodeName.Rect:
  360. {
  361. elementList.Add(new SVGRectElement(node,
  362. summaryTransformList,
  363. paintable));
  364. break;
  365. }
  366. case SVGNodeName.Line:
  367. {
  368. elementList.Add(new SVGLineElement(node,
  369. summaryTransformList,
  370. paintable));
  371. break;
  372. }
  373. case SVGNodeName.Circle:
  374. {
  375. elementList.Add(new SVGCircleElement(node,
  376. summaryTransformList,
  377. paintable));
  378. break;
  379. }
  380. case SVGNodeName.Ellipse:
  381. {
  382. elementList.Add(new SVGEllipseElement(node,
  383. summaryTransformList,
  384. paintable));
  385. break;
  386. }
  387. case SVGNodeName.PolyLine:
  388. {
  389. elementList.Add(new SVGPolylineElement(node,
  390. summaryTransformList,
  391. paintable));
  392. break;
  393. }
  394. case SVGNodeName.Polygon:
  395. {
  396. elementList.Add(new SVGPolygonElement(node,
  397. summaryTransformList,
  398. paintable));
  399. break;
  400. }
  401. case SVGNodeName.Path:
  402. {
  403. elementList.Add(new SVGPathElement(node,
  404. summaryTransformList,
  405. paintable));
  406. break;
  407. }
  408. case SVGNodeName.SVG:
  409. {
  410. if(node is InlineNode) break;
  411. elementList.Add(new SVGElement(this,
  412. summaryTransformList,
  413. paintable));
  414. break;
  415. }
  416. case SVGNodeName.Symbol:
  417. {
  418. if(node is InlineNode) break;
  419. elementList.Add(new SVGElement(this,
  420. summaryTransformList,
  421. paintable));
  422. break;
  423. }
  424. case SVGNodeName.G:
  425. {
  426. if(node is InlineNode) break;
  427. elementList.Add(new SVGElement(this,
  428. summaryTransformList,
  429. paintable));
  430. break;
  431. }
  432. /*
  433. case SVGNodeName.ClipPath:
  434. {
  435. paintable.AppendClipPath(new SVGClipPathElement(this, node));
  436. break;
  437. }
  438. */
  439. case SVGNodeName.LinearGradient:
  440. {
  441. ResolveGradientLinks();
  442. paintable.AppendLinearGradient(new SVGLinearGradientElement(this, node));
  443. break;
  444. }
  445. case SVGNodeName.RadialGradient:
  446. {
  447. ResolveGradientLinks();
  448. paintable.AppendRadialGradient(new SVGRadialGradientElement(this, node));
  449. break;
  450. }
  451. case SVGNodeName.ConicalGradient:
  452. {
  453. ResolveGradientLinks();
  454. paintable.AppendConicalGradient(new SVGConicalGradientElement(this, node));
  455. break;
  456. }
  457. case SVGNodeName.Defs:
  458. {
  459. GetElementList(elementList, paintable, summaryTransformList);
  460. break;
  461. }
  462. case SVGNodeName.Title:
  463. {
  464. GetElementList(elementList, paintable, summaryTransformList);
  465. break;
  466. }
  467. case SVGNodeName.Desc:
  468. {
  469. GetElementList(elementList, paintable, summaryTransformList);
  470. break;
  471. }
  472. case SVGNodeName.Style:
  473. {
  474. paintable.AddCSS(node.content);
  475. break;
  476. }
  477. case SVGNodeName.Use:
  478. {
  479. // Debug.Log("Begin Use Command: " + node.attributes.GetValue("id"));
  480. string xlink = node.attributes.GetValue("xlink:href");
  481. if (!string.IsNullOrEmpty(xlink))
  482. {
  483. if (xlink [0] == '#') xlink = xlink.Remove(0, 1);
  484. if (_defs.ContainsKey(xlink))
  485. {
  486. Node definitionNode = _defs [xlink];
  487. if (definitionNode != null && definitionNode != node)
  488. {
  489. List<Node> injectNodes = definitionNode.GetNodes();
  490. if (injectNodes != null && injectNodes.Count > 0)
  491. {
  492. nodes [idx] = new BlockOpenNode(SVGNodeName.Use, node.attributes, node.depth);
  493. injectNodes.Add(new BlockCloseNode(SVGNodeName.Use, new AttributeList(), node.depth));
  494. nodes.InsertRange(idx + 1, injectNodes);
  495. elementList.Add(new SVGElement(this,
  496. summaryTransformList,
  497. paintable));
  498. }
  499. }
  500. }
  501. }
  502. break;
  503. }
  504. }
  505. }
  506. }
  507. protected void ResolveGradientLinks()
  508. {
  509. string xlink = node.attributes.GetValue("xlink:href");
  510. if (!string.IsNullOrEmpty(xlink))
  511. {
  512. if (xlink [0] == '#') xlink = xlink.Remove(0, 1);
  513. if (_defs.ContainsKey(xlink))
  514. {
  515. Node definitionNode = _defs [xlink];
  516. if (definitionNode != null && definitionNode != node)
  517. {
  518. MergeNodeAttributes(definitionNode, node);
  519. List<Node> injectNodes = definitionNode.GetNodes();
  520. if (injectNodes != null && injectNodes.Count > 0)
  521. {
  522. bool createOpenNode = nodes[idx] is InlineNode;
  523. if(createOpenNode)
  524. {
  525. nodes[idx] = new BlockOpenNode(nodes[idx].name, nodes[idx].attributes, nodes[idx].depth);
  526. }
  527. injectNodes.RemoveAt(0);
  528. if(injectNodes.Count > 0)
  529. {
  530. injectNodes.RemoveAt(injectNodes.Count - 1);
  531. }
  532. if(injectNodes.Count > 0)
  533. {
  534. nodes[idx].children = injectNodes;
  535. nodes.InsertRange(idx + 1, injectNodes);
  536. }
  537. if(createOpenNode)
  538. {
  539. nodes.Insert(idx + 1 + injectNodes.Count, new BlockCloseNode(node.name, new AttributeList(), node.depth));
  540. }
  541. }
  542. }
  543. }
  544. }
  545. }
  546. private static void MergeNodeAttributes(Node source, Node target)
  547. {
  548. Dictionary<string, string> sourceAttributes = source.attributes.Get;
  549. Dictionary<string, string> targetAttributes = target.attributes.Get;
  550. foreach(KeyValuePair<string, string> item in sourceAttributes)
  551. {
  552. if(item.Key == "id" || item.Key == "xlink") continue;
  553. if(targetAttributes.ContainsKey(item.Key))
  554. {
  555. targetAttributes[item.Key] = item.Value;
  556. } else {
  557. targetAttributes.Add(item.Key, item.Value);
  558. }
  559. }
  560. }
  561. private static SVGNodeName Lookup(string name)
  562. {
  563. SVGNodeName retVal = SVGNodeName.G;
  564. switch (name.ToLower())
  565. {
  566. case "rect":
  567. retVal = SVGNodeName.Rect;
  568. break;
  569. case "line":
  570. retVal = SVGNodeName.Line;
  571. break;
  572. case "circle":
  573. retVal = SVGNodeName.Circle;
  574. break;
  575. case "ellipse":
  576. retVal = SVGNodeName.Ellipse;
  577. break;
  578. case "polyline":
  579. retVal = SVGNodeName.PolyLine;
  580. break;
  581. case "polygon":
  582. retVal = SVGNodeName.Polygon;
  583. break;
  584. case "path":
  585. retVal = SVGNodeName.Path;
  586. break;
  587. case "svg":
  588. retVal = SVGNodeName.SVG;
  589. break;
  590. case "g":
  591. retVal = SVGNodeName.G;
  592. break;
  593. case "lineargradient":
  594. retVal = SVGNodeName.LinearGradient;
  595. break;
  596. case "radialgradient":
  597. retVal = SVGNodeName.RadialGradient;
  598. break;
  599. case "conicalgradient":
  600. retVal = SVGNodeName.ConicalGradient;
  601. break;
  602. case "defs":
  603. retVal = SVGNodeName.Defs;
  604. break;
  605. case "title":
  606. retVal = SVGNodeName.Title;
  607. break;
  608. case "desc":
  609. retVal = SVGNodeName.Desc;
  610. break;
  611. case "stop":
  612. retVal = SVGNodeName.Stop;
  613. break;
  614. case "symbol":
  615. retVal = SVGNodeName.Symbol;
  616. break;
  617. case "clippath":
  618. retVal = SVGNodeName.ClipPath;
  619. break;
  620. case "mask":
  621. retVal = SVGNodeName.Mask;
  622. break;
  623. case "image":
  624. retVal = SVGNodeName.Image;
  625. break;
  626. case "use":
  627. retVal = SVGNodeName.Use;
  628. break;
  629. case "style":
  630. retVal = SVGNodeName.Style;
  631. break;
  632. default:
  633. retVal = SVGNodeName.G;
  634. #if UNITY_EDITOR
  635. // Debug.LogError("Unknown element type '" + name + "'!");
  636. // SVGAssetImport.errors.Add(SVGError.Unknown);
  637. #endif
  638. break;
  639. }
  640. return retVal;
  641. }
  642. }
  643. }