Jump to content

Extract the "Font Family Name" from a TrueType 1.0 font


KaFu
 Share

Recommended Posts

Hiho Forum :P,

after reading this excellent article on how to embed TrueType Fonts in your script by eukalyptus in the German Board I wondered how to acquire the font's name for the subsequent operations in the example file (setting the font). It seems there is no solution available for that up to now (except the manual way by opening the font file with a respective viewer and note down the name :x ).

#include <WinApi.au3>

$sFile = FileOpenDialog("Select True Type Font", @ScriptDir, "All (*.*)", 1)
If @error Then Exit

$sFontname = _TrueType_Font_Extract_FontFamilyName($sFile)
If @error Then
    ConsoleWrite(@CRLF & "! Function returned error: " & @error & @CRLF & @CRLF)
Else
    ConsoleWrite(@CRLF & "+ Fontname extracted: " & $sFontname & @CRLF & @CRLF)
EndIf


Func _TrueType_Font_Extract_FontFamilyName($sFile, $bDebug = False)
    ; ===============================================================================================================================
    ;
    ;   Function to extract the "Font Family Name" from a TrueType 1.0 font
    ;
    ;   References:
    ;       Based on this article:
    ;       http://www.codeproject.com/KB/GDI/fontnamefromfile.aspx
    ;
    ;       MS OpenType Font Specificatons:
    ;       http://www.microsoft.com/typography/otspec/default.htm
    ;       http://www.microsoft.com/typography/otspec/name.htm
    ;
    ;   Author: KaFu
    ;
    ; ===============================================================================================================================

    If Not FileExists($sFile) Then Return SetError(1, 0, 0)

    Local $hFile, $sFontname
    $hFile = FileOpen($sFile, 16)

    #cs
        typedef struct _tagTT_OFFSET_TABLE{
        USHORT uMajorVersion;
        USHORT uMinorVersion;
        USHORT uNumOfTables;
        USHORT uSearchRange;
        USHORT uEntrySelector;
        USHORT uRangeShift;
        }TT_OFFSET_TABLE;
    #ce
    If $bDebug Then ConsoleWrite("+ $str_TT_OFFSET_TABLE" & @CRLF & "=================" & @CRLF)
    Local $tag_TT_OFFSET_TABLE = "USHORT uMajorVersion;USHORT uMinorVersion;USHORT uNumOfTables;USHORT uSearchRange;USHORT uEntrySelector;USHORT uRangeShift;"
    Local $str_TT_OFFSET_TABLE = DllStructCreate($tag_TT_OFFSET_TABLE)

    DllStructSetData($str_TT_OFFSET_TABLE, "uMajorVersion", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_OFFSET_TABLE, "uMinorVersion", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_OFFSET_TABLE, "uNumOfTables", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_OFFSET_TABLE, "uSearchRange", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_OFFSET_TABLE, "uEntrySelector", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_OFFSET_TABLE, "uRangeShift", _SwapWord(FileRead($hFile, 2)))

    If $bDebug Then ConsoleWrite("uMajorVersion: " & DllStructGetData($str_TT_OFFSET_TABLE, "uMajorVersion") & @CRLF)
    If $bDebug Then ConsoleWrite("uMinorVersion :" & DllStructGetData($str_TT_OFFSET_TABLE, "uMinorVersion") & @CRLF)
    If $bDebug Then ConsoleWrite("uNumOfTables :" & DllStructGetData($str_TT_OFFSET_TABLE, "uNumOfTables") & @CRLF)
    If $bDebug Then ConsoleWrite("uSearchRange :" & DllStructGetData($str_TT_OFFSET_TABLE, "uSearchRange") & @CRLF)
    If $bDebug Then ConsoleWrite("uEntrySelector :" & DllStructGetData($str_TT_OFFSET_TABLE, "uEntrySelector") & @CRLF)
    If $bDebug Then ConsoleWrite("uRangeShift :" & DllStructGetData($str_TT_OFFSET_TABLE, "uRangeShift") & @CRLF & @CRLF)

    ; //check is this is a true type font and the version is 1.0
    If not ((DllStructGetData($str_TT_OFFSET_TABLE, "uMajorVersion") = 1) AND (DllStructGetData($str_TT_OFFSET_TABLE, "uMinorVersion") = 0)) Then
        FileClose($hFile)
        Return SetError(2, 0, 0)
    EndIf

    #cs
        typedef struct _tagTT_TABLE_DIRECTORY{
        char szTag[4]; //table name
        ULONG uCheckSum; //Check sum
        ULONG uOffset; //Offset from beginning of file
        ULONG uLength; //length of the table in bytes
        }TT_TABLE_DIRECTORY;
    #ce
    If $bDebug Then ConsoleWrite(@CRLF & "+ $str_TT_TABLE_DIRECTORY_Name" & @CRLF & "=================" & @CRLF)
    Local $tag_TT_TABLE_DIRECTORY = "char szTag[4];ULONG uChecksum;ULONG uOffset;ULONG uLength"
    Local $str_TT_TABLE_DIRECTORY_Name = DllStructCreate($tag_TT_TABLE_DIRECTORY)

    Local $a_TT_TABLE_DIRECTORY[DllStructGetData($str_TT_OFFSET_TABLE, "uNumOfTables")]

    For $i = 0 To DllStructGetData($str_TT_OFFSET_TABLE, "uNumOfTables") - 1
        $a_TT_TABLE_DIRECTORY[$i] = DllStructCreate($tag_TT_TABLE_DIRECTORY)
        DllStructSetData($a_TT_TABLE_DIRECTORY[$i], 1, BinaryToString(FileRead($hFile, 4)))
        If $bDebug Then ConsoleWrite("szTag: " & DllStructGetData($a_TT_TABLE_DIRECTORY[$i], 1) & @CRLF)
        If DllStructGetData($a_TT_TABLE_DIRECTORY[$i], 1) = "name" Then
            DllStructSetData($str_TT_TABLE_DIRECTORY_Name, "szTag", DllStructGetData($a_TT_TABLE_DIRECTORY[$i], 1))
            DllStructSetData($str_TT_TABLE_DIRECTORY_Name, "uChecksum", _WinAPI_SwapLong(FileRead($hFile, 4)))
            DllStructSetData($str_TT_TABLE_DIRECTORY_Name, "uOffset", _WinAPI_SwapLong(FileRead($hFile, 4)))
            DllStructSetData($str_TT_TABLE_DIRECTORY_Name, "uLength", _WinAPI_SwapLong(FileRead($hFile, 4)))
            $a_TT_TABLE_DIRECTORY[$i] = 0
            ExitLoop
        EndIf
        $a_TT_TABLE_DIRECTORY[$i] = 0
        FileRead($hFile, 12)
        #cs
            DllStructSetData($a_TT_TABLE_DIRECTORY[$i], 2, FileRead($hFile, 4))
            DllStructSetData($a_TT_TABLE_DIRECTORY[$i], 3, FileRead($hFile, 4))
            DllStructSetData($a_TT_TABLE_DIRECTORY[$i], 4, FileRead($hFile, 4))
        #ce
    Next
    $a_TT_TABLE_DIRECTORY = 0

    If $bDebug Then ConsoleWrite("szTag: " & DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "szTag") & @CRLF)
    If $bDebug Then ConsoleWrite("uChecksum: " & DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uChecksum") & @CRLF)
    If $bDebug Then ConsoleWrite("uOffset: " & DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uOffset") & @CRLF)
    If $bDebug Then ConsoleWrite("uLength: " & DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uLength") & @CRLF)

    #cs
        typedef struct _tagTT_NAME_TABLE_HEADER{
        USHORT uFSelector; //format selector. Always 0
        USHORT uNRCount; //Name Records count
        USHORT uStorageOffset; //Offset for strings storage,
        //from start of the table
        }TT_NAME_TABLE_HEADER;
    #ce
    If $bDebug Then ConsoleWrite(@CRLF & "+ $str_TT_NAME_TABLE_HEADER" & @CRLF & "=================" & @CRLF)
    Local $tag_TT_NAME_TABLE_HEADER = "USHORT uFSelector; USHORT uNRCount;USHORT uStorageOffset"
    Local $str_TT_NAME_TABLE_HEADER = DllStructCreate($tag_TT_NAME_TABLE_HEADER)

    FileSetPos($hFile, DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uOffset"), 0)

    DllStructSetData($str_TT_NAME_TABLE_HEADER, "uFSelector", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_NAME_TABLE_HEADER, "uNRCount", _SwapWord(FileRead($hFile, 2)))
    DllStructSetData($str_TT_NAME_TABLE_HEADER, "uStorageOffset", _SwapWord(FileRead($hFile, 2)))

    If $bDebug Then ConsoleWrite("uFSelector: " & DllStructGetData($str_TT_NAME_TABLE_HEADER, "uFSelector") & @CRLF)
    If $bDebug Then ConsoleWrite("uNRCount: " & DllStructGetData($str_TT_NAME_TABLE_HEADER, "uNRCount") & @CRLF)
    If $bDebug Then ConsoleWrite("uStorageOffset: " & DllStructGetData($str_TT_NAME_TABLE_HEADER, "uStorageOffset") & @CRLF)

    #cs
        typedef struct _tagTT_NAME_RECORD{
        USHORT uPlatformID;
        USHORT uEncodingID;
        USHORT uLanguageID;
        USHORT uNameID;
        USHORT uStringLength;
        USHORT uStringOffset; //from start of storage area
        }TT_NAME_RECORD;
    #ce
    Local $tag_TT_NAME_RECORD = "USHORT uPlatformID;USHORT uEncodingID;USHORT uLanguageID;USHORT uNameID;USHORT uStringLength;USHORT uStringOffset"

    For $i = 0 To DllStructGetData($str_TT_NAME_TABLE_HEADER, "uNRCount") - 1
        If $bDebug Then ConsoleWrite(@CRLF & "+ $str_TT_NAME_RECORD #" & $i + 1 & @CRLF & "=================" & @CRLF)
        $str_TT_NAME_RECORD = DllStructCreate($tag_TT_NAME_RECORD)
        DllStructSetData($str_TT_NAME_RECORD, "uPlatformID", _SwapWord(FileRead($hFile, 2)))
        DllStructSetData($str_TT_NAME_RECORD, "uEncodingID", _SwapWord(FileRead($hFile, 2)))
        DllStructSetData($str_TT_NAME_RECORD, "uLanguageID", _SwapWord(FileRead($hFile, 2)))
        DllStructSetData($str_TT_NAME_RECORD, "uNameID", _SwapWord(FileRead($hFile, 2)))
        DllStructSetData($str_TT_NAME_RECORD, "uStringLength", _SwapWord(FileRead($hFile, 2)))
        DllStructSetData($str_TT_NAME_RECORD, "uStringOffset", _SwapWord(FileRead($hFile, 2)))

        If $bDebug Then ConsoleWrite("uPlatformID: " & DllStructGetData($str_TT_NAME_RECORD, "uPlatformID") & @CRLF)
        If $bDebug Then ConsoleWrite("uEncodingID: " & DllStructGetData($str_TT_NAME_RECORD, "uEncodingID") & @CRLF)
        If $bDebug Then ConsoleWrite("uLanguageID: " & DllStructGetData($str_TT_NAME_RECORD, "uLanguageID") & @CRLF)
        If $bDebug Then ConsoleWrite("uNameID: " & DllStructGetData($str_TT_NAME_RECORD, "uNameID") & @CRLF)
        If $bDebug Then ConsoleWrite("uStringLength: " & DllStructGetData($str_TT_NAME_RECORD, "uStringLength") & @CRLF)
        If $bDebug Then ConsoleWrite("uStringOffset: " & DllStructGetData($str_TT_NAME_RECORD, "uStringOffset") & @CRLF & @CRLF)

        If DllStructGetData($str_TT_NAME_RECORD, "uNameID") = 1 Then ; Should contain the "Font Family name" => look under "Name IDs" at http://www.microsoft.com/typography/otspec/name.htm
            $iFileReadPosition_Save = FileGetPos($hFile)
            If $bDebug Then ConsoleWrite("- Offset #1: " & DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uOffset") & @CRLF)
            If $bDebug Then ConsoleWrite("- Offset #1: " & DllStructGetData($str_TT_NAME_TABLE_HEADER, "uStorageOffset") & @CRLF)
            If $bDebug Then ConsoleWrite("- Offset #1: " & DllStructGetData($str_TT_NAME_RECORD, "uStringOffset") & @CRLF)
            If $bDebug Then ConsoleWrite("! Offset Total: " & DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uOffset") + DllStructGetData($str_TT_NAME_TABLE_HEADER, "uStorageOffset") + DllStructGetData($str_TT_NAME_RECORD, "uStringOffset") & @CRLF)
            FileSetPos($hFile, DllStructGetData($str_TT_TABLE_DIRECTORY_Name, "uOffset") + DllStructGetData($str_TT_NAME_TABLE_HEADER, "uStorageOffset") + DllStructGetData($str_TT_NAME_RECORD, "uStringOffset"), 0)
            $sFontname = BinaryToString(FileRead($hFile, DllStructGetData($str_TT_NAME_RECORD, "uStringLength")), 4)
            If StringIsASCII($sFontname) Then
                If $bDebug Then ConsoleWrite(@CRLF & "+ sFontName found: " & $sFontname & @CRLF)
                If $bDebug Then ConsoleWrite(@CRLF)
                $str_TT_NAME_RECORD = 0
                ExitLoop ; according to the article the loop can be exited here... if the Fontname is found...
            EndIf
            $sFontname = ""
            FileSetPos($hFile, $iFileReadPosition_Save, 0)
        EndIf
        $str_TT_NAME_RECORD = 0
    Next
    FileClose($hFile)

    If $sFontname Then Return $sFontname
    Return SetError(3, 0, 0)

EndFunc   ;==>_TrueType_Font_Extract_FontFamilyName

; #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
Func _SwapWord($iValue)
    Return _MakeWord(BitShift($iValue, 8), BitAND($iValue, 0xFF))
EndFunc   ;==>_SwapWord

Func _MakeWord($LoByte, $HiByte)
    Return BitOR($LoByte, 0x100 * $HiByte)
EndFunc   ;==>_MakeWord

Func _WinAPI_SwapLong($iValue)
    ; #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
    Return _WinAPI_MakeLong(_SwapWord(_WinAPI_HiWord($iValue)), _SwapWord(_WinAPI_LoWord($iValue)))
EndFunc   ;==>_WinAPI_SwapLong

Edit: Corrected spelling typos

Regards

Edited by KaFu
Link to comment
Share on other sites

Why not use the API?

#Include <FontConstants.au3>
#Include <WinAPIEx.au3>

$sFile = FileOpenDialog('Browse Font File', @ScriptDir, 'Font Files (*.ttf;*.ttc;*.otf;*.otc;*.pfm;*.pfb)|All Files (*.*)', 1 + 2)
If Not $sFile Then
    Exit
EndIf

Switch StringRegExpReplace($sFile, '^.*\.', '')
    Case 'pfm', 'pfb'
        $sFile = StringRegExpReplace($sFile, '\.[^.]*\Z', '.pfm') & '|' & StringRegExpReplace($sFile, '\.[^.]*\Z', '.pfb')
    Case Else

EndSwitch

$sFont = _WinAPI_GetFontResourceInfo($sFile, 1)
If Not $sFont Then
    Exit
EndIf

_WinAPI_AddFontResourceEx($sFile, $FR_PRIVATE)
$hFont = _WinAPI_CreateFont(12, 0, 0, 0, $FW_NORMAL , 0, 0, 0, $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $ANTIALIASED_QUALITY, $DEFAULT_PITCH, $sFont)
$hDC = _WinAPI_GetDC(0)
$hSv = _WinAPI_SelectObject($hDC, $hFont)
$tOLTM = _WinAPI_GetOutlineTextMetrics($hDC)
_WinAPI_SelectObject($hDC, $hSv)
_WinAPI_ReleaseDC(0, $hDC)
_WinAPI_DeleteObject($hFont)
_WinAPI_RemoveFontResourceEx($sFile, $FR_PRIVATE)

ConsoleWrite('Family name:   ' & _otm($tOLTM, 'otmFamilyName') & @CR)
ConsoleWrite('Style name     ' & _otm($tOLTM, 'otmStyleName') & @CR)
ConsoleWrite('Typeface name: ' & _otm($tOLTM, 'otmFaceName') & @CR)
ConsoleWrite('Full name:     ' & _otm($tOLTM, 'otmFullName') & @CR)

ConsoleWrite('Nature:        ' & DllStructGetData($tOLTM, 'otmSelection') & @CR)
ConsoleWrite('License:       ' & DllStructGetData($tOLTM, 'otmType') & @CR)

Func _otm(ByRef $tOLTM, $sName)
    Return DllStructGetData(DllStructCreate('wchar[' & (_WinAPI_StrLen(DllStructGetPtr($tOLTM) + DllStructGetData($tOLTM, $sName)) + 1) & ']', DllStructGetPtr($tOLTM) + DllStructGetData($tOLTM, $sName)), 1)
EndFunc   ;==>_otm

Or

#Include <Array.au3>
#Include <File.au3>
#Include <WinAPIEx.au3>

Global $FileList = _FileListToArray(_WinAPI_ShellGetSpecialFolderPath($CSIDL_FONTS), '*.ttf', 1)
Global $FontList[UBound($FileList) - 1][2]

For $i = 1 To $FileList[0]
    $FontList[$i - 1][0] = $FileList[$i]
    $FontList[$i - 1][1] = _WinAPI_GetFontResourceInfo($FileList[$i], 1)
Next

_ArrayDisplay($FontList)

Edited by Yashied
Link to comment
Share on other sites

Fonts were on my menu some time ago.

because it's just me showing off since KaFu you said "up to now".

Seeing your name as the last respondent I would have bet to read a bitchy comment on "the hard way" :P... Okay, seems like I reinvented the wheel... again :x. But at least I've learned a lot doing it on my own :shifty:... and added some more useful tags for others to search for.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...