我给自己写了一个本地字体预览工具:选字体不再靠猜名字
目录
- 缘起:字体太多时,选择反而变慢
- 需求拆解:预览工具不是字体安装器
- 技术选型:系统字体 API 已经够用
- 核心实现一:枚举字体并建立可搜索清单
- 核心实现二:按示例文字实时渲染预览
- UI 定制:让比较过程少一次点击
- 三个字体预览中容易误判的细节
- 结尾:把选择字体变成看得见的比较
1. 缘起:字体太多时,选择反而变慢
电脑里安装的字体越来越多之后,找一个合适的字体反而变麻烦。设计软件里的字体下拉框通常只有一行小字,必须不断切换才能知道某个字体的中文效果、数字宽度和标点风格。
我经常遇到这些情况:
- 字体名字相似,但中文覆盖范围完全不同;
- 英文字母看起来不错,数字和符号却很奇怪;
- 同一个字体有多个粗细,列表中不容易区分;
- 项目需要比较几种字体,却只能靠记忆来回切换;
- 下载了字体文件后,不确定它是否已经安装。
我并不需要一个替我自动安装所有字体的软件,而是想先把电脑里能用的字体看清楚。于是写了一个本地预览工具:输入一段示例文字,所有字体用相同内容排列出来,滚动浏览即可比较。
2. 需求拆解:预览工具不是字体安装器
功能边界先定清楚,工具才能保持轻量:
| 诉求(人话) | 拆解后(可验收) |
|---|---|
| 看字体实际效果 | 用同一段示例文字批量渲染 |
| 快速定位字体 | 按字体名、语言覆盖和分类筛选 |
| 比较不同粗细 | 把同一字体的 family 与 style 分组显示 |
| 看清字体信息 | 展示文件来源、版本和支持的样式 |
| 预览自定义内容 | 支持输入标题、正文和数字样例 |
| 不改变系统状态 | 默认只读取,不自动安装或删除字体 |
字体安装和卸载会影响正在运行的应用,甚至需要管理员权限。第一版只做查看,用户确认后再用系统自己的字体安装流程处理文件。
心得:当一个工具的主要目标是比较,就不要把有副作用的管理动作混在同一个按钮里。
3. 技术选型:系统字体 API 已经够用
工具使用 .NET 8 和 WPF,字体相关能力直接来自 Windows 和 WPF:
| 能力 | 方案 |
|---|---|
| 枚举已安装字体 | System.Windows.Media.Fonts |
| 字体样式信息 | GlyphTypeface |
| 预览渲染 | WPF TextBlock |
| 列表虚拟化 | VirtualizingStackPanel |
| 本地设置 | JSON |
| 字体文件拖入 | WPF 拖放事件 |
我没有自己解析字体二进制表。字体格式包含大量元数据和变体规则,系统已经提供了可靠的字体加载和渲染能力,应用层只需要组织数据和界面。
字体预览是一个视觉密集型任务,列表必须启用虚拟化。否则一次创建几百个文本控件,会让启动和滚动明显变慢。
4. 核心实现一:枚举字体并建立可搜索清单
WPF 可以读取系统字体集合,并提供 family 和 style 信息:
using System.Windows.Media;
public static IReadOnlyList<FontEntry> LoadFonts()
{
return Fonts.SystemFontFamilies
.SelectMany(family => family.FamilyMaps.Select(map =>
new FontEntry(
family.Source,
map.Key.ToString(),
family)))
.OrderBy(item => item.FamilyName)
.ToList();
}
不同字体的本地化名称可能不一样,Source 更适合作为内部比较键,界面则显示当前系统语言下的名称。一个字体 family 可能对应多个 style 和语言映射,不能简单地把 family 名称当成唯一记录。
筛选器支持三种条件:字体名包含关键词、是否包含中文字符、是否包含粗体或斜体样式。中文覆盖判断不是简单看字体名字,而是用小段测试文本尝试创建 FormattedText,确认是否能找到对应字形。
扫描完成后把结果缓存到内存。字体安装或卸载后,用户可以点击“重新扫描”,而不是每次输入筛选词都重新访问系统字体集合。
5. 核心实现二:按示例文字实时渲染预览
每一行预览卡片包含字体名、style 标签和三段示例:中文、英文字母与数字符号。用户修改示例文字后,列表不会立即重建所有控件,而是使用防抖重新渲染当前可见区域。
private void RenderPreview(FontEntry entry, TextBlock block)
{
block.FontFamily = entry.Family;
block.FontStyle = entry.Style == FontStyles.Italic
? FontStyles.Italic
: FontStyles.Normal;
block.FontWeight = entry.Style == FontStyles.Bold
? FontWeights.Bold
: FontWeights.Normal;
block.Text = PreviewText;
}
预览字号提供小、中、大三档,但不把字号选择当成字体评分。某些字体在标题尺寸好看,在正文尺寸却显得拥挤;同时比较三档字号比只看一个尺寸更接近真实使用。
点击某一行会打开对比面板,最多选择四种字体并排展示。对比面板使用相同字号、行高和文字内容,避免因为布局参数不同而产生错觉。
字体加载失败时,列表显示占位符和失败原因。程序不会自动用相近字体替代,否则用户会误以为某个字体支持完整预览。
6. UI 定制:让比较过程少一次点击
窗口左侧是筛选和分类,右侧是预览列表。顶部的示例文字框始终可见,用户不用打开设置对话框就能替换内容。
每个字体卡片保留三个操作:加入对比、复制字体名、打开字体所在位置。复制的是 family 名称而不是内部文件路径,方便粘贴到 CSS、设计说明或文档中。
字体卡片下方显示“已安装”和“来源”标签。拖入一个字体文件时,工具只读取并预览临时字体,不立即安装;用户可以先确认效果,再点击“交给系统安装”。这样预览动作不会改变系统字体列表。
列表支持按字母、中文覆盖和最近使用排序。最近使用只保存字体标识,不保存文档内容,也不向外部服务同步。
7. 三个字体预览中容易误判的细节
坑一:字体名不等于字体覆盖范围。 一个字体的英文名可能暗示支持中文,但实际只包含拉丁字符。预览内容必须包含目标语言和标点,不能只看字母样例。
坑二:合成粗体不等于真实粗体。 系统可以对没有 Bold style 的字体做合成加粗,但设计结果和字体作者提供的真实粗体不同。工具把真实 style 和合成效果分开标记,避免把两者混在一起比较。
坑三:屏幕预览不等于印刷效果。 抗锯齿、显示器缩放和最终输出尺寸都会影响字体观感。预览工具只能帮助筛选候选字体,不能代替在实际画布和打印尺寸中的校样。
此外,字体文件可能包含多个 family 或可变轴。第一版把可变字体显示为一个可展开项目,并提供粗细轴的简单滑块,不把所有轴设置一次性塞进主界面。
8. 结尾:把选择字体变成看得见的比较
做完这个工具后,我不再凭字体名字或记忆选择。把同一段真实文案放进预览列表,中文、数字、标点和粗细差异会直接呈现出来。
一个好用的字体预览器应该做到:
- 使用真实示例,而不是只展示字体名称;
- 区分 family、style 和语言覆盖;
- 支持少量字体并排比较;
- 预览不会偷偷安装或删除字体;
- 明确提醒屏幕预览与最终输出之间的差异。
如果你也想做类似工具,可以先实现系统字体枚举和自定义文本预览,再加入筛选与对比面板。字体管理的复杂度很容易失控,先把“看清楚”做顺手,就已经能解决大部分日常选择问题。
字体选择不应该是一张小小下拉列表里的记忆题,而应该是一个可以直接观察和比较的过程。
- 点赞
- 收藏
- 关注作者
评论(0)