编程作品-即时字体安装

在公共场合的电脑上进行 PPT 演示时,这些电脑的字体安装情况往往和我们个人的有所出入,部分字体的缺失最终导致演示效果与我们所设想的情况不符,因此,有必要在开始 PPT 演示前就准备好字体并且安装,而传统字体的安装实在太过繁琐。基于 Python 语言的学习成果,我编写了一款程序,利用 Windows 的管理员授权,可以在其目录下安装所有需要的字体,从而保证演示效果。在使用时,建议提前打包为可执行文件。

源码如下:

import os
import sys
import shutil
import ctypes
import glob
import winreg

def is_admin():
    """检查当前是否拥有管理员权限"""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

def install_font(src_path):
    """安装单个字体"""
    font_dir = os.path.join(os.environ['WINDIR'], 'Fonts')
    font_filename = os.path.basename(src_path)
    dst_path = os.path.join(font_dir, font_filename)

    # 1. 复制文件到字体目录
    # 如果文件已存在,为了安全起见跳过复制,但仍会尝试注册
    if not os.path.exists(dst_path):
        try:
            shutil.copy(src_path, dst_path)
            print(f"[复制成功] {font_filename}")
        except Exception as e:
            print(f"[复制失败] {font_filename} - {e}")
            return False
    else:
        print(f"[已存在] {font_filename} (跳过复制)")

    # 2. 注册字体到注册表 (永久生效)
    # 注意:标准做法是解析字体内部名称,但直接用文件名作为 Key 在大多数情况下也是兼容的
    try:
        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, winreg.KEY_SET_VALUE)
        # 值名为 "文件名 (TrueType)" 的格式通常最稳妥,或者直接用文件名
        winreg.SetValueEx(key, f"{font_filename} (TrueType)", 0, winreg.REG_SZ, font_filename)
        winreg.CloseKey(key)
    except Exception as e:
        print(f"[注册表写入失败] {e}")

    # 3. 调用 Windows API 加载字体 (当前会话立即生效)
    # AddFontResourceW 返回安装的字体数量,0 表示失败
    result = ctypes.windll.gdi32.AddFontResourceW(dst_path)
    if result > 0:
        print(f"[加载成功] {font_filename}")
        return True
    else:
        print(f"[加载失败] 可能文件已损坏或非字体文件")
        return False

def main():
    # 强制以管理员身份运行
    if not is_admin():
        print("正在请求管理员权限...")
        # 重新运行当前程序,参数为 runas (管理员模式)
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
        return

    # 获取当前程序所在目录 (兼容 Pyinstaller 打包后的路径)
    if getattr(sys, 'frozen', False):
        base_path = os.path.dirname(sys.executable)
    else:
        base_path = os.path.dirname(os.path.abspath(__file__))

    print(f"正在扫描目录: {base_path} ...")
    
    # 扫描常见字体格式
    extensions = ['*.ttf', '*.otf', '*.ttc']
    fonts = []
    for ext in extensions:
        fonts.extend(glob.glob(os.path.join(base_path, ext)))

    if not fonts:
        print("未找到任何字体文件 (.ttf, .otf, .ttc)。")
        input("按回车键退出...")
        return

    success_count = 0
    for font in fonts:
        if install_font(font):
            success_count += 1

    # 4. 广播字体变更消息
    # 这一步至关重要,它告诉 PPT 等程序“有新字体了,快刷新一下缓存”
    HWND_BROADCAST = 0xFFFF
    WM_FONTCHANGE = 0x001D
    ctypes.windll.user32.SendMessageW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0)

    print(f"\n操作完成!共处理 {len(fonts)} 个文件,成功加载 {success_count} 个。")
    print("建议:如果 PPT 仍然显示不正常,请完全关闭 PPT 后重新打开。")
    input("按回车键退出...")

if __name__ == "__main__":
    main()