You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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卸载加载的私有字体,避免系统资源泄漏。
  • 你提供的测试代码本身存在两个语法/逻辑错误,会直接导致运行失败:
    1. CreateDC第二个参数传入的printer变量未定义,应该传入实际的打印机名称字符串,也就是前面打开的"Microsoft Print to Pdf"
    2. 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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.17 16:16:01