FileImporter.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Text.RegularExpressions;
  8. using System.Threading.Tasks;
  9. namespace SketchAssistant
  10. {
  11. public class FileImporter
  12. {
  13. /// <summary>
  14. /// pointer to the running instance of main program
  15. /// </summary>
  16. Form1 program;
  17. /// <summary>
  18. /// scale factor for coordinates of svg file
  19. /// </summary>
  20. double scale;
  21. /// <summary>
  22. /// line pointer for the current svg document
  23. /// </summary>
  24. int i;
  25. /// <summary>
  26. /// array containing all characters interpreted as whitespaces which seperate words/tokens in the input file
  27. /// </summary>
  28. readonly char[] whitespaces = new char[] { ' ' , ',' };
  29. public FileImporter(Form1 newProgram)
  30. {
  31. program = newProgram;
  32. }
  33. /// <summary>
  34. /// parses a drawing consisting of line objects, given as a file in the application specific .isad format
  35. /// </summary>
  36. /// <param name="fileName">the path of the input file</param>
  37. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  38. public (int, int, List<Line>) ParseISADInputFile(String fileName)
  39. {
  40. return ParseISADInput(System.IO.File.ReadAllLines(fileName));
  41. }
  42. /// <summary>
  43. /// parses a drawing consisting of line objects, given as the content of a .isad file, seperated into lines
  44. /// </summary>
  45. /// <param name="allLines">an array holding all lines of the input file</param>
  46. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  47. private (int, int, List<Line>) ParseISADInput(String[] allLines)
  48. {
  49. if (allLines.Length == 0)
  50. {
  51. throw new FileImporterException("file is empty", "", -1);
  52. }
  53. if (!"drawing".Equals(allLines[0]))
  54. {
  55. throw new FileImporterException("file is not an interactive sketch assistant drawing", ".isad files have to start with the 'drawing' token", 1);
  56. }
  57. if (!"enddrawing".Equals(allLines[allLines.Length - 1]))
  58. {
  59. throw new FileImporterException("unterminated drawing definition", ".isad files have to end with the 'enddrawing' token", allLines.Length);
  60. }
  61. (int, int) dimensions = ParseISADHeader(allLines);
  62. List<Line> picture = ParseISADBody(allLines, dimensions.Item1, dimensions.Item2);
  63. return (dimensions.Item1, dimensions.Item2, picture);
  64. }
  65. /// <summary>
  66. /// parses the first two lines of an input file in .isad format
  67. /// </summary>
  68. /// <param name="allLines">the input file as an array of lines</param>
  69. /// <returns>the width and height of the left canvas</returns>
  70. private (int, int) ParseISADHeader(String[] allLines)
  71. {
  72. int width;
  73. int height;
  74. if (!(allLines.Length > 1) || !Regex.Match(allLines[1], @"^\d+x?\d+$", RegexOptions.None).Success)
  75. {
  76. throw new FileImporterException("invalid or missing canvas size definition", "format: [width]x[heigth]", 2);
  77. }
  78. String[] size = allLines[1].Split('x');
  79. width = Convert.ToInt32(size[0]);
  80. height = Convert.ToInt32(size[1]);
  81. return (width, height);
  82. }
  83. /// <summary>
  84. /// parses all line entries of an input file in .isad format
  85. /// </summary>
  86. /// <param name="allLines">the input file as an array of lines</param>
  87. /// <returns>the parsed picture as a list of lines</returns>
  88. private List<Line> ParseISADBody(String[] allLines, int width, int height)
  89. {
  90. String lineStartString = "line";
  91. String lineEndString = "endline";
  92. List<Line> drawing = new List<Line>();
  93. //number of the line currently being parsed, enumeration starting at 0, body starts at the third line, therefore lin number 2
  94. int i = 2;
  95. //parse 'line' token and complete line definition
  96. int lineStartPointer = i;
  97. //holds the line number of the next expected beginning of a line definition, or of the enddrawing token
  98. while (lineStartString.Equals(allLines[i]))
  99. {
  100. //start parsing next line
  101. i++;
  102. List<Point> newLine = new List<Point>();
  103. while (!lineEndString.Equals(allLines[i]))
  104. {
  105. if (i == allLines.Length)
  106. {
  107. throw new FileImporterException("unterminated line definition", null, (i + 1));
  108. }
  109. //parse single point definition
  110. if (!Regex.Match(allLines[i], @"^\d+;\d+$", RegexOptions.None).Success)
  111. {
  112. throw new FileImporterException("invalid Point definition: wrong format", "format: [xCoordinate];[yCoordinate]", (i + 1) );
  113. }
  114. String[] coordinates = allLines[i].Split(';');
  115. //no errors possible, convertability to int already checked above
  116. int xCoordinate = Convert.ToInt32(coordinates[0]);
  117. int yCoordinate = Convert.ToInt32(coordinates[1]);
  118. if (xCoordinate < 0 || yCoordinate < 0 || xCoordinate > width - 1 || yCoordinate > height - 1)
  119. {
  120. throw new FileImporterException("invalid Point definition: point out of bounds", null, (i + 1) );
  121. }
  122. newLine.Add(new Point(xCoordinate, yCoordinate));
  123. //start parsing next line
  124. i++;
  125. }
  126. //"parse" 'endline' token, syntax already checked at the beginning, and start parsing next line
  127. i++;
  128. //add line to drawing
  129. drawing.Add(new Line(newLine));
  130. //update lineStartPointer to the presumable start of the next line
  131. lineStartPointer = i;
  132. }
  133. //check if end of body is reached after there are no more line definitions
  134. if(i != allLines.Length - 1)
  135. {
  136. throw new FileImporterException("missing or invalid line definition token", "line definitions start with the 'line' token", (i + 1));
  137. }
  138. //return parsed picture
  139. return drawing;
  140. }
  141. /// <summary>
  142. /// connection point for testing use only: calls ParseISADInput(String[] allLines) and directly passes the given argument (effectively bypassing the File Input functionality)
  143. /// </summary>
  144. /// <param name="allLines">an array holding all lines of the input file</param>
  145. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  146. public (int, int, List<Line>) ParseISADInputForTesting(String[] allLines)
  147. {
  148. return ParseISADInput(allLines);
  149. }
  150. /// <summary>
  151. /// parses a svg drawing, given as a .svg file
  152. /// </summary>
  153. /// <param name="fileName">the path of the input file</param>
  154. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  155. public (int, int, List<Line>) ParseSVGInputFile(String fileName, int windowWidth, int windowHeight)
  156. {
  157. return ParseSVGInput(System.IO.File.ReadAllLines(fileName), windowWidth, windowHeight);
  158. }
  159. /// <summary>
  160. /// parses a svg drawing, given as the content of a .svg file, seperated into lines
  161. /// </summary>
  162. /// <param name="allLines">an array holding all lines of the input file</param>
  163. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  164. private (int, int, List<Line>) ParseSVGInput(String[] allLines, double windowWidth, double windowHeight)
  165. {
  166. i = 0; //reset line pointer
  167. if (allLines.Length == 0) //check for empty file
  168. {
  169. throw new FileImporterException("file is empty", "", -1);
  170. }
  171. (int, int) sizedef = ParseSVGHeader(allLines); //parse svg file header and get internal coordinate range
  172. i++;
  173. int width; //width of the resulting picture in pixels
  174. int height; //height of the resulting picture in pixels
  175. if (windowWidth / windowHeight > sizedef.Item1 / sizedef.Item2) //height dominant, width has to be smaller than drawing window to preserve xy-scale
  176. {
  177. scale = windowHeight / sizedef.Item2;
  178. Console.WriteLine("scale: (heights) " + windowHeight + "/" + sizedef.Item2);
  179. Console.WriteLine("widths: " + windowWidth + "/" + sizedef.Item1);
  180. height = (int)Math.Round(windowHeight);
  181. width = (int) Math.Round(scale * sizedef.Item1);
  182. Console.WriteLine(width + "x" + height + " (" + scale + ")");
  183. }
  184. else //width dominant, height has to be smaller than drawing window to preserve xy-scale
  185. {
  186. scale = windowWidth / sizedef.Item1;
  187. Console.WriteLine("scale: (widths) " + windowWidth + "/" + sizedef.Item1);
  188. Console.WriteLine("heights: " + windowHeight + "/" + sizedef.Item2);
  189. width = (int)Math.Round(windowWidth);
  190. height = (int)Math.Round(scale * sizedef.Item2);
  191. Console.WriteLine(width + "x" + height + " (" + scale + ")");
  192. }
  193. for(int j=0; j < allLines.Length; j++)
  194. {
  195. allLines[j] = allLines[j].Trim(whitespaces);
  196. }
  197. List<Line> picture = ParseSVGBody(allLines); //parse whole svg drawing into list of lines
  198. return (width, height, picture);
  199. }
  200. /// <summary>
  201. /// parses the svg file header and returns the internal coordinate range of this drawing, and iterates i to point to the start of svg element definitions
  202. /// </summary>
  203. /// <param name="allLines">an array holding all lines of the input file</param>
  204. /// <returns>the internal coordinate range of this drawing</returns>
  205. private (int, int) ParseSVGHeader(String[] allLines)
  206. {
  207. while (!allLines[i].StartsWith("<svg")) //skip non-relevant metadata at start of svg file
  208. {
  209. i++;
  210. }
  211. String[] currentLine = allLines[i].Split(' ');
  212. int width= -1;
  213. int height= -1;
  214. for(int j= 0; j < currentLine.Length; j++)
  215. {
  216. if (currentLine[j].StartsWith("width"))
  217. {
  218. width = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
  219. }
  220. else if (currentLine[j].StartsWith("height"))
  221. {
  222. height = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
  223. }
  224. }
  225. if(width == -1)
  226. {
  227. throw new FileImporterException("missing width definition in SVG header", "the header should contain the \"width=...\" attribute", i+1);
  228. }
  229. if (height == -1)
  230. {
  231. throw new FileImporterException("missing height definition in SVG header", "the header should contain the \"height=...\" attribute", i + 1);
  232. }
  233. return (width, height);
  234. }
  235. /// <summary>
  236. /// parses all relevant svg element definitions and skips the ones not representable by the sketch assistant
  237. /// </summary>
  238. /// <param name="allLines">an array holding all lines of the input file</param>
  239. /// <returns>the parsed picture as a list of lines</returns>
  240. private List<Line> ParseSVGBody(String[] allLines)
  241. {
  242. List<Line> picture = new List<Line>();
  243. while (!allLines[i].StartsWith("</svg"))
  244. {
  245. List<Line> element = ParseSingleSVGElement(allLines);
  246. if (element != null)
  247. {
  248. picture.AddRange(element);
  249. }
  250. i++;
  251. }
  252. return picture;
  253. }
  254. /// <summary>
  255. /// parses one toplevel svg element
  256. /// </summary>
  257. /// <param name="allLines">an array holding all lines of the input file</param>
  258. /// <returns>the parsed Element as a list of lines</returns>
  259. private List<Line> ParseSingleSVGElement(string[] allLines)
  260. {
  261. String[] currentElement = GetCurrentElement(allLines);
  262. if (currentElement[currentElement.Length - 1].EndsWith("/>")) //single line element
  263. {
  264. return ParseSingleLineSVGElement(currentElement);
  265. }
  266. else //element containing sub-elements
  267. {
  268. return ParseMultiLineSVGElement(currentElement, allLines);
  269. }
  270. }
  271. /// <summary>
  272. /// parses a single toplevel svg element only taking one line
  273. /// </summary>
  274. /// <param name="allLines">an array holding all lines of the input file</param>
  275. /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
  276. private List<Line> ParseSingleLineSVGElement(string[] currentElement)
  277. {
  278. List<Point> points= null;
  279. List<Line> element = null;
  280. switch (currentElement[0])
  281. {
  282. case "<rect":
  283. points = parseRect(currentElement);
  284. break;
  285. case "<circle":
  286. points = parseCircle(currentElement);
  287. break;
  288. case "<ellipse":
  289. points = parseEllipse(currentElement);
  290. break;
  291. case "<line":
  292. points = parseLine(currentElement);
  293. break;
  294. case "<polyline":
  295. points = parsePolyline(currentElement);
  296. break;
  297. case "<polygon":
  298. points = parsePolygon(currentElement);
  299. break;
  300. case "<path":
  301. element = parsePath(currentElement);
  302. break;
  303. default: //unsupported svg element
  304. Console.WriteLine("unsupported element: " + currentElement[0] + currentElement[0].Length);
  305. return null;
  306. }
  307. if (element == null)
  308. {
  309. element = new List<Line>();
  310. element.Add(new Line(points));
  311. }
  312. return element;
  313. }
  314. /// <summary>
  315. /// parses a rectangle definition into a List of Points representing a single line around the rectangle (in clockwise direction)
  316. /// </summary>
  317. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  318. /// <returns>the parsed element as a List of Points</returns>
  319. private List<Point> parseRect(string[] currentElement)
  320. {
  321. double x = 0;
  322. double y = 0;
  323. double w = 0;
  324. double h = 0;
  325. double rx = 0;
  326. double ry = 0;
  327. for(int j= 0; j < currentElement.Length; j++)
  328. {
  329. if (currentElement[j].StartsWith("x="))
  330. {
  331. x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  332. }
  333. else if (currentElement[j].StartsWith("y="))
  334. {
  335. y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  336. }
  337. else if (currentElement[j].StartsWith("width="))
  338. {
  339. w = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  340. }
  341. else if (currentElement[j].StartsWith("height="))
  342. {
  343. h = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  344. }
  345. else if (currentElement[j].StartsWith("rx="))
  346. {
  347. rx = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  348. }
  349. else if (currentElement[j].StartsWith("ry="))
  350. {
  351. ry = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  352. }
  353. }
  354. List<Point> rect = new List<Point>();
  355. rect.Add(ScaleAndCreatePoint(x, y));
  356. rect.Add(ScaleAndCreatePoint(x + w, y));
  357. rect.Add(ScaleAndCreatePoint(x + w, y + h));
  358. rect.Add(ScaleAndCreatePoint(x, y + h));
  359. rect.Add(ScaleAndCreatePoint(x, y));
  360. Console.WriteLine("parsed point: " + x + ";" + y);
  361. return rect;
  362. }
  363. /// <summary>
  364. /// parses a circle definition into a List of Points
  365. /// </summary>
  366. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  367. /// <returns>the parsed element as a List of Points</returns>
  368. private List<Point> parseCircle(string[] currentElement)
  369. {
  370. double x = 0;
  371. double y = 0;
  372. double r = 0;
  373. for (int j = 0; j < currentElement.Length; j++)
  374. {
  375. if (currentElement[j].StartsWith("cx="))
  376. {
  377. x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  378. }
  379. else if (currentElement[j].StartsWith("cy="))
  380. {
  381. y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  382. }
  383. else if (currentElement[j].StartsWith("r="))
  384. {
  385. r = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  386. }
  387. }
  388. return SampleEllipse(x, y, r, r);
  389. }
  390. /// <summary>
  391. /// parses a ellipse definition into a List of Points
  392. /// </summary>
  393. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  394. /// <returns>the parsed element as a List of Points</returns>
  395. private List<Point> parseEllipse(string[] currentElement)
  396. {
  397. double x = 0;
  398. double y = 0;
  399. double rx = 0;
  400. double ry = 0;
  401. for (int j = 0; j < currentElement.Length; j++)
  402. {
  403. if (currentElement[j].StartsWith("cx="))
  404. {
  405. x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  406. }
  407. else if (currentElement[j].StartsWith("cy="))
  408. {
  409. y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  410. }
  411. else if (currentElement[j].StartsWith("rx="))
  412. {
  413. rx = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  414. }
  415. else if (currentElement[j].StartsWith("ry="))
  416. {
  417. ry = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  418. }
  419. }
  420. return SampleEllipse(x, y, rx, ry);
  421. }
  422. /// <summary>
  423. /// parses a line definition into a List of two Points
  424. /// </summary>
  425. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  426. /// <returns>the parsed element as a List of Points</returns>
  427. private List<Point> parseLine(string[] currentElement)
  428. {
  429. double x1 = 0;
  430. double y1 = 0;
  431. double x2 = 0;
  432. double y2 = 0;
  433. for (int j = 0; j < currentElement.Length; j++)
  434. {
  435. if (currentElement[j].StartsWith("x1="))
  436. {
  437. x1 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  438. }
  439. else if (currentElement[j].StartsWith("y1="))
  440. {
  441. y1 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  442. }
  443. else if (currentElement[j].StartsWith("x2="))
  444. {
  445. x2 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  446. }
  447. else if (currentElement[j].StartsWith("y2="))
  448. {
  449. y2 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  450. }
  451. }
  452. List<Point> line = new List<Point>();
  453. line.Add(ScaleAndCreatePoint(x1, y1));
  454. line.Add(ScaleAndCreatePoint(x2, y2));
  455. return line;
  456. }
  457. /// <summary>
  458. /// parses a polyline definition into a List of Points
  459. /// </summary>
  460. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  461. /// <returns>the parsed element as a List of Points</returns>
  462. private List<Point> parsePolyline(string[] currentElement)
  463. {
  464. String[] points = null;
  465. for (int j = 0; j < currentElement.Length; j++)
  466. {
  467. if (currentElement[j].StartsWith("points="))
  468. {
  469. List<String> pointDefs = new List<string>();
  470. pointDefs.Add(currentElement[j].Substring(8)); //parse first point coordinates by removing 'points="'
  471. j++;
  472. while (!currentElement[j].EndsWith("\""))
  473. {
  474. pointDefs.Add(currentElement[j]); //parse intermediate point coordinates
  475. j++;
  476. }
  477. pointDefs.Add(currentElement[j].Trim('"')); //parse last point coordinates by removing '"'
  478. points = pointDefs.ToArray();
  479. }
  480. }
  481. List<Point> polyline = new List<Point>();
  482. for (int k = 0; k < points.Length - 1; k += 2)
  483. {
  484. polyline.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k + 1], CultureInfo.InvariantCulture)));
  485. }
  486. return polyline;
  487. }
  488. /// <summary>
  489. /// parses a polygon definition into a List of Points
  490. /// </summary>
  491. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  492. /// <returns>the parsed element as a List of Points</returns>
  493. private List<Point> parsePolygon(string[] currentElement)
  494. {
  495. String[] points = null;
  496. for (int j = 0; j < currentElement.Length; j++)
  497. {
  498. if (currentElement[j].StartsWith("points="))
  499. {
  500. List<String> pointDefs = new List<string>();
  501. pointDefs.Add(currentElement[j].Substring(8)); //parse first point coordinates by removing 'points="'
  502. j++;
  503. while (!currentElement[j].EndsWith("\""))
  504. {
  505. pointDefs.Add(currentElement[j]); //parse intermediate point coordinates
  506. j++;
  507. }
  508. pointDefs.Add(currentElement[j].Trim('"')); //parse last point coordinates by removing '"'
  509. points = pointDefs.ToArray();
  510. }
  511. }
  512. List<Point> polygon = new List<Point>();
  513. for (int k = 0; k < points.Length - 1; k+=2)
  514. {
  515. polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k+1], CultureInfo.InvariantCulture)));
  516. Console.WriteLine("parsed point: " + points[k] + ";" + points[k + 1]);
  517. }
  518. polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[0], CultureInfo.InvariantCulture), Convert.ToDouble(points[1], CultureInfo.InvariantCulture))); //close polygon
  519. Console.WriteLine("parsed point: " + points[0] + ";" + points[1]);
  520. return polygon;
  521. }
  522. /// <summary>
  523. /// parses a path definition into a List of Points
  524. /// </summary>
  525. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  526. /// <returns>the parsed element as a List of Points</returns>
  527. private List<Line> parsePath(string[] currentElement)
  528. {
  529. List<String> pathElements = new List<string>();
  530. for (int j = 0; j < currentElement.Length; j++)
  531. {
  532. if (currentElement[j].StartsWith("d="))
  533. {
  534. pathElements.Add(currentElement[j].Substring(3)); //parse first path element by removing 'd="'
  535. j++;
  536. while (!currentElement[j].EndsWith("\""))
  537. {
  538. pathElements.Add(currentElement[j]); //parse intermediate path element
  539. j++;
  540. }
  541. pathElements.Add(currentElement[j].Trim('"')); //parse last path element by removing '"'
  542. }
  543. }
  544. List<Line> element = new List<Line>();
  545. List<Point> currentLine = new List<Point>();
  546. Point mirroredBezierPoint;
  547. pathElements = PreparePathElements(pathElements); //split pathElement list objects until every object is atomar (single character or single number (coordinate))
  548. //int k = 0; //index of active element in pathElements is always 0
  549. currentLine = parse_M(pathElements);
  550. while(!(pathElements.Count == 0){
  551. if (pathElements.First().Equals("M"))
  552. {
  553. element.Add(new Line(currentLine));
  554. currentLine = parse_M(pathElements);
  555. }
  556. else if (pathElements.First().Equals(""))
  557. {
  558. }
  559. }
  560. return element;
  561. }
  562. /// <summary>
  563. /// parses a hierarchical svg element and all its sub-elements
  564. /// </summary>
  565. /// <param name="currentElement">the definition of the top level element as whitespace seperated String[]</param>
  566. /// <param name="allLines">an array holding all lines of the input file</param>
  567. /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
  568. private List<Line> ParseMultiLineSVGElement(string[] currentElement, string[] allLines)
  569. {
  570. throw new NotImplementedException();
  571. }
  572. /// <summary>
  573. /// removes the name of the attribute aswell as the '="' at the beginning and the '"' or '">' at the end of an attribute definition
  574. /// </summary>
  575. /// <param name="definition">the definition from the svg file</param>
  576. /// <returns>the value of the attribute, as String (the part of definition contained between '"'s)</returns>
  577. private String ParseSingleSVGAttribute(String definition)
  578. {
  579. return definition.Split('"')[1];
  580. }
  581. /// <summary>
  582. /// fetches a single svg element definition that may extend ovr several lines of the input file, iterates i to point to the last line of the element definition
  583. /// </summary>
  584. /// <param name="allLines">an array holding all lines of the input file</param>
  585. /// <returns>the definition of the current svg element, as String[] split by whitespaces</returns>
  586. private String[] GetCurrentElement(String[] allLines)
  587. {
  588. List<String> currentElementTemp = allLines[i].Split(whitespaces).ToList();
  589. while (!currentElementTemp.Last().EndsWith(">"))
  590. {
  591. i++;
  592. currentElementTemp.AddRange(allLines[i].Split(whitespaces).ToList());
  593. }
  594. return currentElementTemp.ToArray();
  595. }
  596. /// <summary>
  597. /// applies the scale factor to the coordinates and creates a new Point
  598. /// </summary>
  599. /// <param name="x">unscaled x coordinate</param>
  600. /// <param name="y">unscaled y coordinate</param>
  601. /// <returns>new Point with scaled coordinates</returns>
  602. private Point ScaleAndCreatePoint(double x, double y)
  603. {
  604. return new Point((int)Math.Round(x * scale), (int)Math.Round(y * scale));
  605. }
  606. /// <summary>
  607. /// creates a representation of an ellipse as a List of Points by sampling the outline of the ellipse
  608. /// </summary>
  609. /// <param name="x">x coordinate of the center of the ellipse</param>
  610. /// <param name="y">y coordinate of the center of the ellipse</param>
  611. /// <param name="rx">x radius of the ellipse</param>
  612. /// <param name="ry">y radius of the ellipse</param>
  613. /// <returns>the parsed element as a List of Points</returns>
  614. private List<Point> SampleEllipse(double x, double y, double rx, double ry)
  615. {
  616. throw new NotImplementedException();
  617. }
  618. }
  619. }