Jalaj P. Jha Technical & Miscellaneous Ramblings

5Jun/070




Which Hindi / Devnagari Unicode font is installed on the system?

When you view a Hindi page the characters show up in one of the fonts installed on the system. If you have "Mangal" font installed that came bundled with your Operating system you see the page in that font. The same applies when you don't have any Hindi Unicode font other than "Arial Unicode MS" font or "Raghu" font that came with your MS Office or you downloaded freely from the net respectively. How come the Browsers know which Language font is installed on your system?

Answer is simple the browser enumerates all installed fonts to find that out. One previous post Font Enumeration Application had all the functions and structures that make this possible. The code in this post derives from the code in previous post. While the previous post aimed at enumerating all fonts, store their names and the fsUsb member of the FONTSIGNATURE structure, and show the subranges supported by each font when the font was selected in the list.

Here we aim at finding the name for the first font that supports a font and stop the enumeration as soon as we got it.

Copy the code below which declare all function / constants / structures required, to a module.

Option Explicit

Public Declare Function EnumFontFamiliesEx Lib "gdi32" Alias "EnumFontFamiliesExA" ( _
    ByVal hDC As Long, lpLogFont As LOGFONT, ByVal lpEnumFontProc As Long, _
    ByVal lParam As Long, ByVal dw As Long) As Long

Public Const MAXSUBRANGELISTEDHERE As Integer = 90
Public Const LF_FACESIZE = 32
Public Const LF_FULLFACESIZE = 64

Public UnicodeSubRange(0 To MAXSUBRANGELISTEDHERE) As String
Public Signature() As Variant
Public FontCtr As Long

Public Type NEWTEXTMETRIC
        tmHeight As Long
        tmAscent As Long
        tmDescent As Long
        tmInternalLeading As Long
        tmExternalLeading As Long
        tmAveCharWidth As Long
        tmMaxCharWidth As Long
        tmWeight As Long
        tmOverhang As Long
        tmDigitizedAspectX As Long
        tmDigitizedAspectY As Long
        tmFirstChar As Byte
        tmLastChar As Byte
        tmDefaultChar As Byte
        tmBreakChar As Byte
        tmItalic As Byte
        tmUnderlined As Byte
        tmStruckOut As Byte
        tmPitchAndFamily As Byte
        tmCharSet As Byte
        ntmFlags As Long
        ntmSizeEM As Long
        ntmCellHeight As Long
        ntmAveWidth As Long
End Type

Public Type FONTSIGNATURE
        fsUsb(4) As Long
        fsCsb(2) As Long
End Type

Public Type LOGFONT
        lfHeight As Long
        lfWidth As Long
        lfEscapement As Long
        lfOrientation As Long
        lfWeight As Long
        lfItalic As Byte
        lfUnderline As Byte
        lfStrikeOut As Byte
        lfCharSet As Byte
        lfOutPrecision As Byte
        lfClipPrecision As Byte
        lfQuality As Byte
        lfPitchAndFamily As Byte
        lfFaceName(1 To LF_FACESIZE) As Byte
End Type

Public Type ENUMLOGFONTEX
        elfLogFont As LOGFONT
        elfFullName(LF_FULLFACESIZE) As Byte
        elfStyle(LF_FACESIZE) As Byte
        elfScript(LF_FACESIZE) As Byte
End Type

Public Type NEWTEXTMETRICEX
        ntmTm As NEWTEXTMETRIC
        ntmFontSig As FONTSIGNATURE
End Type

Now let's work on the form. While we are making this application specific to Hindi, it can be used for any language / Unicode subrange. Add a textbox named txtHindiFont and set its Visible Property to false. Intent for this is that it should be available for modification only to the code and that it raises an event as soon as a matching font is found.

The EnumFontFamiliesEx function takes a parameter lParam which it passes when the callback function is called. So while in the previous application we sent this value as zero, we will use it here to send the bit number associated with the language/script. You can get a complete list on this page http://msdn2.microsoft.com/en-us/library/ms776439.aspx.

Create a const initialized to "15" which is Bit associated with Devnagari script. Send this as lParam parameter. The change event of txtHindiFont pops up a message box with the font name. The name is also available to the code as text stored in txtHindiFont.

Const IDX_HINDI = 15

Private Sub Form_Load()

    Dim objlogfont As LOGFONT
    EnumFontFamiliesEx Form1.hDC, objlogfont, AddressOf EnumFontFamExProc, IDX_HINDI, 0

End Sub

Private Sub txtHindiFont_Change()

    MsgBox "Font supporting Hindi/Devnagari installed on system is " & txtHindiFont

End Sub

That was all for the code in the form. Now lets see how the callback function EnumFontFamExProc looks like. Passing a non-zero as return value for the callback function continues the enumeration until the last font. lParam carries the Bit associated with the Unicode subrange (here devnagari) which we would be matching for, in the fonts enumerated. We would be passing a zero as soon as we find the first matching font. While a "Long" data stores 32 bits, an array of 4 longs is used to store information for all Unicode subranges. The calculation below is to find out where in the array and at which bit to look for.

Public Function EnumFontFamExProc(ByRef lpELFX As ENUMLOGFONTEX, _
    ByRef lpNTME As NEWTEXTMETRICEX, ByVal lFontType As Long, _
    ByVal lParam As Long) As Long

    Dim p As Integer
    Dim q As Integer

    p = Int(lParam / 32)
    q = lParam Mod 32

    If q <> 31 Then
        If lpNTME.ntmFontSig.fsUsb(p) And 2 ^ q Then
            UpdateFontInfo (lpELFX.elfLogFont.lfFaceName)
            EnumFontFamExProc = 0
            Exit Function
        End If
    Else
        If lpNTME.ntmFontSig.fsUsb(p) < 0 Then
            UpdateFontInfo (lpELFX.elfLogFont.lfFaceName)
            EnumFontFamExProc = 0
            Exit Function
        End If
    End If

    EnumFontFamExProc = 1

End Function

As soon as the matching font is found UpdateFontInfo function is called which adds all bytes in the array to find the facename and updates the textbox to reflect the same.

Public Function UpdateFontInfo(ByRef lfFaceName)

    Dim ctr As Integer
    Dim sFaceName As String

    sFaceName = ""

    For ctr = 1 To LF_FACESIZE
        If lfFaceName(ctr) = 0 Then
            Exit For
        Else
            sFaceName = sFaceName & Chr(lfFaceName(ctr))
        End If
    Next

    Form1.txtHindiFont = sFaceName

End Function

Execute the application and a message box will tell you which Hindi Unicode font your machine is equipped with.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment


No trackbacks yet.