Files
font2pic/DETAIL-DESIGN.md
2026-02-07 16:05:38 +08:00

243 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DETAIL-DESIGN
更新时间2026-02-07
## 1. 文档目标
本文定义 `font2svg` 的详细设计,覆盖以下范围:
- Web 前端(`frontend/`)的模块职责、数据结构、处理流程
- Python CLI`font2svg.py` / `pic2svg.py`)的职责边界
- 字体资源组织规范与导出策略
- 性能、错误处理、可维护性约束
## 2. 系统边界
## 2.1 子系统划分
1. `frontend/`:交互式预览与导出(主用户入口)
2. `scripts/generate-font-list.py`:字体清单构建
3. `font2svg.py`:命令行字体文本转 SVG
4. `pic2svg.py`:命令行图片转 SVG
## 2.2 非目标
- 不提供后端 API
- 不做字体版权管理系统
- 不做云端存储与用户账号
## 3. 前端架构设计
## 3.1 技术选型
- 框架Vue 3 + Composition API
- 构建Vite + `vite-plugin-wasm`
- 状态Pinia
- 样式UnoCSS + 手写样式
- 字体解析:`opentype.js`
- 字形 shaping`harfbuzzjs`(已封装,主链路暂未强依赖)
## 3.2 目录与模块映射
- `src/App.vue`:页面总编排、交互入口、导出触发
- `src/stores/fontStore.ts`:字体域状态
- `src/stores/uiStore.ts`UI 域状态与导出选择
- `src/components/FontSelector.vue`:字体搜索与树渲染入口
- `src/components/FontTree.vue`:分类树、收藏、预览勾选
- `src/components/FavoritesList.vue`:收藏列表
- `src/components/SvgPreview.vue`:预览生成调度
- `src/utils/svg-builder.ts`SVG 生成核心
- `src/utils/download.ts`:下载与打包
- `src/utils/font-loader.ts`:字体加载(含进度)
- `src/utils/text-layout.ts`:文本换行标准化
## 3.3 状态模型
## FontInfo
```ts
interface FontInfo {
id: string
name: string
path: string
category: string
isFavorite: boolean
font?: Font
loaded: boolean
progress: number
}
```
## UI 持久化 Key
- `font.favoriteFontIds`
- `font.previewFontIds`
- `font.expandedCategories`
- `ui.fontSize`
- `ui.inputText`
- `ui.textColor`
- `ui.selectedExportItems`
## 4. 关键流程设计
## 4.1 字体清单加载
1. `useFontLoader()``App.vue` 初始化阶段触发。
2. 请求 `/fonts.json`
3. 每条记录映射为 `FontInfo`,加入 `fontStore.fonts`
4. 调用 `updateFontTree()` 生成分组树。
异常策略:请求失败弹窗提示,并记录控制台错误。
## 4.2 字体按需加载
入口:`fontStore.loadFont(fontInfo)`
-`loaded=true` 或无路径则直接返回
- 同字体并发请求通过 `loadingFontTasks` 去重
- 使用 `loadFontWithProgress` 拉取字体并实时写入 `progress`
- 解析成功后写入 `fontInfo.font``loaded=true`
设计意图:避免初始一次性加载全部字体导致内存抖动。
## 4.3 预览生成调度
入口:`SvgPreview.vue`
1. 监听 `previewFonts/inputText/fontSize/fillColor` 变化。
2. 采用 `240ms` 防抖触发重算。
3. 每批最多处理 `20` 个字体,批内并发 `4`
4. 借助 `IntersectionObserver` 懒加载后续批次。
5. 使用 `previewGeometryCache` 缓存几何结果,颜色切换直接替换 token。
核心常量:
- `PREVIEW_DEBOUNCE_MS = 240`
- `PREVIEW_BATCH_SIZE = 20`
- `PREVIEW_CONCURRENCY = 4`
- `PREVIEW_GEOMETRY_CACHE_LIMIT = 600`
取消策略:用 `previewGenerationToken` 判定过期任务,避免旧结果污染。
## 4.4 文本布局
`wrapTextByChars(text, 45)` 策略:
- 统一换行符 `\r\n/\r -> \n`
- 保留手动换行
- 每行按字符数上限切分(默认 45
说明:该策略简单稳定,但不进行词边界或 East Asian 宽度感知换行。
## 4.5 SVG 生成
当前主链路:`generateSvg(options)`
- 基于 `opentype.js``charToGlyph` + path 指令拼装
- 计算 glyph 边界盒,汇总 viewBox 与 width/height
- 输出 `<g transform="translate(... ) scale(1 -1)">` 的坐标翻转结构
高级链路:`generateSvgWithHarfbuzz(options, fontBuffer)`
- 已封装 HarfBuzz shaping 与定位缩放逻辑
- 目前未接到主预览流程
## 4.6 导出流程
入口:`App.vue -> handleExport('svg' | 'png')`
1. 先清理失效导出项(不在当前预览集合中的项)
2. 单项导出:直接下载
3. 多项导出:聚合后 ZIP 下载
4. PNG 导出:先 SVG`canvas` 渲染为 Blob
命名规则:
- SVG`{fontPart}_{textPart}.svg`
- PNG`{fontPart}_{textPart}.png`
- `textPart` 截取前 8 字符
- 非法字符统一替换 `_`
## 5. 字体资源规范
## 5.1 目录约束
字体唯一来源:`frontend/public/fonts/`
- 支持多级目录
- 分类名来自相对目录路径
- 根目录字体分类记为 `未分类`
## 5.2 fonts.json 结构
`scripts/generate-font-list.py` 生成:
```json
{
"id": "分类/字体名",
"name": "字体名",
"filename": "字体文件名.ttf",
"category": "分类",
"path": "/fonts/分类/字体文件名.ttf"
}
```
## 6. Python CLI 设计
## 6.1 font2svg.py
职责:把给定字体与文本转换为 SVG。
- 使用 `uharfbuzz` 做 shaping
- 使用 `fonttools` 读取 glyph 与路径
- 支持单字体或目录批量转换
- 支持字距参数 `--letter-spacing`
## 6.2 pic2svg.py
职责:将二值化后的图片轮廓矢量化。
- OpenCV 预处理灰度与阈值
- 可选圆拟合快速路径
- 默认依赖 potrace 做高保真描边
## 7. 性能设计
- 字体元数据与字体文件解耦(先列表后按需)
- 预览批处理 + 并发上限
- 预览项懒加载
- 几何缓存与颜色 token 替换,减少重复 path 生成
## 8. 错误处理设计
- 用户可恢复错误:`alert` 提示(如空文本、未选导出项)
- 任务级错误:控制台 `console.error/warn`,不中断整个批次
- 资源加载失败:单字体失败不阻断其他字体渲染
## 9. 测试与质量门禁
当前仓库实际情况:
- 有 TypeScript 编译检查(`pnpm -C frontend run build` 中包含 `vue-tsc`
- 暂无标准 `lint` 脚本
- 暂无标准单元测试脚本
建议目标:
1. 增加 `lint`ESLint
2. 增加 `test`Vitest
3. 在 CI 中强制执行 `lint + typecheck + test`
## 10. 已知限制
- 主预览链路尚未默认启用 HarfBuzz shaping
- 自动换行仅按固定字符数,不考虑语义断句
- 浏览器内批量 PNG 导出在极大尺寸时可能触发内存压力
## 11. 后续演进建议
1.`generateSvgWithHarfbuzz` 接入主链路并提供回退机制
2. 为预览与导出建立一致性回归样例
3. 增加导出并发队列与失败重试(有限次数)
4. 统一日志级别,移除生产环境调试日志