Loading font face from file using Mono and Cairo

I was trying to build up a little application for previewing and installing FreeType fonts using Gtk#, but I didn’t found any existing API for loading the font face from a file (.ttf) in Mono.Cairo.

In Mono.Cairo there is the class FontFace that represents a FontFace, this class has only a constructor that accept an IntPtr (handle) as unique arguments, this handle should refer to an allocated and initialized cairo’s cairo_font_face_t.

The only chances that I had for obtaining the cairo_font_face_t handler for the file was to relay to the C API of Cairo and FreeType. The result is a new class, FreeTypeFontFace derived from Cairo.FontFace:

using System;
using System.Runtime.InteropServices;
using Cairo;

public class FreeTypeInitException : Exception
{
    public FreeTypeInitException() : base("Can't initialize freetype environment.")
    {
    }
}

public class CreateFaceException : Exception
{
    public CreateFaceException(string filename) : base("Can't create the face for file: " + filename + ".")
    {
    }
}

public class LoadFaceException : Exception
{
    public LoadFaceException(string filename) : base("Can't load the face for file: " + filename + ".")
    {
    }
}

public class FreeTypeFontFace : FontFace
{
    private static bool initialized = false;
    private static IntPtr ft_lib;
    private IntPtr ft_face;

    private FreeTypeFontFace(IntPtr handler, IntPtr ft_face):base(handler)
    {
        this.ft_face = ft_face;
    }

    public void Dispose()
    {
        cairo_font_face_destroy (Handle);
        FT_Done_Face (ft_face);
        ((IDisposable) this).Dispose ();
    }

    public static FreeTypeFontFace Create(string filename, int faceindex, int loadoptions)
    {
        if(!initialized)
            initialize();

        IntPtr ft_face;
        if(FT_New_Face (ft_lib, filename, faceindex, out ft_face) != 0)
            throw new LoadFaceException(filename);

        IntPtr handler = cairo_ft_font_face_create_for_ft_face (ft_face, loadoptions);
        if(cairo_font_face_status(handler) != 0)
            throw new CreateFaceException(filename);

        return new FreeTypeFontFace(handler, ft_face);
    }

    private static void initialize() {
        if(FT_Init_FreeType (out ft_lib) != 0)
            throw new FreeTypeInitException();
        initialized = true;
    }

    [DllImport ("libfreetype.so.6")]
    private static extern int FT_Init_FreeType (out IntPtr ft_lib);

    [DllImport ("libfreetype.so.6")]
    private static extern int FT_New_Face (IntPtr ft_lib, string filename, int faceindex, out IntPtr ft_face);

    [DllImport ("libfreetype.so.6")]
    private static extern int FT_Done_Face (IntPtr ft_face);

    [DllImport ("libcairo.so.2")]
    private static extern IntPtr cairo_ft_font_face_create_for_ft_face (IntPtr ft_face, int loadoptions);

    [DllImport ("libcairo.so.2")]
    private static extern int cairo_font_face_status (IntPtr cr_face);

    [DllImport ("libcairo.so.2")]
    private static extern int cairo_font_face_destroy (IntPtr cr_face);
}

The constructor is private: the FreeTypeFontFace instances should be create using the static method Create because we have to provide the correct cairo_font_face_t handler to the base constructor.

The steps for initialize the cairo_font_face_t structure are:

  1. Initialize the freetype environment if it isn’t already been initialized.
  2. Load the FreeType face from the file using FT_New_Face() (from the FreeType library).
  3. Build the cairo_font_face_t structure from the FreeType face using cairo_ft_font_face_create_for_ft_face().

As you can see, the Create method can throw some exceptions.

When the object isn’t needed anymore the programmer should invoke the Dispose method (as happens with other Cairo related objects).

To obtain a .net library, named FreeTypeFontFace.dll, for using the class save the above file as “FreeTypeFontFace.cs” and build it using:

mcs -pkg:mono-cairo -target:library FreeTypeFontFace.cs

Follow a sample code (written in boo) using the FreeTypeFontFace for displaying a FreeType Font preview:

import System
import FreeTypeFontFace from "FreeTypeFontFace.dll"

import Gtk from "gtk-sharp"
import Gdk from "gdk-sharp"
import Cairo from "Mono.Cairo"

class FontPreview(DrawingArea):
	private fontfile as string
	private static sizes = [6, 8, 10, 11, 12, 14, 16, 18, 20, 22, 24]
	private static lorem = "Loading font face from file using Mono and Cairo"

	def constructor(fontfile as string):
		self.ModifyBg(StateType.Normal, Gdk.Color(0xff,0xff,0xff))
		self.fontfile = fontfile

	def OnExposeEvent(args as Gdk.EventExpose):
		g = Gdk.CairoHelper.Create (args.Window)
		font = FreeTypeFontFace.Create(self.fontfile, 0, 0)
		g.ContextFontFace = font
		p as int = 10
		for i as int in sizes:
			g.SetFontSize(i)
			p = p + i + 2
			g.MoveTo(10, p)
			g.ShowText(lorem)
		te = g.TextExtents(lorem);
		WidthRequest = te.Width + 20;
		HeightRequest = p + te.Height + 10;
		font.Dispose()
		ig as IDisposable = g
		ig.Dispose()

Application.Init()
w = Gtk.Window("FontPreview")
w.Add(FontPreview("/usr/share/fonts/dejavu/DejaVuSansMono-Oblique.ttf"))
w.DeleteEvent += {Application.Quit()}
w.ShowAll()
Application.Run()