update at 2026-02-07 13:32:31

This commit is contained in:
douboer
2026-02-07 13:32:31 +08:00
parent b0e89a56e1
commit 951eda9c58
6 changed files with 73 additions and 79 deletions

56
PLAN.md
View File

@@ -6,6 +6,13 @@
**设计来源**: [Figma 设计稿](https://www.figma.com/design/S7WVUzg3Z0DMWjYUC6dJzN/font2svg?node-id=3-5&m=dev) **设计来源**: [Figma 设计稿](https://www.figma.com/design/S7WVUzg3Z0DMWjYUC6dJzN/font2svg?node-id=3-5&m=dev)
## 🔄 最新机制更新2026-02-07
- 字体唯一来源目录调整为 `frontend/public/fonts/`
- 删除独立 `font/` 源目录流程
- `pnpm run prepare-fonts` 仅执行字体扫描并重建 `frontend/public/fonts.json`
- 如本文后续历史段落仍出现 `font/` 或复制流程,请以上述机制为准
## 📋 Figma Annotations 功能需求清单 ## 📋 Figma Annotations 功能需求清单
以下所有功能需求来自 Figma 设计中各组件的 annotation 标注: 以下所有功能需求来自 Figma 设计中各组件的 annotation 标注:
@@ -25,7 +32,7 @@
-**批量导出**: 选中多个字体时批量导出 -**批量导出**: 选中多个字体时批量导出
### 4. 字体选择区 (节点 21:185) ### 4. 字体选择区 (节点 21:185)
-**数据源**: 从 `font/` 目录读取所有字体 -**数据源**: 从 `frontend/public/fonts/` 目录读取所有字体
-**树状结构**: 字体按**目录树状分组** -**树状结构**: 字体按**目录树状分组**
-**展开/收拢**: 支持展开和收拢 -**展开/收拢**: 支持展开和收拢
-**单选**: 字体支持单个选择 -**单选**: 字体支持单个选择
@@ -95,7 +102,7 @@
### 关键约束 ### 关键约束
- ✅ 保持 `font2svg.py` 不变,仅作为参考 - ✅ 保持 `font2svg.py` 不变,仅作为参考
- ✅ 纯前端实现,无需后端服务器 - ✅ 纯前端实现,无需后端服务器
- ✅ 字体文件放在 `font/` 目录 - ✅ 字体文件放在 `frontend/public/fonts/` 目录
- ✅ SVG 图标放在 `src/assets/icons/` - ✅ SVG 图标放在 `src/assets/icons/`
- ✅ 应用图标为 `src/assets/webicon.png` - ✅ 应用图标为 `src/assets/webicon.png`
- ✅ 使用 fonttools.subset 预处理字体(构建时) - ✅ 使用 fonttools.subset 预处理字体(构建时)
@@ -106,47 +113,26 @@
``` ```
font2svg/ font2svg/
├── font/ # 源字体文件(不打包) ├── frontend/
│ ├── 庞门正道/ │ ├── public/
│ ├── 王漢宗/ │ ├── fonts/ # 字体唯一来源目录(支持分类子目录)
│ └── 其他字体/ │ └── fonts.json # 由脚本重建的字体清单
├── public/ │ └── src/
│ └── fonts/ # 构建时生成的字体子集
├── scripts/
│ └── prepare-fonts.py # 字体预处理脚本fonttools.subset
├── src/
│ ├── assets/ │ ├── assets/
│ │ ├── icons/ # SVG UI 图标 │ │ ├── icons/ # SVG UI 图标
│ │ └── webicon.png # 应用图标 │ │ └── webicon.png # 应用图标
│ ├── components/ │ ├── components/
│ │ ├── FontSelector.vue # 字体选择器(树状结构)
│ │ ├── FontTree.vue # 字体树组件(展开/收拢)
│ │ ├── FavoritesList.vue # 收藏字体列表
│ │ ├── TextInput.vue # 文本输入框
│ │ ├── LetterSpacingSlider.vue# 字间距调整滑块
│ │ ├── SvgPreview.vue # SVG 预览区
│ │ ├── PreviewItem.vue # 单个字体预览项
│ │ └── ExportPanel.vue # 导出面板
│ ├── composables/ │ ├── composables/
│ │ ├── useFont.ts # 字体加载和管理
│ │ ├── useSvgGenerate.ts # SVG 生成核心逻辑
│ │ ├── useTextShaping.ts # HarfBuzz text shaping
│ │ └── useFavorites.ts # 收藏功能
│ ├── stores/ │ ├── stores/
│ │ ├── fontStore.ts # 字体状态管理
│ │ └── uiStore.ts # UI 状态(预览大小等)
│ ├── utils/ │ ├── utils/
│ │ ├── harfbuzz.ts # HarfBuzz WASM 封装
│ │ ├── font-loader.ts # 字体文件加载
│ │ ├── svg-builder.ts # SVG 文档构建
│ │ └── download.ts # 文件下载工具
│ ├── types/ │ ├── types/
── font.d.ts # TypeScript 类型定义 ── App.vue
├── App.vue # 根组件 └── main.ts
│ └── main.ts # 应用入口 ├── scripts/
├── vite.config.ts │ └── generate-font-list.py # 扫描 frontend/public/fonts 并重建 fonts.json
├── tsconfig.json ├── frontend/vite.config.ts
├── uno.config.ts # UnoCSS 配置 ├── frontend/tsconfig.json
├── frontend/uno.config.ts
└── package.json └── package.json
``` ```

View File

@@ -44,11 +44,9 @@
## 目录说明 ## 目录说明
- `frontend/`: 前端应用源码 - `frontend/`: 前端应用源码
- `frontend/public/fonts/`: 前端静态字体目录(由脚本生成 - `frontend/public/fonts/`: 字体目录(唯一字体来源,支持分类子目录
- `frontend/public/fonts.json`: 字体清单(由脚本生成 - `frontend/public/fonts.json`: 字体清单(由脚本重建
- `font/`: 原始字体目录(按分类子目录组织) - `scripts/generate-font-list.py`: 扫描 `frontend/public/fonts` 并生成 `fonts.json`
- `scripts/generate-font-list.py`: 生成 `fonts.json`
- `scripts/copy-fonts.py`: 复制字体到 `frontend/public/fonts`
## 环境要求 ## 环境要求
@@ -69,7 +67,7 @@ pnpm -C frontend install
将字体放入如下结构: 将字体放入如下结构:
```text ```text
font/ frontend/public/fonts/
手写/ 手写/
字体A.ttf 字体A.ttf
黑体/ 黑体/
@@ -82,10 +80,7 @@ font/
pnpm run prepare-fonts pnpm run prepare-fonts
``` ```
该命令会 该命令会扫描 `frontend/public/fonts/` 并重新生成 `frontend/public/fonts.json`
1. 扫描 `font/` 生成 `frontend/public/fonts.json`
2. 复制字体到 `frontend/public/fonts/`
### 3. 启动开发环境 ### 3. 启动开发环境
@@ -107,7 +102,7 @@ pnpm run build
# 本地预览构建产物 # 本地预览构建产物
pnpm run preview pnpm run preview
# 重新生成字体清单并复制字体 # 重新生成字体清单
pnpm run prepare-fonts pnpm run prepare-fonts
``` ```

View File

@@ -104,6 +104,13 @@
"category": "庞门正道", "category": "庞门正道",
"path": "/fonts/庞门正道/庞门正道标题体.ttf" "path": "/fonts/庞门正道/庞门正道标题体.ttf"
}, },
{
"id": "庞门正道-测试/庞门正道标题体",
"name": "庞门正道标题体",
"filename": "庞门正道标题体.ttf",
"category": "庞门正道-测试",
"path": "/fonts/庞门正道-测试/庞门正道标题体.ttf"
},
{ {
"id": "王漢宗/王漢宗勘亭流繁", "id": "王漢宗/王漢宗勘亭流繁",
"name": "王漢宗勘亭流繁", "name": "王漢宗勘亭流繁",

View File

@@ -6,6 +6,6 @@
"dev": "cd frontend && pnpm run dev", "dev": "cd frontend && pnpm run dev",
"build": "cd frontend && pnpm run build", "build": "cd frontend && pnpm run build",
"preview": "cd frontend && pnpm run preview", "preview": "cd frontend && pnpm run preview",
"prepare-fonts": "python3 scripts/generate-font-list.py && python3 scripts/copy-fonts.py" "prepare-fonts": "python3 scripts/generate-font-list.py"
} }
} }

4
run.sh
View File

@@ -4,3 +4,7 @@ source .venv/bin/activate
lsof -ti:5173,5174,5175,5176 | xargs kill -9 2>/dev/null; lsof -ti:5173,5174,5175,5176 | xargs kill -9 2>/dev/null;
python font2svg.py --fontdir font --text "星程紫微" --outdir svg python font2svg.py --fontdir font --text "星程紫微" --outdir svg
# 新增字体放在frontend/public/fonts目录下运行以下命令fonts.json会被更新
npm run prepare-fonts

View File

@@ -1,14 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
生成字体清单 JSON 文件 生成字体清单 JSON 文件
扫描 font/ 目录下的所有字体文件,生成一个 JSON 文件供前端使用 扫描 frontend/public/fonts/ 目录下的所有字体文件,生成 frontend/public/fonts.json
""" """
import os import os
import json import json
from pathlib import Path from pathlib import Path
def scan_fonts(font_dir='font'): def scan_fonts(font_dir='frontend/public/fonts'):
"""扫描字体目录,返回字体信息列表""" """扫描字体目录,返回字体信息列表"""
fonts = [] fonts = []
font_dir_path = Path(font_dir) font_dir_path = Path(font_dir)
@@ -17,25 +17,27 @@ def scan_fonts(font_dir='font'):
print(f"字体目录不存在: {font_dir}") print(f"字体目录不存在: {font_dir}")
return fonts return fonts
# 遍历所有子目录 # 递归遍历 fonts 目录(支持多级分类)
for category_dir in sorted(font_dir_path.iterdir()): for font_file in sorted(font_dir_path.rglob('*')):
if not category_dir.is_dir(): if not font_file.is_file():
continue continue
category_name = category_dir.name
# 遍历类别下的所有字体文件
for font_file in sorted(category_dir.iterdir()):
if font_file.suffix.lower() not in ['.ttf', '.otf']: if font_file.suffix.lower() not in ['.ttf', '.otf']:
continue continue
relative_parent = font_file.parent.relative_to(font_dir_path)
category_name = str(relative_parent).replace('\\', '/')
if category_name == '.':
category_name = '未分类'
relative_path = font_file.relative_to(font_dir_path).as_posix()
# 生成字体信息 # 生成字体信息
font_info = { font_info = {
'id': f"{category_name}/{font_file.stem}", 'id': f"{category_name}/{font_file.stem}",
'name': font_file.stem, 'name': font_file.stem,
'filename': font_file.name, 'filename': font_file.name,
'category': category_name, 'category': category_name,
'path': f"/fonts/{category_name}/{font_file.name}", 'path': f"/fonts/{relative_path}",
} }
fonts.append(font_info) fonts.append(font_info)
@@ -44,8 +46,8 @@ def scan_fonts(font_dir='font'):
def main(): def main():
"""主函数""" """主函数"""
# 扫描字体 # 扫描字体唯一来源frontend/public/fonts
fonts = scan_fonts() fonts = scan_fonts('frontend/public/fonts')
print(f"找到 {len(fonts)} 个字体文件") print(f"找到 {len(fonts)} 个字体文件")