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.

C# - Call Potrace and pass it a bitmap from code

The following snippet calls Potrace — a tool that converts raster images to vector images — and returns an SVG-string, without creating any files in between. I use this one in a drawing application I'm working on.
Process potrace = new Process {
  StartInfo = new ProcessStartInfo {
    FileName = "potrace.exe",
    Arguments = "-s -u 1", //SVG
    RedirectStandardInput = true,
    RedirectStandardOutput = true,
    RedirectStandardError = Program.IsDebug,
    UseShellExecute = false,
    CreateNoWindow = true,
    WindowStyle = ProcessWindowStyle.Hidden
  },
  EnableRaisingEvents = false
};

StringBuilder svgBuilder = new StringBuilder();
potrace.OutputDataReceived += (object sender2, DataReceivedEventArgs e2) => {
  svgBuilder.AppendLine(e2.Data);
};
if (Program.IsDebug) {
  potrace.ErrorDataReceived += (object sender2, DataReceivedEventArgs e2) => {
    Console.WriteLine("Error: " + e2.Data);
  };
}
potrace.Start();
potrace.BeginOutputReadLine();
if (Program.IsDebug) {
  potrace.BeginErrorReadLine();
}

BinaryWriter writer = new BinaryWriter(potrace.StandardInput.BaseStream);
bitmap.Save(writer.BaseStream, ImageFormat.Bmp);
potrace.StandardInput.WriteLine(); //Without this line the input to Potrace won't go through.
potrace.WaitForExit();