Python调用Windows API加载TTF字体实现文本打印的问题求解
问题描述
我一直在寻找加载ttf文件并使用对应字体打印文本的实现方法,查阅资料时找到了一篇讨论Windows API加载ttf字体的相关帖子。
帖子的解答中推荐使用AddFontResourceEx添加私有字体,但我未找到通过pywin32模块和ctypes.windll访问该函数的方法。请问是否有途径调用该函数?如果不行,是否有不使用Pillow库即可调用ttf字体打印文本的其他方法?
我提供如下测试代码供调试参考:
import win32print import win32gui import win32ui hprinter = win32print.OpenPrinter("Microsoft Print to Pdf") devmode = win32print.GetPrinter(hprinter, 2)["pDevMode"] hdc = win32gui.CreateDC("WINSPOOL", printer, devmode) dc = win32ui.CreateDCFromHandle(Self.hdc)
补充编辑1
我已经通过如下方式成功访问到了目标函数:
ctypes.windll.gdi32.AddFontResourceExA
但目前需要获取FR_PRIVATE常量的取值,请问该如何获取?
补充编辑2
我测试发现,即使不传入该常量,该函数也无法正常工作。
解答
FR_PRIVATE是Windows SDK定义的固定常量,值为0x10,作用是让加载的字体仅对当前进程可见,不会被系统全局枚举、不会影响其他程序的字体列表,不需要通过接口动态获取。- 你调用
AddFontResourceExA失败的核心原因是没有给ctypes声明函数的参数类型和返回值类型,ctypes默认的传参规则不会做类型校验,在32/64位环境下很容易出现传参错位、类型不匹配的问题,直接导致函数调用失败。正确的调用方式需要先声明函数原型:
import ctypes from ctypes import wintypes gdi32 = ctypes.WinDLL('gdi32', use_last_error=True) AddFontResourceExA = gdi32.AddFontResourceExA AddFontResourceExA.argtypes = [wintypes.LPCSTR, wintypes.DWORD, ctypes.c_void_p] AddFontResourceExA.restype = wintypes.INT FR_PRIVATE = 0x10 # 替换为本地ttf字体文件的实际路径,注意字符串要转成bytes格式 font_path = b"C:\\path\\to\\your\\custom_font.ttf" load_res = AddFontResourceExA(font_path, FR_PRIVATE, None) # 返回值为0代表加载失败,可通过get_last_error拿到系统错误码定位问题 if load_res == 0: error_code = ctypes.get_last_error() raise RuntimeError(f"私有字体加载失败,系统错误码:{error_code}")
- 字体加载成功后,直接用pywin32自带的GDI接口即可创建对应字体、选入打印DC完成文本打印,完全不需要依赖Pillow库。注意程序退出前要调用
RemoveFontResourceExA卸载加载的私有字体,避免系统资源泄漏。 - 你提供的测试代码本身存在两个语法/逻辑错误,会直接导致运行失败:
CreateDC第二个参数传入的printer变量未定义,应该传入实际的打印机名称字符串,也就是前面打开的"Microsoft Print to Pdf"CreateDCFromHandle传入的Self.hdc变量名错误,你前面定义的HDC句柄是小写开头的hdc,不存在Self前缀。
修正后的完整可运行打印逻辑参考:
import win32print import win32gui import win32ui import ctypes from ctypes import wintypes # 第一步:加载私有ttf字体 gdi32 = ctypes.WinDLL('gdi32', use_last_error=True) AddFontResourceExA = gdi32.AddFontResourceExA AddFontResourceExA.argtypes = [wintypes.LPCSTR, wintypes.DWORD, ctypes.c_void_p] AddFontResourceExA.restype = wintypes.INT RemoveFontResourceExA = gdi32.RemoveFontResourceExA RemoveFontResourceExA.argtypes = [wintypes.LPCSTR, wintypes.DWORD, ctypes.c_void_p] RemoveFontResourceExA.restype = wintypes.BOOL FR_PRIVATE = 0x10 # 替换为你的ttf文件路径 font_path = b"C:\\test\\custom_font.ttf" load_count = AddFontResourceExA(font_path, FR_PRIVATE, None) if load_count == 0: raise RuntimeError(f"字体加载失败,错误码:{ctypes.get_last_error()}") try: # 第二步:初始化打印DC printer_name = "Microsoft Print to Pdf" hprinter = win32print.OpenPrinter(printer_name) devmode = win32print.GetPrinter(hprinter, 2)["pDevMode"] hdc = win32gui.CreateDC("WINSPOOL", printer_name, devmode) dc = win32ui.CreateDCFromHandle(hdc) # 第三步:创建加载的私有字体,最后一个参数替换为你的ttf字体的内部名称(右键ttf文件-属性-详细信息可查) # CreateFont参数顺序:高度、宽度、文本旋转角度、字体倾斜角度、字重、斜体、下划线、删除线、字符集、输出精度、裁剪精度、渲染质量、字体族、字体名 custom_font = win32ui.CreateFont(24, 0, 0, 0, 400, False, False, False, 1, 0, 0, 4, 0, "你的字体内部名称") old_font = dc.SelectObject(custom_font) # 第四步:执行打印 dc.StartDoc("自定义字体打印测试") dc.StartPage() # 坐标(100,100)位置打印文本 dc.TextOut(100, 100, "这是自定义ttf字体打印的测试文本") dc.EndPage() dc.EndDoc() # 第五步:资源回收 dc.SelectObject(old_font) dc.DeleteDC() win32print.ClosePrinter(hprinter) finally: # 卸载私有字体 RemoveFontResourceExA(font_path, FR_PRIVATE, None)
注意:创建字体时传入的字体名称必须是ttf文件内置的字体名,不是ttf的文件名,否则会fallback到系统默认字体,无法加载你想要的字体效果。
内容的提问来源于stack exchange,提问作者Dante S.
相关产品推荐
相关产品推荐

