Search code examples
c#vb.netfontsgdi+kerning

How to get font kerning pairs in .net


I'm trying to get font kerning pairs by using this P/Invoke call:

Imports System.Runtime.InteropServices

Public Class Kerning

Structure KERNINGPAIR
    Public wFirst As UInt16
    Public wSecond As UInt16
    Public iKernelAmount As UInt32
End Structure

<DllImport("gdi32.dll")> _
Private Shared Function GetKerningPairs(hdc As IntPtr, 
      nNumPairs As UInteger, <Out> lpkrnpair As KERNINGPAIR()) As UInteger
End Function

Sub ExaminePairs()
    Dim f As Font
    For Each myFontFamily In System.Drawing.FontFamily.Families

        f = New Font(myFontFamily, 25)
        Dim pairs As UInteger = 0
        Dim pairsArray() As KERNINGPAIR
        ReDim pairsArray(pairs)
        Dim a = GetKerningPairs(f.ToHfont(), pairs, Nothing)
        If a <> 0 Then
            MsgBox("Found!")
        End If
        f.Dispose()
    Next

End Sub
End Class

The ExamineParis function should show a messagebox whenever a font with defined kerning pairs is found (according to this: https://msdn.microsoft.com/en-us/library/windows/desktop/dd144895(v=vs.85).aspx ) But it seems to return always 0.

I need to find a way to get all kerning pairs of a given font (how many there are, and then their structure).

Does anyone know how it could be done?


Solution

  • The accepted answer here shows how to call GetKerningPairs from VB.NET. Here's that code modified to fit with yours:

    Imports System.Drawing
    Imports System.Runtime.InteropServices
    
    Public Class Kerning
    
        <StructLayout(LayoutKind.Sequential)>
        Structure KERNINGPAIR
            Public wFirst As Short
            Public wSecond As Short
            Public iKernelAmount As Integer
        End Structure
    
        <DllImport("gdi32.dll", SetLastError:=True, CallingConvention:=CallingConvention.Winapi)>
        Public Shared Function GetKerningPairs(ByVal hdc As IntPtr, ByVal nPairs As Integer, <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> <Out()> ByVal pairs() As KERNINGPAIR) As Integer
        End Function
    
        <DllImport("gdi32.dll")>
        Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr
        End Function
    
        Public Shared Function GetKerningPairs(ByVal font As Font) As IList(Of KERNINGPAIR)
            Dim pairs() As KERNINGPAIR
            Using g As Graphics = Graphics.FromHwnd(IntPtr.Zero)
                g.PageUnit = GraphicsUnit.Pixel
                Dim hdc As IntPtr = g.GetHdc
                Dim hFont As IntPtr = font.ToHfont
                Dim old As IntPtr = SelectObject(hdc, hFont)
                Try
                    Dim numPairs As Integer = GetKerningPairs(hdc, 0, Nothing)
                    If numPairs > 0 Then
                        pairs = New KERNINGPAIR(numPairs - 1) {}
                        numPairs = GetKerningPairs(hdc, numPairs, pairs)
                        Return pairs
                    Else
                        Return Nothing
                    End If
                Finally
                    old = SelectObject(hdc, old) ' replace whatever object was selected in the dc
                End Try
            End Using
        End Function
    
        Sub ExaminePairs()
            For Each myFontFamily In FontFamily.Families
                Try
                    Using f = New Font(myFontFamily, 25)
                        Dim pairs = GetKerningPairs(f)
                        If pairs IsNot Nothing Then
                            Debug.Print("#Pairs: {0}", pairs.Count)
                        Else
                            Debug.Print("No pairs found")
                        End If
                    End Using
                Catch ex As Exception
                    Debug.Print("Error: {0} for: {1}", ex.Message, myFontFamily.Name)
                End Try
            Next
        End Sub
    
    End Class