Repository navigation
FontManager v2.1.5
FontManager 2.1.5
数据跟着安装目录走:装到 D 盘,标签 / 分组 / 收藏 / 台账 / 快照 / 缓存 / 日志 / 在线下载全在
D:\…\data下,C 盘不再新增长出来的软件数据;安装器界面同时重做成黑白灰的现代排版,
换掉了那张 NSIS 自带的蓝色侧栏图。升级老版本时数据自动搬过去,旧目录留一份当退路。
安装器 · 观感与排版
- 蓝色侧栏图没了。此前欢迎页/完成页左侧是 NSIS 自带的
win.bmp(一张 2003 年风格的蓝天图),
和主程序的黑白灰完全不搭。现在换成纯白 + 右缘 1px 细线,不放 logo:
欢迎页改成自绘后这张图只剩完成页在用,而完成页右侧已经有「立即运行 / 桌面快捷方式」两个交互,
左边再放个图只是抢注意力;另外 MUI 的FitControl会把图拉伸到控件尺寸,图标会被拉变形。
注意这里不是「放不放图」的选择题:MUI_WELCOMEFINISHPAGE_BITMAP的默认值就是那张蓝图,
置空会编译期报错,所以必须提供自己的sidebar.bmp(164×314 24bit,由
src-tauri/windows/gen-nsis-images.py用纯标准库生成,可重新生成;带图标的版本留在脚本里
另一个函数,默认不调用)。 - 欢迎页改成自绘(
Page custom)。此前欢迎页是 MUI 默认版式:标题固定 12pt、正文固定 130u 高、
左边永远留 109u 给位图,做不出「大标题 + 宽留白」的层次。现在是自己画的内页:
22pt 产品名 + 灰色副标题 + 1u 细线 + 两行说明 + 页脚,坐标一律用u(不是像素),
125%/150% 下跟着系统 DPI 走。没有去自绘窗口边框/按钮——那要把 hover/pressed/focus/
键盘 Tab/高对比度全自己兜,等于重写整个向导,还会丢屏幕阅读器。 - 一屏五行废话砍到三行。此前欢迎页是 MUI 默认文案(「建议关闭其它所有程序」之类),
占满一屏还没说清这软件是干什么的。现在大标题 + 一句副标题 + 两行说明,其余留白。 - 按钮文字保持 NSIS 原生(下一步/上一步/取消/安装/完成)。之前考虑过统一改措辞,
但^Next/^Install是全局 LangString,覆盖会影响所有页面,收益只是措辞 —— 不做。 - 页面流程仍是 4 页(欢迎 → 安装位置 → 安装中 → 完成)。开始菜单页的代码原样保留,
只是因为没有配startMenuFolder而被 PRE 回调跳过;删掉它会让「将来想配开始菜单名」
变成静默功能回退。 - 修掉欢迎页正文与页脚叠字。第一版自绘页按「页面高 200u+」排版,而实测内页白区只有约
141u(100% 下 1u≈1.88px、客户端 662×416、内页 y≈84..348px),页脚-22u解析成距顶约
119u,正好压在正文第二行上。现在整体上移、页脚下移留白(-10u),截图量出的文字带
标题 140..169 / 副标题 193..208 / 正文 257..270、287..300 / 页脚 332..341,两行正文与页脚
之间隔 32px 无重叠;注释里把「内页 141u、1u≈1.88px、负值=距底按内页高度解析」三个事实写死,
免得下次又按错误的页面高度排版。 - 升级维护页默认勾选「直接覆盖安装(推荐)」。此前默认停在「先卸载再安装」上
(Tauri 上游默认,2.1.4 也这样),2.1.5 给另一项加了「(推荐)」之后就自相矛盾:
用户直接点下一步 = 走卸载分支,卸载成功但安装失败就没软件了。
现在只改$ReinstallPageCheck的初值(升级/降级分支勾第 2 个 radio),
$R1/$R2/$R3的映射与卸载分支逻辑一个字节没动(R-18 红线)。 - 完成页会告诉你数据在哪。此前装完只有一句「完成」。现在多一行「数据保存在安装目录的 data 文件夹,
卸载时可选择是否保留」—— 「装到 D 盘数据在哪」这个问题不该让用户去翻文档。 - 安装位置页顶部有一句人话:「程序、字体库和全部数据都会保存在这个目录下。以后想整体搬走,
直接搬这个文件夹即可。」 - 选中
C:\Program Files会当场被拦下。此前普通用户选 Program Files 时,NSIS 会在写 exe
那一步报Error opening file for writing,然后留下一个「目录建了、文件没写、注册表没写全」的半装状态。
现在目录页离开时先做写探针(建目录 → 写一个 0 字节文件 → 删掉),失败就弹提示并停在原页。 - 升级时目录页被跳过、装回原目录。此前理论上允许升级时改安装目录 —— 老用户手滑把
%LOCALAPPDATA%\FontManager改成D:\FontManager,装完就是两套(快捷方式指向新的,
字体库和账本留在旧的),用户看到的是「升个级标签分组全没了」。现在注册表里已有安装目录时
直接跳过这一页并锁回去(显式/D=仍然按用户意愿执行,留给企业静默部署)。 - 中英双语,跟随系统。此前只有英文(MUI 默认)。现在
languages: ["SimpChinese","English"],
不显示语言选择框;文案集中在src-tauri/windows/zfm-strings.nsh,与模板分离。 - 全程没有
SetDetailsPrint none:安装过程中的 DetailPrint 明细仍可在「显示详细信息」里展开。
好看不能以「静默安装失败时零线索」为代价。
数据落盘 · 全部收进安装目录
<安装目录>\data成为唯一数据根。此前是%APPDATA%\FontManager(1.2.4 至今),
装到 D 盘也只是程序在 D 盘、数据留在 C 盘。现在state.json/prefs.json/font-cache.json/
system-ps.json/api.json/activation.log/preview-cache/thumb-cache/online/
staging/Trash全在data下。字体库与plugins故意不搬进data:它们本来就在安装目录下(用户的原话是「都放在
安装地址下面」,这两个已经满足),而且字体是用户资产、插件自带运行时。搬一次要改 4 处口径、
动几个 GB 的文件、改写 state 里几千条绝对路径 —— 收益为 0,事故面极大。- 四条判定链,只锚定一次:
ZFM_DATA_DIR(排障用,需绝对路径且真的可写)→<安装目录>\data
已存在即锚定 → 可写就建 →%LOCALAPPDATA%\FontManager\data→ 最后兜底%APPDATA%\FontManager。
为什么不每次重新探测:用户今天以管理员身份跑一次(Program Files 可写)、明天普通权限跑
(不可写),数据就会在两处之间横跳,表现为「我的标签怎么少了一半」。 activation.log第一行就写数据位置:data_root=<路径> source=install|localappdata|roaming(fallback)。
四条分支在界面上长得一模一样,有这一行,「你的数据在哪」一句话就能答。- 修掉一处会让老用户插件全消失的隐患:
plugins::legacy_plugins_dir()原本写成
app_data_dir().join("plugins")。app_data_dir()语义一改,这个「老位置」会漂到
<安装目录>\data\plugins,于是「老位置有插件 → 沿用老位置」的规则判定成「老位置什么都没有」,
老用户几十个插件会在界面上全部消失(文件其实还在%APPDATA%里)。现已钉死到
legacy_app_data_dir()。 - 本地 API 的报错文案不再骗人:此前硬编码「令牌在
%APPDATA%\FontManager\api.json」,
路径一改就是让用户去空目录里找。现在动态拼实际路径。 - MCP 服务发现跟着改:
mcp/index.mjs依次找ZFM_API_JSON→$ZFM_DATA_DIR\api.json→
%LOCALAPPDATA%\FontManager\data\api.json→%APPDATA%\FontManager\api.json
(后两个覆盖「默认安装」与「装到 Program Files 后的回退」两种常见情况)。
升级迁移 · 一个都不能少
- 自动把
%APPDATA%\FontManager的数据复制进data\,然后才切过去:state.json(标签 / 分组 /
收藏 / 启停台账 / 快照)、state.json.bak*(滚动备份是救命绳)、prefs.json*、font-cache.json、
system-ps.json、api.json、activation.log*、Trash\、online\、Deactivated\、plugins\
(仅当<安装目录>\plugins还不存在时)。 preview-cache(实测 385 MB)与thumb-cache(上限 256 MB)不搬,而且升级后旧的那份刻意不保留:
它们是纯生成物,搬一遍要读 640 MB 写 640 MB,机械盘上是分钟级,而用户的感受是「升级卡住了」;
留一份旧的又等于在 C 盘继续占 400 MB,与「缓存都放到安装目录下面」的要求相反。
所以迁移成功后旧缓存会被清掉、由新位置按需重新生成(首次预览/缩略图会慢一点)——
这是设计行为(验收项 A-06),不是「没删干净」;账本类(标签/分组/收藏/台账/快照/插件)
一个字节都不会少,那是「可回滚」真正要保的东西。- copy 不 move:搬到一半失败(磁盘满 / 权限 / 杀软拦)时源文件已经没了的场景绝不允许发生;
而且用户回退到 2.1.4 时还能接着用旧位置。代价是%APPDATA%\FontManager剩一份约 9 MB 的副本。 - 账本先写
.tmp再 rename,且回读校验字节数:直接 copy 到state.json被打断会留下半份文件,
下次启动会被当成「坏文件」走留证 + 回滚流程,用户看到的是「标签少了一半」。 .zfm-migrate-in-progress标记挡住最致命的一条链:迁移中途失败 → data 目录只剩半个.tmp→
下次启动当成全新安装 → 把默认 state 写进去 → 几年的账本被一份空账本覆盖。
只要标记在,load()就绝不会以「空账本」启动;老位置也没有可用账本时宁可用弹窗报错并停下。- 任一失败就回退老位置:搬不动就继续用
%APPDATA%\FontManager,功能完全正常,下次启动重试。 - 只有一处会删东西:老位置的
preview-cache/thumb-cache,且必须同时满足「目标账本已写入
并能反序列化」「白名单全部搬成功」「目标不是老位置本身」三个条件。 - 删旧缓存挪到后台线程,不再阻塞启动。实测删这两个目录(383MB + 256MB 上限)约 151 秒,
而迁移跑在建窗之前 —— 同步删会让首次升级启动黑屏两分半,用户以为软件卡死。
现在migrate_to_data_root立刻返回、界面正常出现,删除在std::thread::spawn里跑;
失败只记日志(文件被占用是常态),不重试、不拦启动。已知限制:进程若在删完之前被强杀
会留下部分残留(下次启动不补删 —— 幂等出口看到state.json就直接返回),
用户可手动删,只占磁盘、不影响功能;这不是缺陷,别当缺陷查。 - 幂等:唯一判据是
data\state.json是否存在。它还有两个副作用 —— 卸载时保留数据再装到别的目录,
会自动「收养」旧账本;连续启动多次不会重复搬。 - 迁移期间的日志延后写:迁移过程中数据根还可能回退,那时写日志会分裂到两个文件、
还会先建一个空的activation.log再被 copy 覆盖(丢掉会话头)。现在先收集,等路径定下来再统一落。
卸载 · 口径只加不减
- 复选框文案改成「同时删除全部数据与字体库」。此前用的是 Tauri 的默认文案
「Delete the application data」,但勾选后hooks.nsh实际会连$INSTDIR\字体库一起删
—— 里面有用户花钱买的字体。文案不写清楚就是在踩口径风险,这属于信息披露不是行为变更。 - 勾「同时删除应用数据」时新增两条白名单:
<安装目录>\data(先认state.json/
font-cache.json/api.json/.zfm-data-root任一特征文件,避免误删用户塞进同名目录的东西)
与%LOCALAPPDATA%\FontManager\data(Program Files 安装时的回退位置)。 - 这两段在
$UpdateMode <> 1括号里面,覆盖升级(/UPDATE)时一个字节都不动 ——
挪出去等于升级时把用户 1.7 MB 的账本连根删掉。 RMDir "$INSTDIR"保持非递归:data存在时它自动失败,正是我们要的。- 字体注册项的前缀白名单追加
$INSTDIR\data\(预防性:当前字体库不在 data 下,
这条今天匹配不到任何东西;留着是为了将来若真把库搬进 data,不会残留一堆指向已删文件的字体项)。
已知差异(不是 bug)
- MSI 版(按机器装进
Program Files)在普通权限下写不了程序目录,数据会走
%LOCALAPPDATA%\FontManager\data回退分支 —— 这是「程序目录不可写」的自洽行为。
MSI 卸载也没有「删除应用数据」复选框,数据永远保留。 bundle.targets仍是"all",NSIS 与 MSI 都出。- 安装器不是无边框自绘窗口:只有欢迎页是自绘的,目录页、进度条、完成页、按钮
仍由 Windows 主题绘制,做不到参考图那种「控件全自绘」的排版。理由写在模板头部注释里:
自绘等于把拖拽、DPI 换屏、键盘可达性、高对比度全部自己兜,维护成本远超收益。 - WebView2 内核缓存(约 100MB)仍在
%LOCALAPPDATA%\com.iieozo.fontmanager,本期不改道。
它不是软件数据,是 Windows 托管的浏览器 profile(里面还有界面语言等设置,换目录不搬 profile
会把语言重置)。改它需要把整个app.windows从tauri.conf.json搬进 Rust —— 窗口配置从此
没有单一事实来源,且 profile 初始化失败时窗口建不出来、用户双击毫无反应也没有错误框。
为一个一次性 100MB 付永久维护税不划算。README 已如实披露这块体积,卸载勾选删除数据时会一并清掉。 - 英文侧目录页标题仍是 MUI 原生的「Choose Install Location」(定稿表写「Choose a location」)。
为一句措辞去覆盖全局私有 LangString 不划算;中文侧「选择安装位置」与定稿表逐字一致。 - 设置页没有「数据位置」这一行(需要 3 个 i18n key × 9 种语言,列 P2):
位置信息这一版由「安装器完成页文案 + 启动日志第一行 + README」三处承担。 - 安装器模板是从 Tauri CLI 2.12.2 内嵌模板 fork 的(984 行 handlebars)。
升级 Tauri 时必须重新提取上游模板与src-tauri/windows/installer.nsi做 diff,
否则上游对安装器的修复不会自动进来,而且不会有任何报错。
⚠️ 本版本未做代码签名
两个安装包目前没有数字签名,Windows 因此无法验证发布者。下载或安装时可能遇到下面两种提示,
都不是病毒告警,按说明处理即可。
1. 浏览器提示「通常不会下载」(Edge / Chrome 的下载保护)
点提示条右侧的「…」或「更多」,选择 保留 / 仍然下载。
2. Windows 11 智能应用控制(Smart App Control)提示「已阻止可能不安全的应用」
这个提示没有「仍要运行」按钮,只有两个选择:
- 点「正常」关闭提示,按下面的方式验证文件完整性后自行决定是否继续;
- 或在「设置 → 隐私和安全性 → Windows 安全中心 → 应用和浏览器控制 → 智能应用控制」里
关闭该功能。注意:智能应用控制一旦关闭,通常无法再次开启(需重置或重装系统),
请自行权衡,不建议为装一个软件长期关掉系统防护。
验证文件完整性(推荐,用来确认下载没被篡改):
Get-FileHash .\FontManager_2.1.5_x64-setup.exe -Algorithm SHA256把输出与下方 SHA-256 对照,一致即说明文件完整。
本项目是个人维护的免费开源项目,没有购买代码签名证书,详见仓库根目录的
CODE_SIGNING_POLICY.md。
下载
| 文件 | 说明 |
|---|---|
FontManager_2.1.5_x64-setup.exe |
NSIS 安装包,推荐(同版本号可覆盖安装,装到当前用户目录,不需要管理员权限) |
FontManager_2.1.5_x64_en-US.msi |
MSI 安装包(同一版本号的 MSI 无法覆盖安装,升级请用 setup.exe) |
setup.exe:装到%LOCALAPPDATA%\FontManager,普通用户双击即可,同版本可直接覆盖重装。.msi:装到Program Files,需要管理员权限,适合域内 GPO / Intune / SCCM 静默推送:
msiexec /i FontManager_2.1.5_x64_en-US.msi /qn。
⚠️ 两个安装器共用注册表项HKCU\Software\IIEOzo\FontManager记录安装目录,
裸.msi只在干净机器上用;已经装过 setup.exe 的机器请继续用 setup.exe。
1.5.5起发布者署名改为IIEOzo,该键路径随之变化;若你曾自定义安装目录,
建议先卸载旧版(不要勾选「删除全部数据」)再安装本版,用户数据不会丢。
插件运行时(普通用户不用下)
python-runtime.zip / node-runtime.zip 是插件系统用的可选运行时。插件面板点「补全运行时」
时,程序从本 Release 的 latest/download/<kind>-runtime.zip 拉取,所以这两个包必须一起上传。
不装插件的话完全用不到,不必下载。
SHA-256:
a6b82715df8264639db4bec1e9299e42d797fd754c938a74c6fd053169325873 FontManager_2.1.5_x64-setup.exe
58908257f66d5b0dcecc463cd4e789393086ff58f4bcde887b4221364ff2089a FontManager_2.1.5_x64_en-US.msi