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")
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
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
-
In order to avoid setting invalid values, it’s used in conjunction with its counterpart: [MS.Learn]: GetCurrentConsoleFontEx function. Call that function in order to populate the CONSOLE_FONT_INFOEX structure, then modify only the desired values
-
There are "simpler" functions (e.g. [MS.Learn]: GetCurrentConsoleFont function or [MS.Learn]: GetConsoleFontSize function), but unfortunately there are no corresponding setters
-
Note that all referenced WinAPI functions are deprecated
-
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):
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))
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")
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
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
-
In order to avoid setting invalid values, it’s used in conjunction with its counterpart: [MS.Learn]: GetCurrentConsoleFontEx function. Call that function in order to populate the CONSOLE_FONT_INFOEX structure, then modify only the desired values
-
There are "simpler" functions (e.g. [MS.Learn]: GetCurrentConsoleFont function or [MS.Learn]: GetConsoleFontSize function), but unfortunately there are no corresponding setters
-
Note that all referenced WinAPI functions are deprecated
-
-
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):
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))