Is there a way to programmatically determine if a font file has a specific Unicode Glyph?

Here’s a pass at it using c# and the windows API.

[DllImport("gdi32.dll")]
public static extern uint GetFontUnicodeRanges(IntPtr hdc, IntPtr lpgs);

[DllImport("gdi32.dll")]
public extern static IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

public struct FontRange
{
    public UInt16 Low;
    public UInt16 High;
}

public List<FontRange> GetUnicodeRangesForFont(Font font)
{
    Graphics g = Graphics.FromHwnd(IntPtr.Zero);
    IntPtr hdc = g.GetHdc();
    IntPtr hFont = font.ToHfont();
    IntPtr old = SelectObject(hdc, hFont);
    uint size = GetFontUnicodeRanges(hdc, IntPtr.Zero);
    IntPtr glyphSet = Marshal.AllocHGlobal((int)size);
    GetFontUnicodeRanges(hdc, glyphSet);
    List<FontRange> fontRanges = new List<FontRange>();
    int count = Marshal.ReadInt32(glyphSet, 12);
    for (int i = 0; i < count; i++)
    {
        FontRange range = new FontRange();
        range.Low = (UInt16)Marshal.ReadInt16(glyphSet, 16 + i * 4);
        range.High = (UInt16)(range.Low + Marshal.ReadInt16(glyphSet, 18 + i * 4) - 1);
        fontRanges.Add(range);
    }
    SelectObject(hdc, old);
    Marshal.FreeHGlobal(glyphSet);
    g.ReleaseHdc(hdc);
    g.Dispose();
    return fontRanges;
}

public bool CheckIfCharInFont(char character, Font font)
{
    UInt16 intval = Convert.ToUInt16(character);
    List<FontRange> ranges = GetUnicodeRangesForFont(font);
    bool isCharacterPresent = false;
    foreach (FontRange range in ranges)
    {
        if (intval >= range.Low && intval <= range.High)
        {
            isCharacterPresent = true;
            break;
        }
    }
    return isCharacterPresent;
}

Then, given a char toCheck that you want to check and a Font theFont to test it against…

if (!CheckIfCharInFont(toCheck, theFont) {
    // not present
}

Same code using VB.Net

<DllImport("gdi32.dll")> _
Public Shared Function GetFontUnicodeRanges(ByVal hds As IntPtr, ByVal lpgs As IntPtr) As UInteger
End Function  

<DllImport("gdi32.dll")> _
Public Shared Function SelectObject(ByVal hDc As IntPtr, ByVal hObject As IntPtr) As IntPtr
End Function  

Public Structure FontRange
    Public Low As UInt16
    Public High As UInt16
End Structure  

Public Function GetUnicodeRangesForFont(ByVal font As Font) As List(Of FontRange)
    Dim g As Graphics
    Dim hdc, hFont, old, glyphSet As IntPtr
    Dim size As UInteger
    Dim fontRanges As List(Of FontRange)
    Dim count As Integer

    g = Graphics.FromHwnd(IntPtr.Zero)
    hdc = g.GetHdc()
    hFont = font.ToHfont()
    old = SelectObject(hdc, hFont)
    size = GetFontUnicodeRanges(hdc, IntPtr.Zero)
    glyphSet = Marshal.AllocHGlobal(CInt(size))
    GetFontUnicodeRanges(hdc, glyphSet)
    fontRanges = New List(Of FontRange)
    count = Marshal.ReadInt32(glyphSet, 12)

    For i = 0 To count - 1
        Dim range As FontRange = New FontRange
        range.Low = Marshal.ReadInt16(glyphSet, 16 + (i * 4))
        range.High = range.Low + Marshal.ReadInt16(glyphSet, 18 + (i * 4)) - 1
        fontRanges.Add(range)
    Next

    SelectObject(hdc, old)
    Marshal.FreeHGlobal(glyphSet)
    g.ReleaseHdc(hdc)
    g.Dispose()

    Return fontRanges
End Function  

Public Function CheckIfCharInFont(ByVal character As Char, ByVal font As Font) As Boolean
    Dim intval As UInt16 = Convert.ToUInt16(character)
    Dim ranges As List(Of FontRange) = GetUnicodeRangesForFont(font)
    Dim isCharacterPresent As Boolean = False

    For Each range In ranges
        If intval >= range.Low And intval <= range.High Then
            isCharacterPresent = True
            Exit For
        End If
    Next range
    Return isCharacterPresent
End Function  

Leave a Comment