1555 字
8 分钟
用 Python 写个超实用的本地字体预览工具

拒绝一个个点开!用 Python 写个超实用的本地字体预览工具#

你一定经历过这样的崩溃时刻:

下载了一个庞大的字体包(里面有几十上百个 .ttf.otf 文件),想要挑一个适合当前项目的字体,结果发现:

  1. 系统预览太慢:必须双击每一个文件,等待系统字体查看器弹出来。
  2. 无法对比:一次只能看一个,关掉这个才能看下一个,完全没法做直观的对比。
  3. 文案固定:系统预览通常只显示 “The Quick Brown Fox” 或者 “天地玄黄”,根本没法看到你项目里实际要用的文案(比如标题)是什么效果。
  4. 垃圾文件干扰:如果在 macOS 下解压的文件,经常混杂着 ._ 开头的垃圾文件,很烦人。

痛点总结: 面对一堆字体文件,筛选难、预览难、对比难。

为了解决这个问题,我花半小时写了一个 Python 脚本。它不需要安装任何第三方库,就能瞬间把当前文件夹变成一个可视化的字体网页,支持自定义文案预览,还能一键调用系统安装。

它可以做什么?#

这个脚本(view_fonts.py)只有不到 100 行代码,但功能非常硬核:

  • 零依赖:只用 Python 标准库,不用 pip install 任何东西,即拷即用。
  • 批量扫描:自动扫描当前目录及子目录下的所有字体文件(.ttf, .otf, .woff 等)。
  • Web 可视化:直接在浏览器中渲染字体,利用 CSS 的 @font-face 实现最真实的预览。
  • 自定义预览:网页顶栏有一个输入框,输入什么文字,下面几百个字体就立刻显示什么文字。
  • 一键安装/打开:看到喜欢的字体,点击旁边的“安装/查看”按钮,直接调用系统(Windows/Mac/Linux)的原生程序打开该文件,方便你点击“安装”。
  • 智能过滤:自动忽略 macOS 产生的 ._ 隐藏文件,界面清清爽爽。

效果预览#

运行脚本后,浏览器打开 http://localhost:8899,你将看到如下界面:

  • 控制栏:可以输入想要预览的文案,拖动滑块调整字体大小。
  • 卡片流:每一个字体都是一张卡片,显示文件名、路径,以及大大的预览区域。

代码实现#

新建一个文件 view_fonts.py,将以下代码复制进去:

import os
import http.server
import socketserver
import urllib.parse
import sys
import platform
import subprocess
# === 配置 ===
PORT = 8899
EXTENSIONS = ('.ttf', '.otf', '.woff', '.woff2')
class FontPreviewHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# 解码 URL (处理中文路径)
req_path = urllib.parse.unquote(self.path)
# === 接口:请求打开文件 (实现“点击安装”的前一步) ===
if req_path.startswith('/api/open'):
# 解析参数,获取文件路径
query = urllib.parse.urlparse(req_path).query
params = urllib.parse.parse_qs(query)
if 'path' in params:
file_rel_path = params['path'][0] # 获取相对路径
# 拼接回绝对路径
abs_path = os.path.abspath(os.path.join(os.getcwd(), file_rel_path))
print(f"尝试打开文件: {abs_path}")
self.open_file_in_system(abs_path)
self.send_response(200)
self.end_headers()
self.wfile.write(b"OK")
return
# === 页面:主页 ===
if req_path == '/':
self.send_response(200)
self.send_header('Content-type', 'text/html; charset=utf-8')
self.end_headers()
font_files = []
current_dir = os.getcwd()
# 扫描文件
for root, dirs, files in os.walk(current_dir):
for file in files:
# 1. 过滤掉 macos 的垃圾文件 (以 ._ 开头)
if file.startswith('._'):
continue
if file.lower().endswith(EXTENSIONS):
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, current_dir)
# Web 路径需要转义,但保留原始路径用于“打开”功能
web_path = urllib.parse.quote(rel_path.replace('\\', '/'))
font_files.append({
'name': file,
'web_path': web_path, # 给浏览器加载字体用
'raw_path': rel_path.replace('\\', '/'), # 给 Python 打开文件用
'id': f'font_{len(font_files)}'
})
html_content = self.generate_html(font_files)
self.wfile.write(html_content.encode('utf-8'))
else:
# === 静态资源:加载字体文件 ===
super().do_GET()
def open_file_in_system(self, filepath):
"""跨平台打开文件"""
try:
if platform.system() == 'Darwin': # macOS
subprocess.call(('open', filepath))
elif platform.system() == 'Windows': # Windows
os.startfile(filepath)
else: # Linux
subprocess.call(('xdg-open', filepath))
except Exception as e:
print(f"打开文件失败: {e}")
def generate_html(self, fonts):
css_rules = []
html_cards = []
for f in fonts:
css_rules.append(f"""
@font-face {{ font-family: '{f['id']}'; src: url('{f['web_path']}'); }}
""")
# 注意:这里增加了一个“安装/打开”按钮
html_cards.append(f"""
<div class="font-card">
<div class="font-info">
<div class="info-left">
<span class="font-filename">{f['name']}</span>
<span class="font-path">{f['raw_path']}</span>
</div>
<button class="install-btn" onclick="openFile('{f['raw_path']}')">安装 / 查看</button>
</div>
<div class="preview-area" style="font-family: '{f['id']}';">
天地玄黄 宇宙洪荒 The Quick Brown Fox
</div>
</div>
""")
full_html = f"""
<!DOCTYPE html>
<html>
<head>
<title>字体预览 v2.0</title>
<style>
body {{ font-family: system-ui, sans-serif; background: #f0f2f5; padding: 20px; max-width: 1200px; margin: 0 auto; color: #333; }}
.controls {{ position: sticky; top: 0; background: #fff; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); z-index: 100; border-radius: 8px; margin-bottom: 20px; display: flex; gap: 15px; align-items: center; }}
input[type="text"] {{ flex: 1; padding: 10px; font-size: 16px; border: 1px solid #ddd; border-radius: 6px; }}
.font-list {{ display: grid; gap: 20px; }}
.font-card {{ background: #fff; padding: 20px; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border: 1px solid #e1e4e8; transition: transform 0.2s; }}
.font-card:hover {{ box-shadow: 0 4px 12px rgba(0,0,0,0.12); border-color: #2196f3; }}
.font-info {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; border-bottom: 1px solid #eee; padding-bottom: 10px; }}
.info-left {{ display: flex; flex-direction: column; }}
.font-filename {{ font-weight: bold; font-size: 14px; color: #1f2937; }}
.font-path {{ font-size: 12px; color: #9ca3af; margin-top: 2px; }}
/* 按钮样式 */
.install-btn {{
background: #2563eb; color: white; border: none; padding: 8px 16px;
border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500;
transition: background 0.2s;
}}
.install-btn:hover {{ background: #1d4ed8; }}
.install-btn:active {{ transform: scale(0.98); }}
.preview-area {{ font-size: 32px; word-break: break-all; line-height: 1.4; padding: 10px 0; }}
{''.join(css_rules)}
</style>
</head>
<body>
<div class="controls">
<input type="text" id="input-text" placeholder="输入文字预览..." value="天地玄黄 宇宙洪荒 The Quick Brown Fox">
<label>大小: <input type="range" id="size-range" min="12" max="120" value="36"></label>
<span style="font-size:14px; color:#666">共 {len(fonts)} 个字体</span>
</div>
<div class="font-list">
{''.join(html_cards)}
</div>
<script>
const input = document.getElementById('input-text');
const range = document.getElementById('size-range');
const previews = document.querySelectorAll('.preview-area');
input.addEventListener('input', (e) => {{
const val = e.target.value || 'Preview Text';
previews.forEach(div => div.textContent = val);
}});
range.addEventListener('input', (e) => {{
previews.forEach(div => div.style.fontSize = e.target.value + 'px');
}});
// 调用 Python 接口打开文件
function openFile(filePath) {{
// 对路径进行编码,防止中文乱码
const encodedPath = encodeURIComponent(filePath);
fetch(`/api/open?path=${{encodedPath}}`)
.then(res => {{
if(res.ok) console.log('已请求打开文件');
}})
.catch(err => console.error('打开失败', err));
}}
</script>
</body>
</html>
"""
return full_html
# 启动
print(f"正在扫描 (已过滤 ._ 文件)...")
print(f"打开: http://localhost:{PORT}")
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("", PORT), FontPreviewHandler) as httpd:
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass

如何使用#

  1. 将上述代码保存为 view_fonts.py

  2. 将该文件放入你存放字体的文件夹根目录。

  3. WIN打开终端(或 CMD),进入该目录,运行:

    Terminal window
    python view_fonts.py
  4. MAC打开终端,进入该目录,运行:

    python3 view_fonts.py
  5. 打开浏览器访问 http://localhost:8899

现在,你可以随意输入文案,看着几百个字体瞬间变成你想要的样子。遇到心仪的,点击“安装/查看”按钮,系统会自动弹出字体安装窗口,直接点击安装即可。

比起安装庞大的字体管理软件,这个不到 10KB 的小脚本是不是更香?

用 Python 写个超实用的本地字体预览工具
https://blog.wlens.top/posts/拒绝一个个点开用-python-写个超实用的本地字体预览工具/
作者
Lao Wang
发布于
2025-12-12
许可协议
CC BY-NC-SA 4.0