SVGs in .Net using Cairo and Gtk+ (and C#)

If I was using C it would have been simply a couple of calls to librsvg, but on C# things got a bit more ugly because Rsvg, the wrapper around librsvg is not finished. And other bindings are also missing. Just getting a Cairo context out of a Gtk.DrawingArea was not as simple as I would have liked it to be (I describe how to do it in a previous post, but I’ll do it again here).

This is my first C# or .Net application (the second if you count the three day prototype I’ve done at my work some days ago) so there are a couple of important remarks:

  • Be careful with taking my advice, I am no authority whatsoever in the field. Actually, if you find something that’s wrong or that you know a better way of doing, please, tell me, so I can improve this post for the next reader.
  • I wouldn’t have been able to do this without the help of the community. A special thanks to David Anes and everyone else for the replies in the Mono mailing list and to all those that patiently answered my questions in Mono’s irc channel.

Enough, let’s code!

To do all the development I am going to use MonoDevelop, you can do it in any way you want but you’ll be on your own regarding assemblies, references and other things MonoDevelop makes easy. Let’s start from the very bottom, a “Console Project” in C# which we’ll call RoadSVG. It’s code is no more than:

using System;

namespace RoadSVG {
    class RoadSVG {
        public static void Main(string[] args) {
            Console.WriteLine("Hello World!");
        }
    }
}

Let’s now turn it into a simple Gtk application that opens a window and quits when the window is closed (at least I got tired of having to kill the application manually from the console the second time I’ve had to do it):

using System; using Gtk;

namespace RoadSVG {
    class RoadSVG: Gtk.Window {
        public static void Main(string[] args) {
            Application.Init(); RoadSVG w = new RoadSVG();
            w.Show();
            Application.Run();
        }

        public RoadSVG(): base("RoadSVG") {
            this.DeleteEvent += Quit;
        }

        void Quit(object sender, DeleteEventArgs a){
            Application.Quit();
            a.RetVal = true;
        }
    }
}

As you can see, we are now “using Gtk”, you’ll also need to add it as reference. In MonoDevelop it is done by right-clicking the “Reference” section in the “Solution” tab, going to “Edit References” and picking it up (gtk-sharp). For the command line read the man page of your compiler.

Since we are not naughty boys, when we draw, we only draw in the drawing area (not in the walls, chairs or any live being). Let’s create a Gtk.DrawingArea and add it to the window (I am not going to paste all the code every time):

private Gtk.DrawingArea da;

public RoadSVG(): base("RoadSVG") {
    this.DeleteEvent += Quit;
    this.da = new Gtk.DrawingArea();
    this.Add(da);
    this.da.Show();
}

To do all the drawing we’ll use Cairo, which has an excellent API and it is so cool. It is the new revolution in 2D drawing in the free world. Everything in Cairo is performed to (or on a) context, which means we have to get a Cairo.Context out of the drawing area. Since Gtk+ 2.8, the back-end is based on Cairo which means that getting a Cairo context out of the drawing area will be easy.

We add Mono.Cairo to our references and our using clauses:

using Cairo;

and then it is quite easy to get a context:

Cairo.Context context = Gdk.CairoHelper.Create(this.da.GdkWindow);

Let’s draw something so you get an idea how it works. When the window is covered or minimized whatever you’ve drawn will be lost, so you’ll have to draw it again. That means that the draw-method will be driven by the expose event. For example:

void Draw(object o, Gtk.ExposeEventArgs e) {
    Cairo.Context context = Gdk.CairoHelper.Create(this.da.GdkWindow);

    context.MoveTo(10,10);
    context.LineTo(100,10);
    context.LineTo(100,100);
    context.LineTo(10,100);
    context.LineTo(10,100);
    context.ClosePath();

    context.Color = new Cairo.Color(0,0,0);
    context.FillPreserve();
    context.Color = new Cairo.Color(1,0,0);
    context.Stroke();
}

but first connect it on the constructor:

this.da.ExposeEvent += Draw;

When you run this application you should see a black rectangle with red borders. Having achieved that we can now move into displaying an SVG. Here it’s where it gets trickier. To render SVGs into a Cairo context there’s a library called librsvg. The bindings are with the Gnome# libraries, in the Rsvg namespace. These bindings are already used by Gnome# itself, for PixBufs or something like that; so one would say they are quite complete, But when I tried to use them I’ve found many missing methods.

It may not be totally clear, but what we need to create is an Rsvg.Handle, which is a representation of a SVG. Now, take a look at the constructors. We would like to build a handle out of a SVG file, but all we can do is built an empty handle or a handle from a pointer. To keep the suspense low, we’ll have to invoke some of the C functions. What I did is create my own namespace and class. There are other ways to do it of course but this is the one I liked:

namespace MyRsvg {
    class Handle: Rsvg.Handle {
    }
}

Of course to compile that we need

using Rsvg;

and a reference to rsvg-sharp. And while your at at it, add

using System.Runtime.InteropServices;

because we are going to need it to invoke the C functions. The first C function we are interested in is rsvg_handle_new_from_file which has a quite explicative name. It’s C prototype is:

RsvgHandle *rsvg_handle_new_from_file(const gchar *file_name, GError **error);

and the declaration from C# is:

[DllImport("rsvg-2")]
internal static extern IntPtr rsvg_handle_new_from_file(IntPtr file_name, out IntPtr error);

Since having to build an IntPtr with the file name and having to decode an IntPtr of an error is not something we are particularly fond of, we create a nice wrapper around it:

protected static IntPtr NewFromFile(string fileName) {
    IntPtr error = IntPtr.Zero;
    IntPtr nativeFileName = GLib.Marshaller.StringToPtrGStrdup(fileName);
    IntPtr handlePtr = rsvg_handle_new_from_file(nativeFileName, out error);
    GLib.Marshaller.Free(nativeFileName);

    if(IntPtr.Zero != error){
        throw new GLib.GException(error);
    }

    return handlePtr;
}

And we already have a static method that will build a rsvg handle for us. Personally I am quite impressed with how easy this was from C#. A couple of notes about this: I haven’t written this code, I just copied from other similar methods in the Rsvn bindings (or others in Gnome# or Gtk#). And this is a novelization and not a tutorial because, for example, I am not sure whether that error catching works. Actually some problems I’ve had with a particular case shows me that it doesn’t work, yet this style is all over Gtk# and Gnome#, so I am just not sure.

Now, around that static method we can implement our first useful constructor:

public Handle(string fileName): base(MyRsvg.Handle.NewFromFile(fileName)) {
}

We have enough now to create an rsvg handle in our draw method by adding:

MyRsvg.Handle s = new MyRsvg.Handle("/some/nice/image.svg");

and now we have to render it on the Cairo context. We are now again facing missing methods. Without loosing too much time let’s go and wrap them:

[DllImport("rsvg-2")]
internal static extern void rsvg_handle_render_cairo(IntPtr handle, IntPtr cairoContext);

[DllImport("rsvg-2")]
internal static extern void rsvg_handle_render_cairo_sub(IntPtr handle, IntPtr cairoContext, string id);

Again, being at this low level is not fun, so we abstract it a bit more with:

public void RenderOn(Cairo.Context context) {
rsvg_handle_render_cairo(this.Raw, context.Handle);
}

public void RenderOn(Cairo.Context context, string id) {
rsvg_handle_render_cairo_sub(this.Raw, context.Handle, id);
}

Now all we have to do to render the SVG is:

s.RenderOn(context);

Voilà!

If you need it to be render in a special place and/or size, use a transformation matrix.

Something I needed the dimensions of a SVG. To get that I’ve tried to wrap a structure and some functions with:

[StructLayout(LayoutKind.Sequential)]
public class RsvgDimensionData {
    public int width;
    public int heigh;
    public double em;
    public double ex;
}

[DllImport("rsvg-2")]
internal static extern void rsvg_handle_get_dimensions(IntPtr handle, [Out, MarshalAs(UnmanagedType.LPStruct)] out RsvgDimensionData dimension_data);

RsvgDimensionData GetDimensions() {
    Console.WriteLine("GetDimensions()");
    RsvgDimensionData dd;
    rsvg_handle_get_dimensions(this.Raw, out dd);
    Console.WriteLine("return dd");
    return dd;
}

and then write these nice methods:

public int Width {
    get {
        RsvgDimensionData dd = this.GetDimensions();
        return dd.width;
    }
}

public int Height {
    get {
        RsvgDimensionData dd = this.GetDimensions();
        return dd.heigh;
    }
}

Very nice, but if you really know C# you would already know that this code crashes, and possible you may also know why. I don’t. Taking the quick and dirty path (something I rarely do) I’ve did this:

public int Width {
get {
return this.Pixbuf.Width;
}
}

public int Height {
get {
return this.Pixbuf.Height;
}
}

To be able to create SVGs “handles” out of more sources, particularly interesting is creating them from Assemblies, I ended up with all these constructors:

public Handle(byte[] data): base(MyRsvg.Handle.NewFromData(data)){
// Do nothing. Everything was done by StreamToPtr and the base class.
}

public Handle(string fileName): base(MyRsvg.Handle.NewFromFile(fileName)) {
// Do nothing. Everything was done by NewFormFile and the base class.
}

public Handle(System.IO.Stream strm): base(MyRsvg.Handle.NewFromData(MyRsvg.Handle.StreamToArray(strm))){
// Do nothing. Everything was done by StreamToArray, NewFromData and the base class.
}

public Handle(Assembly a, String name): base(MyRsvg.Handle.NewFromData(MyRsvg.Handle.StreamToArray(a.GetManifestResourceStream(name)))){
// Do nothing. Everything was done by StreamToArray, NewFromData and the base class.
}

public Handle(System.IntPtr data): base(data) {
// Just reimplement the constructor.
}

public Handle(): base() {
// Just reimplement the constructor.
}

which need this method to work:

static protected byte[] StreamToArray(System.IO.Stream st) {
if(st.Length > int.MaxValue) {
// TODO: raise exception or something.
return null;
} else {
// TODO: not all streams have a length and can be read completely, do something about it, like raising an exception.
System.IO.StreamReader str = new System.IO.StreamReader(st);
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
return encoder.GetBytes(str.ReadToEnd());
}
}

And that’s it. So, what now? Well, I believe all these methods belong in the mainstream Rsvg, but it is up to a developer that understand how those bindings are created to add these methods (I just don’t have time to do that). I hope someone will find this useful.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s