Click here to Skip to main content
15,886,519 members
Articles / Desktop Programming / Windows Forms
Tip/Trick

Rendering Text with OpenType Fonts Using GDI

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
6 Mar 2013CPOL2 min read 177.4K   1.8K   6   10
Rendering text with OpenType fonts by P/Invoking GDI API in WinForm applications.

Introduction

As you will probably encounter this problem in WinForm development--you can't create System.Drawing.Font instances from OpenType fonts. That's because the implementation of that class has used GDI+, which provides very limited support for OpenType fonts.

There are several way-outs. One is to switch to WPF from WinForm, which supports OpenType fonts, but I guess you won't do so easily, especially when the other parts of your code are fine and nice and the only missing feature is just the OpenType font files support. The second way is to use FreeType, a fabulous font rendering engine. It is viable but you have some good stuff to learn.

Hereby, this tip provides a method of rendering text with OpenType fonts by P/Invoking the system API.

Image 1

Background

The GDI+ API used by the .NET platform does not support OpenType fonts well, but the system wide API, GDI (without the plus sign, but does more here), supports OpenType much better. We can use Platform Invoke to call functions exposed by the GDI and render texts with OpenType fonts.

The procedure is listed below. The major functions to use are the CreateFont function and the DrawText function.

  1. Get the device context (DC) from the graphics.
  2. Call the CreateFont function and create the GDI font.
  3. Use the font in the DC by calling SelectObject.
  4. Call the DrawText function and draw the text onto the specific region of the DC.
  5. Release the resources by calling DeleteObject.

The C# code is listed below, there are some other functions involved, but they are just listed for your reference.

C#
public static void RenderText(IDeviceContext hdc, string text, 
                string fontFamily, Color color, Rectangle region, int size) {
    // create the handle of DC
    var h = new HandleRef (null, hdc.GetHdc ());
    // create the font
    var p = new HandleRef (null, NativeMethods.CreateFont 
        (size, 0, 0, 0, 0, 0, 0, 0, 1/*Ansi_encoding*/, 0, 0, 4, 0, fontFamily));
    try {
        // use the font in the DC
        NativeMethods.SelectObject (h, p.Handle);
        // set the background to transparent
        NativeMethods.SetBkMode (h, 1);
        // set the color of the text
        NativeMethods.SetTextColor (h, color);
        // draw the text to the region
        NativeMethods.DrawText (h, text, region, 0x0100);
    }
    finally {
        // release the resources
        NativeMethods.DeleteObject (p);
        hdc.ReleaseHdc ();
    }
}

We should firstly prepare our P/Invoke code for use. The protocol of the CreateFont function and DrawText function can be found out from MSDN.

C++
HFONT CreateFont(
  __in  int nHeight,
  __in  int nWidth,
  __in  int nEscapement,
  __in  int nOrientation,
  __in  int fnWeight,
  __in  DWORD fdwItalic,
  __in  DWORD fdwUnderline,
  __in  DWORD fdwStrikeOut,
  __in  DWORD fdwCharSet,
  __in  DWORD fdwOutputPrecision,
  __in  DWORD fdwClipPrecision,
  __in  DWORD fdwQuality,
  __in  DWORD fdwPitchAndFamily,
  __in  LPCTSTR lpszFace
);

int DrawText(
  __in     HDC hDC,
  __inout  LPCTSTR lpchText,
  __in     int nCount,
  __inout  LPRECT lpRect,
  __in     UINT uFormat
);

And the corresponding P/Invoke code can be the following:

C#
const string GDI32 = "gdi32.dll";
struct Rect
{
    public long Left, Top, Right, Bottom;
    public Rect (Rectangle rect) {
        this.Left = rect.Left;
        this.Top = rect.Top;
        this.Right = rect.Right;
        this.Bottom = rect.Bottom;
    }
}
[DllImport (GDI32)]
internal static extern IntPtr CreateFont (
    int nHeight,
    int nWidth,
    int nEscapement,
    int nOrientation,
    int fnWeight,
    uint fdwItalic,
    uint fdwUnderline,
    uint fdwStrikeOut,
    uint fdwCharSet,
    uint fdwOutputPrecision,
    uint fdwClipPrecision,
    uint fdwQuality,
    uint fdwPitchAndFamily,
    string lpszFace
    );
[DllImport (USER32, CharSet = CharSet.Auto)]
static extern int DrawText 
    (HandleRef hDC, string lpchText, int nCount, ref Rect lpRect, uint uFormat);
internal static int DrawText (HandleRef hDC, string text, Rectangle rect, uint format) {
    var r = new Rect (rect);
    return DrawText (hDC, text, text.Length, ref r, format);
}

Using the Code

With the above stuff ready, the rest is quite easy. You just need to gather the parameters and call the RenderText method, like the following code shows:

C++
private void panel1_Paint (object sender, PaintEventArgs e) {
    int size;
    if (Int32.TryParse (comboBox1.Text, out size) == false) {
        size = (int)SystemFonts.DefaultFont.Size;
    }
    RenderText (e.Graphics, textBox1.Text, textBox2.Text, 
                Color.Red, panel1.ClientRectangle, size);
}

You can download the demo application and see how it works. The screen shot at the top of this tip shows an OpenType font LiheiPro is used to render some Chinese and Japanese fonts, which is impossible with the managed .NET API that calls GDI+.

Points of Interest

The GDI methods can return error codes. However, our demo application just assumes everything will go well. You shall refer to the documentation on MSDN and see how to deal with them.

There are quite a few other useful functions exposed by GDI but not covered in this short article. You can find more on MSDN as well.

History

  • Initial post: 2013-March-6

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
China China
I am now programming applications for the Internet of Things.

Comments and Discussions

 
QuestionIssue with GDI in C1FlashCanvas Pin
kailash_tandel8730-Dec-15 2:20
kailash_tandel8730-Dec-15 2:20 
QuestionMessage Closed Pin
12-Oct-14 20:46
paul000912-Oct-14 20:46 
Questionhow do I set text alignment? Pin
vizimuchi8-Jan-14 22:06
vizimuchi8-Jan-14 22:06 
AnswerRe: how do I set text alignment? Pin
wmjordan12-Oct-14 23:18
professionalwmjordan12-Oct-14 23:18 
Generalusing the graphics of a bitmap writes the wrong text Pin
Member 1008257716-Oct-13 4:51
Member 1008257716-Oct-13 4:51 
GeneralRe: using the graphics of a bitmap writes the wrong text Pin
wmjordan16-Oct-13 15:01
professionalwmjordan16-Oct-13 15:01 
GeneralRe: using the graphics of a bitmap writes the wrong text Pin
Member 1008257716-Oct-13 21:00
Member 1008257716-Oct-13 21:00 
GeneralRe: using the graphics of a bitmap writes the wrong text Pin
wmjordan17-Oct-13 15:43
professionalwmjordan17-Oct-13 15:43 
Questionhow could i use this with asp.net web application? Pin
amar_b30-May-13 0:08
amar_b30-May-13 0:08 
General支持 Pin
nandixxp17-Mar-13 2:41
nandixxp17-Mar-13 2:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.