Python Programmatically Change Console font size

Question:

I found the code below which is supposed to programmatically change the console font size. I’m on Windows 10.

However, whatever values I tweak, I can’t seem to get any control over the font size, and also for some reason the console which gets opened when I run this script is very wide.

I have no idea how ctypes works – all I want is to modify the size of the console font from inside Python.

Any actual working solutions?

import ctypes

LF_FACESIZE = 32
STD_OUTPUT_HANDLE = -11

class COORD(ctypes.Structure):
    _fields_ = [("X", ctypes.c_short), ("Y", ctypes.c_short)]

class CONSOLE_FONT_INFOEX(ctypes.Structure):
    _fields_ = [("cbSize", ctypes.c_ulong),
                ("nFont", ctypes.c_ulong),
                ("dwFontSize", COORD),
                ("FontFamily", ctypes.c_uint),
                ("FontWeight", ctypes.c_uint),
                ("FaceName", ctypes.c_wchar * LF_FACESIZE)]

font = CONSOLE_FONT_INFOEX()
font.cbSize = ctypes.sizeof(CONSOLE_FONT_INFOEX)
font.nFont = 12
font.dwFontSize.X = 11
font.dwFontSize.Y = 18
font.FontFamily = 54
font.FontWeight = 400
font.FaceName = "Lucida Console"

handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
ctypes.windll.kernel32.SetCurrentConsoleFontEx(
        handle, ctypes.c_long(False), ctypes.pointer(font))


print("Foo")
Asked By: Robin Andrews

||

Answers:

It’s not a pure python problem, but covers Windows API.

Look at the documentation of CONSOLE_FONT_INFOEX structure. There is a COORD member on it for width and height of each character.

To change console font size, you can give these attributes as a proper value:

font.dwFontSize.X = 11
font.dwFontSize.Y = 18

Reference: Change console font in Windows

Answered By: Yinian Chin

Before everything, check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati’s answer) for a common pitfall when working with CTypes (calling functions).

The CTypes home page (also listed in the above URL): [Python.Docs]: ctypes – A foreign function library for Python

I changed your code "a bit".

code00.py:

#!/usr/bin/env python

import ctypes as cts
import ctypes.wintypes as wts
import sys


LF_FACESIZE = 32
STD_OUTPUT_HANDLE = -11


class CONSOLE_FONT_INFOEX(cts.Structure):
    _fields_ = (
        ("cbSize", wts.ULONG),
        ("nFont", wts.DWORD),
        ("dwFontSize", wts._COORD),
        ("FontFamily", wts.UINT),
        ("FontWeight", wts.UINT),
        ("FaceName", wts.WCHAR * LF_FACESIZE)
    )


def main(*argv):
    kernel32 = cts.WinDLL("Kernel32.dll")

    GetLastError = kernel32.GetLastError
    GetLastError.argtypes = ()
    GetLastError.restype = wts.DWORD

    GetStdHandle = kernel32.GetStdHandle
    GetStdHandle.argtypes = (wts.DWORD,)
    GetStdHandle.restype = wts.HANDLE

    GetCurrentConsoleFontEx = kernel32.GetCurrentConsoleFontEx
    GetCurrentConsoleFontEx.argtypes = (wts.HANDLE, wts.BOOL, cts.POINTER(CONSOLE_FONT_INFOEX))
    GetCurrentConsoleFontEx.restype = wts.BOOL

    SetCurrentConsoleFontEx = kernel32.SetCurrentConsoleFontEx
    SetCurrentConsoleFontEx.argtypes = (wts.HANDLE, wts.BOOL, cts.POINTER(CONSOLE_FONT_INFOEX))
    SetCurrentConsoleFontEx.restype = wts.BOOL

    # Get stdout handle
    stdout = GetStdHandle(STD_OUTPUT_HANDLE)
    if not stdout:
        print("{:s} error: {:d}".format(GetStdHandle.__name__, GetLastError()))
        return
    # Get current font characteristics
    font = CONSOLE_FONT_INFOEX()
    font.cbSize = cts.sizeof(CONSOLE_FONT_INFOEX)
    res = GetCurrentConsoleFontEx(stdout, False, cts.byref(font))
    if not res:
        print("{:s} error: {:d}".format(GetCurrentConsoleFontEx.__name__, GetLastError()))
        return
    # Display font information
    print("Console information for {:}".format(font))
    for field_name, _ in font._fields_:
        field_data = getattr(font, field_name)
        if field_name == "dwFontSize":
            print("    {:s}: {{X: {:d}, Y: {:d}}}".format(field_name, field_data.X, field_data.Y))
        else:
            print("    {:s}: {:}".format(field_name, field_data))
    while 1:
        try:
            height = int(input("nEnter font height (invalid to exit): "))
        except:
            break
        # Alter font height
        font.dwFontSize.X = 10  # Changing X has no effect (at least on my machine)
        font.dwFontSize.Y = height
        # Display font information
        res = SetCurrentConsoleFontEx(stdout, False, cts.byref(font))
        if not res:
            print("{:s} error: {:d}".format(SetCurrentConsoleFontEx.__name__, GetLastError()))
            return
        print("OMG! The window changed :)")
        # Get current font characteristics again
        res = GetCurrentConsoleFontEx(stdout, False, cts.byref(font))
        if not res:
            print("{:s} error: {:d}".format(GetCurrentConsoleFontEx.__name__, GetLastError()))
            return
        print("nNew sizes    X: {:d}, Y: {:d}".format(font.dwFontSize.X, font.dwFontSize.Y))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}n".format(" ".join(elem.strip() for elem in sys.version.split("n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("nDone.n")
    sys.exit(rc)

Notes:

  • CTypes allows low level access similar to C (only the syntax is Python)

  • Code uses [MS.Learn]: SetConsoleTextAttribute function

  • The ctypes.wintypes constants (which reference the standard CTypes types) are used (to give the code a Win like flavor)

  • It is very (almost painfully) long (also because I’ve added proper error handling)

  • An alternative, as suggested in one of the answers of [SO]: Change console font in Windows (where you copied the code from), would be to install a 3rd-party module (e.g. [GitHub]: mhammond/pywin32 – Python for Windows (pywin32) Extensions which is a Python wrapper over WINAPIs) which would require less code to write because the bridging between Python and C would be already implemented, and probably the above functionality could be accomplished in just a few lines

  • As I commented in the code, setting COORD.X seems to be ignored. But it’s automatically set when setting COORD.Y (to a value close to COORD.Y // 2 – probably to preserve the aspect ratio). On my machine (Win 10 pc064) with the currently selected font, the default value is 16. You might want to set it back at the end, to avoid leaving the console in a "challenged" state (apparently, Win adjusts Cmd window size, to be (sort of) in sync with the font size):

Img0

Answered By: CristiFati

For anyone in the future who wants to use the console as a method of displaying (black and white) images or just getting the smallest possible font size, this is the modified answer code I used. I found a height and width of 2px was the best smallest size, but be cautious, all sizes distort the picture, some more than others. As the font, I use the only "rasterfont" that’s available. (Personally I had massive problems getting this to work, not sure why tho… and this is NOT the same as the accepted answer)

def changeFontSize(size=2): #Changes the font size to *size* pixels (kind of, but not really. You'll have to try it to chack if it works for your purpose ;) )
    from ctypes import POINTER, WinDLL, Structure, sizeof, byref
    from ctypes.wintypes import BOOL, SHORT, WCHAR, UINT, ULONG, DWORD, HANDLE

    LF_FACESIZE = 32
    STD_OUTPUT_HANDLE = -11

    class COORD(Structure):
        _fields_ = [
            ("X", SHORT),
            ("Y", SHORT),
        ]

    class CONSOLE_FONT_INFOEX(Structure):
        _fields_ = [
            ("cbSize", ULONG),
            ("nFont", DWORD),
            ("dwFontSize", COORD),
            ("FontFamily", UINT),
            ("FontWeight", UINT),
            ("FaceName", WCHAR * LF_FACESIZE)
        ]

    kernel32_dll = WinDLL("kernel32.dll")

    get_last_error_func = kernel32_dll.GetLastError
    get_last_error_func.argtypes = []
    get_last_error_func.restype = DWORD

    get_std_handle_func = kernel32_dll.GetStdHandle
    get_std_handle_func.argtypes = [DWORD]
    get_std_handle_func.restype = HANDLE

    get_current_console_font_ex_func = kernel32_dll.GetCurrentConsoleFontEx
    get_current_console_font_ex_func.argtypes = [HANDLE, BOOL, POINTER(CONSOLE_FONT_INFOEX)]
    get_current_console_font_ex_func.restype = BOOL

    set_current_console_font_ex_func = kernel32_dll.SetCurrentConsoleFontEx
    set_current_console_font_ex_func.argtypes = [HANDLE, BOOL, POINTER(CONSOLE_FONT_INFOEX)]
    set_current_console_font_ex_func.restype = BOOL

    stdout = get_std_handle_func(STD_OUTPUT_HANDLE)
    font = CONSOLE_FONT_INFOEX()
    font.cbSize = sizeof(CONSOLE_FONT_INFOEX)

    font.dwFontSize.X = size
    font.dwFontSize.Y = size

    set_current_console_font_ex_func(stdout, False, byref(font))
Answered By: DinosaurMoritz
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.