如何将.ttf字体嵌入Python打包的.exe并在任意电脑生效
如何将自定义字体嵌入打包后的Tkinter exe文件
我有一个正常运行的Python Tkinter程序,使用本地下载的Trajan Pro Regular.ttf字体。目标是将该字体嵌入打包后的.exe文件,让任何电脑运行程序时都能正确显示该字体,无需额外安装或放置字体文件在exe同目录。
尝试过的方法及问题
1. pyglet加载字体
使用pyglet加载字体时触发FileNotFoundError,提示找不到字体文件:
Traceback (most recent call last): File "ModifierTool.py", line 66, in <module> File "pyglet\font\__init__.py", line 156, in add_file FileNotFoundError: [Errno 2] No such file or directory: 'Trajan Pro Regular.ttf'
将字体放在exe同目录可消除错误,但未安装该字体的电脑仍无法正常显示;且这种方式不符合嵌入字体的需求。
2. 用resource_path加载字体
尝试用加载图片/资源的resource_path方法加载字体,代码片段如下:
import tkinter as tk import tkinter.font as tkfont import sys import os def resource_path(relative_path): try: base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) font_file = resource_path('Trajan Pro Regular.ttf') trajan_pro = tkfont.Font(family='Trajan Pro', file=font_file, size=10, weight='bold') def create_widgets(self): button_text = tk.Label(frame, text=option, font=trajan_pro, bg='#222222', fg='white')
该方法无效,字体仍无法正常加载。
3. tkextrafont加载字体
使用tkextrafont加载字体时直接失败,代码片段:
import tkinter as tk import pathlib import tkextrafont from tkextrafont import Font def create_widgets(self): fontpath = pathlib.Path(__file__).parent/"Trajan Pro Regular.ttf" font = tkextrafont.Font(file=str(fontpath), family= "Trajan Pro Regular", size= 12, weight= 'bold') all_button_label = tk.Label(frame, text='All', font=font, bg='#222222', fg='white') if __name__ == '__main__': root = tk.Tk()
4. AddFontResourceEx函数加载字体
使用Windows API的AddFontResourceEx函数加载字体,能在未安装字体的电脑上运行.py和.exe文件,但字体必须放在exe同目录,无法实现嵌入。打包时已添加--add-data "D:/Desktop/Modifier Tool/Trajan Pro Regular.ttf;."参数,直接使用PyInstaller命令结果一致。
问题复现代码:
import tkinter as tk import tkinter.font as tkfont from ctypes import windll, byref, create_unicode_buffer, create_string_buffer FR_PRIVATE = 0x10 FR_NOT_ENUM = 0x20 def loadfont(fontpath, private=True, enumerable=False): if isinstance(fontpath, bytes): pathbuf = create_string_buffer(fontpath) AddFontResourceEx = windll.gdi32.AddFontResourceExA elif isinstance(fontpath, str): pathbuf = create_unicode_buffer(fontpath) AddFontResourceEx = windll.gdi32.AddFontResourceExW else: raise TypeError('fontpath must be of type str or unicode') flags = (FR_PRIVATE if private else 0) | (FR_NOT_ENUM if not enumerable else 0) numFontsAdded = AddFontResourceEx(byref(pathbuf), flags, 0) return bool(numFontsAdded) class App(tk.Frame): def __init__(self, master=None, **kwargs): super().__init__(master, **kwargs) self.master = master self.create_widgets() def create_widgets(self): font_path = ("MyFont.ttf") loadfont(font_path) label = tk.Label(text='Hello', font=tkfont.Font(family='My Font')) label.pack() root = tk.Tk() app = App(root) root.mainloop()
补充:pyglet问题复现
使用pyglet的代码,除非字体放在exe同目录否则报错,放在同目录则能正常加载:
import tkinter as tk import tkinter.font as tkfont import pyglet pyglet.options['win32_gdi_font'] = True pyglet.font.add_file('MyFont.ttf') class App(tk.Frame): def __init__(self, master=None, **kwargs): super().__init__(master, **kwargs) self.master = master self.create_widgets() def create_widgets(self): label = tk.Label(text='Hello', font=tkfont.Font(family='My Font')) label.pack() if __name__ == '__main__': root = tk.Tk() app = App(root) root.mainloop()
报错信息:
Traceback (most recent call last): File "Test.py", line 7, in <module> File "pyglet\font\__init__.py", line 156, in add_file FileNotFoundError: [Errno 2] No such file or directory: 'MyFont.ttf'
内容的提问来源于stack exchange,提问作者Basile
相关产品推荐
相关产品推荐

