21 March 2013

C# - Convert an SVG path to a GraphicsPath

The following algorithm takes the data string of an SVG path and converts it to a GDI+ GraphicsPath.
char command = ' ';
int[] args = null;
Point initPoint = new Point(); //Initial point of the subpath
Point endPoint = new Point(); //End point of the (previous) command
Point currentPoint = new Point(); //Current point of the command
Point point = new Point(); //Parsing variable
GraphicsPath path = new GraphicsPath();
List points = new List();

var functions = Regex.Split(data, @"(?=[A-Za-z])").Where(c => c.HasValue());
foreach (string function in functions) {
  command = function[0];
  args = Regex.Split(function.Remove(0, 1), @"[\s,]|(?=-)").Where(c => c.HasValue()).Select(c => c.ToInt()).ToArray();
  switch (command) {
    case 'M':
    case 'm': //Open subpath
      initPoint = new Point {
        X = (char.IsUpper(command) ? translation.X : currentPoint.X) + args[0] * scale.X,
        Y = (char.IsUpper(command) ? translation.Y : currentPoint.Y) + args[1] * scale.Y
      };
      endPoint = initPoint;
      currentPoint = initPoint;
      break;
    case 'Z':
    case 'z':
      path.CloseFigure();
      currentPoint = initPoint; //Init point becomes the current point
      break;
    case 'C':
    case 'c':
      points.Clear();
      points.Add(endPoint);
      int n = 0;
      for (int i = 0; i < args.Length; i += 2) {
        point = new Point {
          X = (char.IsUpper(command) ? translation.X : currentPoint.X) + args[i] * scale.X,
          Y = (char.IsUpper(command) ? translation.Y : currentPoint.Y) + args[i + 1] * scale.Y
        };
        points.Add(point);
        if (++n >= 3) { //Not a control point
          currentPoint = point;
          endPoint = point;
          n = 0;
        }
      }
      path.AddBeziers(points.ToArray());
      break;
    case 'L':
    case 'l':
      points.Clear();
      points.Add(endPoint);
      for (int i = 0; i < args.Length; i += 2) {
        point = new Point {
          X = (char.IsUpper(command) ? translation.X : currentPoint.X) + args[i] * scale.X,
          Y = (char.IsUpper(command) ? translation.Y : currentPoint.Y) + args[i + 1] * scale.Y
        };
        points.Add(point);
        currentPoint = point;
      }
      endPoint = currentPoint;
      path.AddLines(points.ToArray());
      break;
  }
}
Note that the points translation and scale were also taken from SVG and are used to displace the path points.

4 comments:

  1. What is translation.x and scale.x in above code. I cant find its declaration

    ReplyDelete
  2. As the last line below the method says, "translation" and "scale" are properties of the SVG path that were taken from the SVG file and used to transform the path.

    ReplyDelete
  3. Hello EGS,
    I have only this line here:
    "M 291 607 Q 287 610 282 613 Q 258 630 243 623 Q 237 617 241 597 Q 253 553 250 524 Q 243 438 241 321 Q 237 131 249 93 Q 255 51 296 22 Q 383 -41 606 -40 Q 651 -39 690 -33 Q 826 -17 941 53 Q 960 69 949 94 Q 937 129 923 229 Q 923 245 916 251 Q 909 255 906 237 Q 872 131 851 98 Q 819 63 735 40 Q 538 -12 374 47 Q 312 74 296 112 Q 274 161 284 276 Q 285 288 288 331 Q 302 551 306 572 Q 308 578 308 580 C 310 598 310 598 291 607 Z"

    Where do I find the "translation" and the "scale"? Any idea?

    ReplyDelete
    Replies
    1. Hi Eugen,

      What you have there is only path data.
      Translation and scale can be found in the attribute "transform".
      See tutorials.jenkov.com/svg/svg-transformation.html.

      Delete