Consoling oneself with TrueType

by Michael S. Kaplan, published on 2011/09/22 07:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2011/09/22/10215125.aspx


It was not quite a year ago, in Myth busting in the console, where Myth #12 was:

Myth #12: You can't change the setting of whether a console window is using a TrueType font.

This myth too is quite untrue.

I have used a few console API functions and that IsConsoleFontTrueType function from this blog to change the font within a console window to a TrueType font, from code running in the console window.

This is something I would never recommend in production code, mind you; I only did it because someone told me it wasn't possible and I was sure she was mistaken.

The impact of the accomplishment was interesting, mind you; she and I dated for about a month after that. ;-)

And the story there may be worthy of its own dedicated myth-busting blog, along with the code itself. If people are interested, I mean. Let me know....

I've had some people ask for the code, so I thought I would oblige...

Others wanted top hear the story about the relationship. Well let's just say it only lasted for a month. Like this code, it really was not something that worked in  production! 

Anyway, here is the code I wrote at the time:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Test {
    public static class ConsoleStuff {
        public static void Main() {
            SwitchToAnyTrueTypeFont();
        }

        internal static bool SwitchToAnyTrueTypeFont() {
            IntPtr stdout = GetStdHandle(STD_OUTPUT_HANDLE);
            uint nFont = 0;

            // First see if we are truetype already
            if (IsConsoleFontTrueType(stdout)) {
                return true;
            }

            // Get the current index to set it back if no font is found.
            CONSOLE_FONT_INFO cfi = new CONSOLE_FONT_INFO();
            if (GetCurrentConsoleFont(stdout, false, ref cfi)) {
                nFont = cfi.Index;
            }
            CONSOLE_FONT_INFO[] fonts = new CONSOLE_FONT_INFO[GetNumberOfConsoleFonts()];
            if (fonts.Length > 0) {
                GetConsoleFontInfo(stdout, false, (uint)fonts.Length, fonts);
            }

            //
            // Try to set each font until a TrueType one is found. This
            // part is hacky, but AFAIK there is no way to get TrueType
            // status on any fonts other than the current one.
            //
            for (int iFont = 0; iFont < fonts.Length; iFont++) {
                if (SetConsoleFont(stdout, fonts[iFont].Index)) {
                    if (IsConsoleFontTrueType(stdout)) {
                        return true;
                    }
                }
            }

            // Could not find a font, so give up
            SetConsoleFont(stdout, nFont);
            return false;
        }

        internal static bool IsConsoleFontTrueType(IntPtr std) {
            CONSOLE_FONT_INFO_EX cfie = new CONSOLE_FONT_INFO_EX();
            cfie.cbSize = (uint)Marshal.SizeOf(cfie);
            if (GetCurrentConsoleFontEx(std, false, ref cfie)) {
                return ((cfie.FontFamily & TMPF_TRUETYPE) == TMPF_TRUETYPE);
            }
            return false;
        }

        internal const int TMPF_TRUETYPE = 0x4;
        internal const int LF_FACESIZE = 32;
        internal const int STD_OUTPUT_HANDLE = -11; // Handle to the standard output device.

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct CONSOLE_FONT_INFO {
            internal uint Index;
            internal ushort dwFontSizeX;
            internal ushort dwFontSizeY;
        }
        [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
        internal struct CONSOLE_FONT_INFO_EX {
            internal uint cbSize;
            internal uint nFont;
            internal ushort dwFontSizeX;
            internal ushort dwFontSizeY;
            internal int FontFamily;
            internal int FontWeight;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
            internal string FaceName;
        }

        [DllImport("kernel32.dll", ExactSpelling = true)]
        internal static extern bool GetCurrentConsoleFontEx(
            IntPtr hConsoleOutput,
            bool bMaximumWindow,
            ref CONSOLE_FONT_INFO_EX lpConsoleCurrentFontEx);

        [DllImport("kernel32.dll", ExactSpelling = true)]
        internal static extern bool GetCurrentConsoleFont(
            IntPtr hConsoleOutput,
            bool bMaximumWindow,
            ref CONSOLE_FONT_INFO lpConsoleCurrentFont);

        [DllImport("kernel32")]
        private extern static bool SetConsoleFont(IntPtr hOutput, uint index);

        [DllImport("Kernel32.DLL", ExactSpelling = true)]
        internal static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32", ExactSpelling = true)]
        private static extern bool GetConsoleFontInfo(
            IntPtr hOutput,
            bool bMaximize,
            uint count,
            [MarshalAs(UnmanagedType.LPArray), Out] CONSOLE_FONT_INFO[] fonts);

        [DllImport("kernel32", ExactSpelling = true)]
        internal static extern uint GetNumberOfConsoleFonts();
    }
}

You get the point. Just don't get crazy on on it.... 


Simon Buchan on 22 Sep 2011 6:20 PM:

I'm assuming GetNumberOfConsoleFonts(), SetConsoleFont() is using Software\Microsoft\Windows NT\CurrentVersion\Console\{Raster,TrueType}Font?

I'm just glad Windows 7 made Consolas the default.


Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

go to newer or older post, or back to index or month or day