Blog.wlens.top
1555 字
8 分钟
用 Python 写个超实用的本地字体预览工具
拒绝一个个点开!用 Python 写个超实用的本地字体预览工具
你一定经历过这样的崩溃时刻:
下载了一个庞大的字体包(里面有几十上百个 .ttf 或 .otf 文件),想要挑一个适合当前项目的字体,结果发现:
- 系统预览太慢:必须双击每一个文件,等待系统字体查看器弹出来。
- 无法对比:一次只能看一个,关掉这个才能看下一个,完全没法做直观的对比。
- 文案固定:系统预览通常只显示 “The Quick Brown Fox” 或者 “天地玄黄”,根本没法看到你项目里实际要用的文案(比如标题)是什么效果。
- 垃圾文件干扰:如果在 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 osimport http.serverimport socketserverimport urllib.parseimport sysimport platformimport subprocess
# === 配置 ===PORT = 8899EXTENSIONS = ('.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 = Truewith socketserver.TCPServer(("", PORT), FontPreviewHandler) as httpd: try: httpd.serve_forever() except KeyboardInterrupt: pass如何使用
-
将上述代码保存为
view_fonts.py。 -
将该文件放入你存放字体的文件夹根目录。
-
WIN打开终端(或 CMD),进入该目录,运行:
Terminal window python view_fonts.py -
MAC打开终端,进入该目录,运行:
python3 view_fonts.py -
打开浏览器访问
http://localhost:8899。
现在,你可以随意输入文案,看着几百个字体瞬间变成你想要的样子。遇到心仪的,点击“安装/查看”按钮,系统会自动弹出字体安装窗口,直接点击安装即可。
比起安装庞大的字体管理软件,这个不到 10KB 的小脚本是不是更香?
用 Python 写个超实用的本地字体预览工具
https://blog.wlens.top/posts/拒绝一个个点开用-python-写个超实用的本地字体预览工具/