Search
Module:
Directory

   Desktop Functions:

   Smart Device Functions:


Show Recent Changes
Subscribe (RSS)
Misc. Pages
Comments
FAQ
Helpful Tools
Playground
Suggested Reading
Website TODO List
Download Visual Studio Add-In

GetFontData (gdi32)
 
.
Summary
Allows access to the data within a font metrics table in any TrueType / OpenType font file as specified by http://www.microsoft.com/typography/otspec/otff.htm. Minimum Server version supported: "Windows 2000 Server [desktop apps only]" i.e. NOT web.
Summary

C# Signature:

[DllImport("gdi32.dll")]
static extern uint GetFontData(IntPtr hdc, uint dwTable, uint dwOffset,
   [Out] byte [] lpvBuffer, uint cbData);

User-Defined Types:

None.

Notes:

As a prerequisite a font handle has to be loaded into the device context using the SelectObject method in gdi32.dll.

Release the handle after usage with the method DeleteObject in gdi32.dll.

Parameter descriptions:

  • hdc: The device context, the font file was loaded into.
  • dwTable: The key of the font metric table as uint assembled by the ASCII HEX codes of the single characters of the table in reversed order, i.e. 'eman' represents the font table keyword character sequence 'name' in reversed order.

            uint table = 0x656D616E    // represents char sequence 'eman'


  • dwOffset: Specifies the offset from the beginning of the font metric table towhere the function should begin retrieving information. If this parameter is zero, the information is retrieved starting at the beginning of the table specified by the dwTable parameter. If this value is greater than or equal to the size of the table, an error occurs.
  • lpvBuffer: Points to a byte array to receive the font information. If this parameter is NULL, the function returns the size of the buffer required for the font data.
  • cbData: Specifies the length, in bytes, of the information to be retrieved. If this parameter is zero, the size of the font metrics table specified in the dwTable parameter is returned.

If the function succeeds, the return value is the number of bytes returned. If the function fails, the return value is GDI_ERROR.

Tips & Tricks:

Call the function first of all by passing a byte[] referring to null and by passing cbData value as 0.

The return value provides you with the number of bytes in corresponding font metrics table data.

Initialize then the byte[] with the length the method call returned. The latter value shall also be used for cbData parameter.

You can then call the method a second time with initialized array and defined data length, and the byte array then contains the raw bytes.

Please see in code sample a possibility to extract the full font name out of a .ttf file for a windows platform.

Sample Code:

/// <summary>

    /// Helper to enable loading the full font name as specified in TrueType font specs.
    /// http://www.microsoft.com/typography/otspec/name.htm
    /// </summary>
    internal static class FontNameExtractor
    {
    #region PInvoke gdi32.dll

    /// <summary>
    /// Selects the graphics object (here a font handle) into the device context.
    /// </summary>
    /// <param name="hdc">A handle to the device context.</param>
    /// <param name="hgdiobj">A handle to the object to be selected.
    ///  The specified object must have been created by using one of the following functions.</param>
    /// <returns>If the selected object is not a region and the function succeeds, the return value is a handle to the object being replaced.
    /// If an error occurs and the selected object is not a region, the return value is NULL. Otherwise, it is HGDI_ERROR.</returns>
    [DllImport("gdi32.dll")]
    private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

    /// <summary>
    /// Deletes a logical pen, brush, font, bitmap, region, or palette handle freeing all system resources associated with the object.
    /// After the object is deleted, the specified handle is no longer valid.
    /// </summary>
    /// <param name="hgdiobj">The handle to the logical GDI object.</param>
    /// <returns><c>true</c>, if the handle to the GDI object was successfully released; otherwise <c>false</c> is returned.</returns>
    [DllImport("gdi32.dll")]
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DeleteObject(IntPtr hgdiobj);

    /// <summary>
    /// Gets the font data from specified font metric table to be accessed by key defined as <paramref name="dwTable"/>.
    /// </summary>
    /// <param name="hdc">The device context, the font file was loaded into.
    ///  Use <see cref="SelectObject"/> for loading the font into a device context.</param>
    /// <param name="dwTable">The key of the font metric table as <see cref="uint"/>
    /// assembled by ASCII HEX code of the single characters of the table in reversed order.
    /// <code>uint table = 0x656D616E</code>, represents the font table keyword character sequence 'name' in reversed order (i.e. 'eman').</param>
    /// <param name="dwOffset">Specifies the dwOffset from the beginning of the font metric table to the location where the function should begin retrieving information.
    /// If this parameter is zero, the information is retrieved starting at the beginning of the table specified by the <paramref name="dwTable"/> parameter.
    /// If this value is greater than or equal to the size of the table, an error occurs.</param>
    /// <param name="lpvBuffer">Points to a lpvBuffer to receive the font information. If this parameter is NULL, the function returns the size of the <paramref name="lpvBuffer"/> required for the font data.</param>
    /// <param name="cbData">Specifies the length, in bytes, of the information to be retrieved.
    /// If this parameter is zero, GetFontData returns the size of the font metrics table specified in the <paramref name="dwTable"/> parameter. </param>
    /// <returns>If the function succeeds, the return value is the number of bytes returned. If the function fails, the return value is GDI_ERROR.</returns>
    [DllImport("gdi32.dll")]
    private static extern uint GetFontData(IntPtr hdc, uint dwTable, uint dwOffset, [Out] byte[] lpvBuffer, uint cbData);

    #endregion

    private static uint nameTableKey = 0x656D616E;   // Represents the ASCII character sequence 'eman' => reversed name table key 'name'

    /// <summary>
    /// Gets the full name directly out of specified True Type font file.
    /// </summary>
    /// <param name="fontFile">The True Type font file.</param>
    /// <returns>The full font name.</returns>
    internal static string GetFullFontName(string fontFile)
    {
        string fontName = string.Empty;

        using (PrivateFontCollection fontCollection = new PrivateFontCollection())
        {
        fontCollection.AddFontFile(fontFile);
        byte[] fontData = LoadFontMetricsNameTable(fontCollection);

        fontName = ExtractFullName(fontData);
        }

        return fontName;
    }

    #region Private Helper

    /// <summary>
    /// Extracts the full font name from raw bytes.
    /// </summary>
    /// <param name="fontData">The font data as raw bytes.</param>
    /// <returns>The extracted full font name.</returns>
    private static string ExtractFullName(byte[] fontData)
    {
        string fontName = string.Empty;

        using (BinaryReader br = new BinaryReader(new MemoryStream(fontData)))
        {
        // Read selector (always = 0) to advance reader position by 2 bytes

        ushort selector = ToLittleEndian(br.ReadUInt16());

        // Get number of records and offset byte value, from where font descriptions start

        ushort records = ToLittleEndian(br.ReadUInt16());
        ushort offset = ToLittleEndian(br.ReadUInt16());

        // Get the correct name record

        NameRecord nameRecord = SeekCorrectNameRecord(br, records);

        if (nameRecord != null)
        {
            // Get the full font name for the record

            fontName = nameRecord.ProvideFullName(br, offset);
        }
        else
        {
            //TODO: Exception handling
        }

        br.Close();
        }

        return fontName;
    }

    /// <summary>
    /// Seeks the correct <see cref="NameRecord"/>.
    /// </summary>
    /// <param name="br">The <see cref="BinaryReader"/> to be used for reading the font metrics name table.</param>
    /// <param name="recordCount">The count of name records in the font metrics name table.</param>
    /// <returns>The <see cref="NameRecord"/> providing access to the correct full font name.</returns>
    private static NameRecord SeekCorrectNameRecord(BinaryReader br, int recordCount)
    {
        for (int i = 0; i < recordCount; i++)
        {
        NameRecord record = new NameRecord(br);

        if (record.IsWindowsUnicodeFullFontName)
        {
            return record;
        }
        }

        return null;
    }

    /// <summary>
    /// Loads the font metrics name table as raw bytes from the font in the <paramref name="fontCollection"/>.
    /// </summary>
    /// <param name="fontCollection">The font <see cref="PrivateFontCollection"/>.</param>
    /// <returns>The metrics table as raw bytes.</returns>
    private static byte[] LoadFontMetricsNameTable(PrivateFontCollection fontCollection)
    {
        byte[] fontData = null;

        // Create dummy bitmap to generate a graphics device context

        using (Graphics g = Graphics.FromImage(new Bitmap(1, 1)))
        {
        // Get the device context

        IntPtr hdc = g.GetHdc();

        using (FontFamily family = fontCollection.Families[0])
        {
            // Create handle to the font and load it into the device context

            IntPtr fontHandle = new Font(family, 0.0f).ToHfont();
            SelectObject(hdc, fontHandle);

            // First determine the amount of bytes in the font metrics name table

            uint byteCount = GetFontData(hdc, nameTableKey, 0, fontData, 0);

            // Now init the byte array and load the data by calling GetFontData once again

            fontData = new byte[byteCount];
            GetFontData(hdc, nameTableKey, 0, fontData, byteCount);

            // Release font handle

            DeleteObject(fontHandle);
        }

        g.ReleaseHdc(hdc);
        }

        return fontData;
    }

    /// <summary>
    /// Helper function to convert any <see cref="ushort"/> value in font metrics name table to little endian,
    ///  because all stored in big endian.
    /// </summary>
    /// <param name="value">The value to be converted into little endian byte order.</param>
    /// <returns>The corresponding <see cref="ushort"/> value in big endian byte order.</returns>
    private static ushort ToLittleEndian(ushort value)
    {
        if (BitConverter.IsLittleEndian)
        {
        byte[] bytes = BitConverter.GetBytes(value);
        Array.Reverse(bytes);

        return BitConverter.ToUInt16(bytes, 0);
        }

        return value;
    }

    /// <summary>
    /// Converts the raw bytes to a <see cref="string"/> by using UTF-16BE Encoding (code page 1201).
    /// </summary>
    /// <param name="bytes">The raw bytes.</param>
    /// <returns>The converted <see cref="string"/>.</returns>
    private static string BytesToString(byte[] bytes)
    {
        // Use UTF-16BE (Unicode big endian) code page

        return Encoding.GetEncoding(1201).GetString(bytes);
    }

    #endregion

    #region TTF NameRecord Class

    /// <summary>
    /// Encapsulates a name record as specified in True Type font specification for the font metrics name table
    /// http://www.microsoft.com/typography/otspec/name.htm
    /// </summary>
    private class NameRecord
    {
        private ushort platformId;
        private ushort encodingId;
        private ushort languageId;
        private ushort nameId;

        /// <summary>
        /// Gets length of the full font name as specified in the font file.
        /// </summary>
        /// <value>
        /// The length of the full font name name.
        /// </value>
        internal ushort NameLength { get; private set; }

        /// <summary>
        /// Gets the byte offset value, where the full font name information is stored within the font metrics table .
        /// </summary>
        /// <value>
        /// The byte offset value.
        /// </value>
        internal ushort ByteOffset { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="NameRecord"/> represents a Windows Unicode full font name.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this <see cref="NameRecord"/> represents a Windows Unicode full font name; otherwise, <c>false</c>.
        /// </value>
        internal bool IsWindowsUnicodeFullFontName
        {
        get
        {
            // platformId = 3 => Windows
            // encodingId = 1 => Unicode BMP (UCS-2)
            // nameId = 4 => full font name

            return platformId == 3 && encodingId == 1 && nameId == 4;
        }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NameRecord"/> class.
        /// </summary>
        /// <param name="br">The <see cref="BinaryReader"/> for interpretation of the bytes.</param>
        internal NameRecord(BinaryReader br)
        {
        // Read the unsigned 16-bit integers and convert to little endian

        platformId = ToLittleEndian(br.ReadUInt16());
        encodingId = ToLittleEndian(br.ReadUInt16());

        // Only read to advance reader position by 2 bytes

        languageId = ToLittleEndian(br.ReadUInt16());

        nameId = ToLittleEndian(br.ReadUInt16());
        NameLength = ToLittleEndian(br.ReadUInt16());
        ByteOffset = ToLittleEndian(br.ReadUInt16());
        }

        internal string ProvideFullName(BinaryReader br, int recordOffset)
        {
        // Search the start position of the font name

        int totalOffset = recordOffset + ByteOffset;
        br.BaseStream.Seek(totalOffset, SeekOrigin.Begin);

        // Now read the amount of bytes specified in the name record
        // and convert to a string

        byte[] nameBytes = br.ReadBytes(NameLength);
        return BytesToString(nameBytes);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
        return string.Format("NameRecord - Key; Platform ID = {0}, Encoding ID = {1}, Name ID = {2}",
                     platformId.ToString(), encodingId.ToString(), nameId.ToString());
        }


    }

    #endregion
    }

Alternative Managed API:

Do you know one? Please contribute it!

Documentation
GetFontData on MSDN

Please edit this page!

Do you have...

  • helpful tips or sample code to share for using this API in managed code?
  • corrections to the existing content?
  • variations of the signature you want to share?
  • additional languages you want to include?

Select "Edit This Page" on the right hand toolbar and edit it! Or add new pages containing supporting types needed for this API (structures, delegates, and more).

 
Access PInvoke.net directly from VS:
Terms of Use
Edit This Page
Find References
Show Printable Version
Revisions