[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-94619":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":9,"rankLanguage":9,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":9,"pushedAt":9,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":14,"starSnapshotCount":14,"syncStatus":34,"lastSyncTime":35,"discoverSource":36},94619,"Handwriting-simulator","bamboostrip\u002FHandwriting-simulator","bamboostrip","把普通文本变成以假乱真的手写体图片：手写字体 + 信纸背景 + 字距\u002F行距\u002F笔画随机扰动。GUI（PyQt6）与 CLI 双入口，numpy\u002Fscipy 高性能渲染引擎。",null,"Python",117,19,1,0,15,45.4,"MIT License",false,"main",true,[22,23,24,25,26,27,28,29,30],"cli","gui","handwriting","handwriting-simulation","image-generation","numpy","pyqt6","python","text-to-image","2026-08-24 04:01:22","# 手写模拟器（HandWriteSim）\n\n把普通文本变成以假乱真的手写体图片：选择一款手写字体、一张信纸背景，程序会按真实的书写习惯排版并施加**字距、行距、字号、笔画位移、笔画旋转**等多种随机扰动，让每个字、每一页都独一无二。\n\n提供 **图形界面（GUI）** 与 **命令行（CLI）** 两种使用方式，核心引擎基于 `numpy` + `scipy` 全向量化重写，预览渲染一页约 0.15 秒，实时交互流畅。\n\n## 与 Rust 版的关系\n\n本项目另有 [handwrite-sim](https:\u002F\u002Fgithub.com\u002Fbamboostrip\u002FHandwriting-sim-rs)（Rust 版）——纯 Rust 重写（ab_glyph + 自研笔画扰动引擎），两个版本功能保持同步：\n\n| | Python 版 | Rust 版 |\n| --- | --- | --- |\n| 渲染引擎 | numpy + scipy（FastEngine） | 纯 Rust：ab_glyph + 自研笔画扰动引擎 |\n| 界面 | PyQt6 | Slint（原生渲染，winit + femtovg，软件渲染兜底） |\n| PDF 导出 | ✅（同步对齐） | ✅ **300 DPI 位图层 PDF**（printpdf + lopdf） |\n| 错字率模拟 | ✅（同步对齐） | ✅ **错字率 + 划掉重写**（单线\u002F双线\u002F斜线\u002F叉号四种涂改样式） |\n| docx 导入 | python-docx | zip + quick-xml 自研解析（对齐\u002F首行缩进） |\n| 预览降采样 | PIL resize | fast_image_resize（SIMD，32MP 背景毫秒级） |\n\n> 两个版本功能保持同步（PDF 导出、错字率模拟 Python 版已对齐）。\n> 由于 Rust 版性能优异，部分新功能可能由 Rust 版先行实现，随后同步到 Python 版。\n\n## 技术栈\n\n| 类别 | 选型 |\n| --- | --- |\n| 语言 \u002F 运行时 | Python 3.14（`.python-version` 锁定） |\n| 依赖管理 | uv（`pyproject.toml` + `uv.lock`） |\n| 界面 | PyQt6（纯 Qt 控件 + 自动布局，无背景图片依赖） |\n| 渲染引擎 | numpy + scipy（FastEngine，默认）；handright 8.2.0（可选经典后端） |\n| 图像处理 | Pillow |\n| docx 解析 | python-docx |\n| 测试 | pytest |\n| 打包 | PyInstaller（onefile 单文件，跨 Windows\u002FmacOS\u002FLinux，`HandWriteSim.spec`） |\n\n## 功能特性\n\n### GUI（图形界面）\n\n- **富文本输入**：多段文本、空行，所见即所得\n- **段落排版工具**：左对齐 \u002F 居中 \u002F 右对齐 \u002F 首行缩进（按 2 倍字体大小），支持整段应用\n- **导入 docx**：自动解析段落对齐方式与首行缩进（支持 Word 的「首行缩进 2 字符」写法，沿样式链继承）\n- **字体 \u002F 背景选择**：字体支持 `.ttf` `.ttc` `.otf`；背景支持 `.png` `.jpg` `.jpeg` `.bmp`\n- **文字颜色**：直接输入 `#RRGGBB` 十六进制颜色值\n- **排版参数**：字水平间距、字竖直间距（行距）、字体大小，每个都带独立的随机扰动 σ\n- **笔画扰动**：水平位移、竖直位移、笔画旋转三个独立扰动强度\n- **边距设置**：上 \u002F 下 \u002F 左 \u002F 右 位置式布局，输入框位置即含义\n- **边界提示（仅预览）**：开关 + 自定义颜色，非渲染区域半透明着色并绘制边距框线，直观看清文字实际渲染边界（默认关闭）\n- **实时自动预览**：停止输入 300ms 后自动渲染，全程后台线程不卡界面；参数不完整时静默跳过\n- **多页预览**：上一页 \u002F 下一页 \u002F 页码指示，自动分页\n- **预览底色切换**：浅灰绿 \u002F 深灰两档，背景图与底色撞色时可切换以区分边界\n- **预设系统**：预设文件夹（`presets\u002F`）内预设可直接下拉切换，也支持保存 \u002F 载入任意位置（JSON 格式，颜色为 `#RRGGBB`），旧版预设自动兼容\n- **一键导出**：全部页面导出为 `0.png`、`1.png`……到 `output\u002F` 目录\n\n### CLI（命令行）\n\n- 纯文本或 docx 输入，批量生成手写图片\n- 全部排版 \u002F 扰动 \u002F 颜色参数可用命令行指定\n- 载入预设作为基础参数，命令行参数可覆盖\n- `--save-preset` 生成后保存参数预设\n- 未指定背景时自动生成纯白背景，可用 `--width` \u002F `--height` 控制尺寸\n- `--preview-only` 仅渲染第一页，快速查看效果\n\n### 渲染引擎\n\n- **高性能**：连通区域提取用 `scipy.ndimage.label`（C 实现），笔画扰动一次为全部笔画生成随机参数并做向量化坐标变换（旋转 + 平移），替代 handright 的逐像素 Python 循环\n- **两种后端**：默认 FastEngine；可切换 `backend=\"handright\"` 使用经典引擎对照\n- **段落化排版**：标题居中、右对齐、首行缩进、空行占位，逐行流式跨页，与纯文本路径行为一致\n- **真实书写习惯**：标点不换行（`end_chars`）、行首禁则字符（`start_chars`）等换行规则\n- **随机性可控**：引擎与排版各自持有随机源，可传入 `seed` 复现结果\n\n## 环境要求与安装\n\n- 操作系统：Windows \u002F macOS \u002F Linux（GUI 与打包均支持）\n- Python 3.12+（项目锁定 3.14）\n- 包管理器：[uv](https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002F)\n\n```powershell\n# 一键安装全部依赖（自动按 .python-version 选择 Python 版本）\nuv sync\n\n# 如需开发\u002F打包依赖（pytest、pyinstaller）\nuv sync --extra dev\n```\n\n### 依赖清单\n\n| 包 | 版本 | 用途 |\n| --- | --- | --- |\n| handright | 8.2.0 | 经典手写渲染后端（可选） |\n| numpy | >=2.5.1 | 向量化渲染计算 |\n| scipy | >=1.18.0 | 连通区域标记等 |\n| pillow | >=11,\u003C13 | 图像绘制与读写 |\n| PyQt6 | >=6.6 | 图形界面 |\n| python-docx | >=1.2.0 | docx 解析 |\n| pytest | >=8.0（dev） | 测试 |\n| pyinstaller | >=6.0（dev） | 打包 |\n\n## 快速开始\n\n### 启动图形界面\n\n```powershell\nuv run python main.py\n# 或\nuv run handwrite-gui\n```\n\n操作流程：输入文字 → 选择字体 → 选择背景 → （可选）调整排版参数与颜色 → 点「预览」或直接「导出」。\n\n### 便携模式（推荐）\n\nexe 首次运行会自动在自身所在目录创建 `fonts\u002F`、`backgrounds\u002F`、`presets\u002F` 三个文件夹：\n\n- `fonts\u002F`：把字体文件（`.ttf` \u002F `.ttc` \u002F `.otf`）放进去，选择字体时默认打开此目录\n- `backgrounds\u002F`：放背景图片，选择背景时默认打开此目录\n- `presets\u002F`：放预设文件，界面上的预设下拉框会列出此目录内的预设，一键切换\n\n整个文件夹（exe + 资源目录）可以随意拷贝到任意位置，**所有相对路径都以 exe 所在目录为锚点**，无需修改任何路径。\n\n### 命令行示例\n\n```powershell\n# 最简：微软雅黑字体，导出到 output\u002F\nuv run handwrite-cli \"你好，世界！\" --font C:\u002FWindows\u002FFonts\u002Fmsyh.ttc\n\n# 指定背景与字号，多页文本自动分页\nuv run handwrite-cli \"第一页正文……第二页正文……\" --font f.ttf --background bg.png --font-size 40\n\n# 载入预设 + 命令行覆盖参数\nuv run handwrite-cli --preset preset.json \"文本内容\" --font-size 48 --out output\n\n# 导入 docx（解析对齐与首行缩进）\nuv run handwrite-cli --docx 通知.docx --font f.ttf --background 信纸.png\n\n# 仅预览第一页\nuv run handwrite-cli \"你好\" --font f.ttf --background bg.png --preview-only\n\n# 生成并保存当前参数为预设\nuv run handwrite-cli \"你好\" --font f.ttf --background bg.png --save-preset my.preset.json\n```\n\n## CLI 参数说明\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `text` | 位置参数 | `\"\"` | 要处理的手写文本 |\n| `--docx` | 路径 | `\"\"` | 导入 docx（解析对齐与首行缩进），与 text 互斥优先 |\n| `--font` | 路径 | 必填 | 字体文件（`.ttf` \u002F `.ttc`） |\n| `--background` | 路径 | `\"\"` | 背景图片；缺省时生成纯白背景 |\n| `--width` \u002F `--height` | int | 800 \u002F 1200 | 纯白背景尺寸 |\n| `--out` | 目录 | `output` | 输出目录 |\n| `--preset` | 路径 | `\"\"` | 载入预设（`.json` \u002F `.txt` \u002F `.preset`） |\n| `--save-preset` | 路径 | `\"\"` | 生成后保存参数为预设 |\n| `--preview-only` | flag | 关 | 仅渲染第一页保存为 `preview.png` |\n| `--font-size` | int | 36 | 字体大小 |\n| `--word-spacing` | int | 5 | 字水平间距 |\n| `--line-spacing` | int | 48 | 字竖直间距（行距，不含字高） |\n| `--left\u002Fright\u002Ftop\u002Fbottom-margin` | int | 30 | 四周边距 |\n| `--word-spacing-sigma` | int | 2 | 字间距随机扰动 |\n| `--line-spacing-sigma` | int | 2 | 行距随机扰动 |\n| `--font-size-sigma` | int | 2 | 字号随机扰动 |\n| `--perturb-x-sigma` | int | 2 | 笔画水平位移扰动 |\n| `--perturb-y-sigma` | int | 2 | 笔画竖直位移扰动 |\n| `--perturb-theta-sigma` | float | 0.05 | 笔画旋转扰动（弧度） |\n| `--red` \u002F `--green` \u002F `--blue` | int | 0 | 文字颜色 RGB（0-255） |\n\n## 预设文件格式\n\n### JSON 预设（v2，现行格式）\n\n保存到 `.json` 时使用版本 2 结构，**只保存排版参数**（不含文本内容），颜色为 `#RRGGBB` 十六进制：\n\n```json\n{\n  \"version\": 2,\n  \"params\": {\n    \"color\": \"#000000\",\n    \"font_path\": \"C:\u002FWindows\u002FFonts\u002Fmsyh.ttc\",\n    \"background_path\": \"D:\u002F信纸\u002F横线信纸.png\",\n    \"font_size\": 36,\n    \"word_spacing\": 5,\n    \"line_spacing\": 48,\n    \"left_margin\": 30,\n    \"right_margin\": 30,\n    \"top_margin\": 30,\n    \"bottom_margin\": 30,\n    \"word_spacing_sigma\": 2,\n    \"line_spacing_sigma\": 2,\n    \"font_size_sigma\": 2,\n    \"perturb_x_sigma\": 2,\n    \"perturb_y_sigma\": 2,\n    \"perturb_theta_sigma\": 0.05,\n    \"end_chars\": \"，。\",\n    \"start_chars\": \"\"\n  }\n}\n```\n\n> 预设刻意**不包含** `text` \u002F `paragraphs`：文本属于内容而非排版风格，载入预设时保留当前输入框内容。\n\n### 兼容性\n\n| 旧格式 | 载入行为 |\n| --- | --- |\n| JSON v1（`red` \u002F `green` \u002F `blue` 数字） | 自动转换载入，等价于新 hex 颜色 |\n| 旧版 18 行纯文本（`.txt` \u002F `.preset`） | 格式不变，照常载入 |\n\n旧预设载入后若再保存，将统一以新格式（v2 + `#RRGGBB`）写出。\n\n## 项目结构\n\n```\nHandwriting-simulator\u002F\n├── main.py                      # GUI 启动入口（uv run python main.py）\n├── pyproject.toml               # 项目元数据、依赖声明、脚本入口、uv 依赖覆盖\n├── uv.lock                      # 依赖锁定文件\n├── .python-version              # Python 版本锁定（3.14）\n├── HandWriteSim.spec            # PyInstaller 打包配置（onefile，跨平台）\n├── build.ps1                    # Windows 一键打包脚本\n├── backgrounds\u002F                 # 内置背景素材（随仓库分发，可自行替换）\n├── presets\u002F                     # 内置预设示例（JSON v2，相对路径引用资源）\n├── fonts\u002F                       # 运行时自动创建，用户自备字体（版权字体不入库）\n├── ui\u002F                          # GUI 资源\n│   └── 3d.ico                   # 窗口图标\n├── docs\u002Fsuperpowers\u002F            # 设计文档（段落化排版功能的设计与实现计划）\n│   ├── plans\u002F\n│   └── specs\u002F\n├── src\u002Fhandwritesim\u002F            # 包源码（src 布局）\n│   ├── app.py                   # GUI 应用入口（QApplication + MainWindow）\n│   ├── cli.py                   # 命令行入口（argparse）\n│   ├── core\u002F                    # 核心逻辑层（不依赖任何 GUI 组件）\n│   │   ├── models.py            # HandwritingParams \u002F Paragraph 数据模型、校验、序列化\n│   │   ├── paths.py             # 资产目录解析（exe 旁\u002F项目根）与目录自动创建\n│   │   ├── engine.py            # 引擎门面：统一接口，默认 fast 后端，可切 handright\n│   │   ├── engine_fast.py       # FastEngine：numpy\u002Fscipy 高性能渲染\n│   │   ├── engine_handright.py  # HandrightEngine：handright 经典后端\n│   │   ├── docx_io.py           # docx 解析：段落对齐 + 首行缩进（含样式链继承）\n│   │   └── presets.py           # 预设读写：JSON v2 + 旧版文本兼容 + 相对路径双向转换\n│   └── gui\u002F                     # 图形界面层\n│       ├── ui.py                # Qt 界面构建（纯控件 + 自动布局）\n│       ├── main_window.py       # 主窗口逻辑：参数映射、事件、预览降采样、预设下拉切换\n│       ├── workers.py           # QThread 后台渲染 \u002F 导出，信号回传结果\n│       └── resources.py         # 资源路径解析（开发 \u002F PyInstaller 双环境）\n└── tests\u002F                       # pytest 测试\n    ├── test_docx_io.py          # docx 对齐与缩进解析\n    ├── test_engine.py           # 引擎接口、校验、参数序列化\n    ├── test_engine_fast.py      # FastEngine 渲染与段落路径\n    └── test_presets.py          # 预设读写、hex 颜色、相对路径往返、新旧格式兼容\n```\n\n### 分层约定\n\n- `core\u002F` 不 import 任何 GUI 模块，GUI 与 CLI 共同依赖 core，保证命令行与图形界面行为一致\n- 数据流：GUI\u002FCLI → `HandwritingParams`（校验）→ `HandwritingEngine` → 图片\n- 界面控件名沿用历史命名（`lineEdit_10` 等），`main_window.py` 通过对象名引用，与 `ui.py` 解耦\n\n## 渲染引擎原理\n\n### FastEngine（默认）\n\n针对 handright 逐像素纯 Python 循环的性能瓶颈，用 numpy + scipy 重写：\n\n1. **排版**：复用 PIL C 层 `ImageDraw.text` 逐字绘制（本就快，非瓶颈），逐字字号 \u002F 行位高斯扰动，字符宽度按 `(字号, 字符)` 缓存\n2. **连通区域提取**：`scipy.ndimage.label`（C 实现）一次得到所有笔画的像素标签，替代原 Python DFS\n3. **笔画扰动**：对每个笔画用 numpy 向量化坐标变换（绕笔画中心旋转 + 平移），一次写回画布\n\n### 段落渲染路径\n\n`params.paragraphs` 非空时启用：每个段落独立排版（对齐 \u002F 首行缩进），按行提取墨迹后逐行流式拼接跨页；行节奏与纯文本路径完全对齐，空行保留占位。居中按行测量水平置中；右对齐按行逻辑宽度（含尾部空格）平移到右边距。\n\n### 随机性\n\n- 排版随机源：`random.Random(seed)`，保证逐字扰动序列可复现\n- 笔画扰动随机源：`numpy.random.default_rng(seed)`\n- 预览与导出的随机行为一致（预览仅针对超大背景做参数等比缩放）\n\n### 换行规则\n\n- `end_chars`（默认 `，。`）：行尾遇到这些字符时不换行，避免标点孤悬行首\n- `start_chars`：行首禁则字符\n\n### 预览降采样策略\n\n背景宽度超过 4096px 时，预览将背景缩放到 4096 宽，并**按同一比例缩放全部空间参数**（字号、边距、间距、扰动），保证预览与导出布局一致、行线不错位；导出始终使用原始参数全分辨率渲染。\n\n## 开发指南\n\n```powershell\n# 运行全部测试\nuv run pytest\n\n# 运行单个测试文件\nuv run pytest tests\u002Ftest_presets.py -v\n```\n\n当前测试覆盖：引擎生成 \u002F 预览 \u002F 导出、参数校验、段落往返序列化、docx 对齐与缩进解析、预设新旧格式兼容、hex 颜色解析等。\n\n### 代码规范要点\n\n- 核心逻辑放 `core\u002F`，GUI 只做控件映射与任务调度\n- 渲染 \u002F 导出在 `QThread` 中执行，禁止在子线程操作控件\n- 参数校验统一在 `HandwritingParams.validate()`，GUI 与 CLI 共用\n- 数字输入框禁用滚轮改值（`NoWheelSpinBox`），避免与滚动面板冲突\n\n### 设计文档\n\n功能开发遵循「先设计后编码」流程，相关设计文档位于 `docs\u002Fsuperpowers\u002Fspecs\u002F` 与 `docs\u002Fsuperpowers\u002Fplans\u002F`（如段落化排版的设计与实现计划）。\n\n## 字体与版权\n\n**仓库不随包分发任何字体**：手写体多为商业版权字体（如汉呈、华阳、云江等字库），开源分发需授权，故请用户自备字体。推荐以下**开源 \u002F 免费可商用**的手写体（下载后放入 `fonts\u002F` 即可）：\n\n| 字体 | 协议 | 说明 |\n| --- | --- | --- |\n| 霞鹜文楷（LXGW WenKai） | OFL 1.1 | 开源可商用，最接近手写体观感 |\n| 沐瑶随心手写体 | 免费可商用 | 灵动手写风格 |\n| 站酷小薇 \u002F 站酷快乐体 | 免费可商用 | 站酷字库出品 |\n| 内海字体（NeiHai） | 开源 | 手写圆体 |\n\n仓库内置的 `backgrounds\u002F`（信纸、格子纸等）与 `presets\u002F`（参数示例）均为原创素材，可自由使用与分发。\n\n## 打包发布\n\n```powershell\n# Windows\npowershell -ExecutionPolicy Bypass -File build.ps1\n\n# macOS \u002F Linux（等价命令）\nuv run --extra dev pyinstaller --noconfirm --clean HandWriteSim.spec\n```\n\n> 也可以直接在 GitHub Actions 中构建：推送 `v*` 标签或手动触发 `Build and Release` workflow（`.github\u002Fworkflows\u002Fbuild.yml`），会自动在 Windows \u002F Linux \u002F macOS 三平台打包并组装便携 zip（exe + 预设 + 背景 + fonts 目录），打标签时自动发布到 Release。\n\n- 产物：`dist\u002FHandWriteSim`（**单文件**，Windows 约 49 MB，Linux\u002FmacOS 体积相近）\n- 便携模式：首次运行自动创建 `fonts\u002F`、`backgrounds\u002F`、`presets\u002F` 目录，把资源放进去即可，整个文件夹可随意拷贝\n- 打包配置见 `HandWriteSim.spec`（PyInstaller onefile 模式），已按 `sys.platform` 跨平台适配：\n  - imageformats 插件按平台文件名收集（`qjpeg.dll` \u002F `libqjpeg.so` \u002F `libqjpeg.dylib`）\n  - 未使用 Qt DLL 的剔除清单仅 Windows 生效，其余平台保留避免启动失败\n  - 图标仅 Windows 支持 `.ico`，Linux\u002FmacOS 使用默认图标\n  - UPX 压缩仅非 macOS 平台启用（不破坏签名）\n- 首次启动时单文件会自解压到系统临时目录，启动稍慢属正常现象\n- 首次运行若被杀毒软件（如 Windows Defender）实时扫描拦截，等待扫描完成或将其加入白名单后即可正常启动（PyInstaller 单文件程序的共性现象，并非程序问题）\n\n## 常见问题\n\n**为什么没有随包字体？**\n手写体多为商业版权字体，开源分发需授权，故仓库不提供字体。请自备字体放入 `fonts\u002F` 目录，或从上方「字体与版权」章节的开源字体清单中下载。预设中的字体路径为占位（如 `fonts\u002F云烟体.ttf`），对应的字体文件放入后即可使用，也可以直接改用其他字体。\n\n**为什么预览没有边界提示？**\n边界提示默认关闭，勾选「边界提示(仅预览)」后按边距参数在预览图上着色并绘制框线，可自定义提示颜色（`#RRGGBB`）。\n\n**载入预设后文本被清空了吗？**\n不会。预设只包含排版参数，载入时保留当前文本输入框内容；仅当旧预设本身携带文本时才会回填。\n\n**自动预览什么时候不触发？**\n字体 \u002F 背景缺失、未输入文本时静默跳过，不弹窗打断输入；输入停止 300ms 后触发。\n","HandWriteSim 是一款将普通文本生成高仿真手写体图片的工具，支持手写字体渲染、信纸背景叠加及多维度随机扰动（字距、行距、笔画位移与旋转等），确保输出结果具备自然书写特征。项目采用 numpy\u002Fscipy 全向量化渲染引擎实现高性能处理（单页预览约0.15秒），提供 PyQt6 图形界面与命令行双入口，支持 docx 导入、段落排版、错字率模拟及 PDF\u002F图片批量导出。适用于教育材料制作、个性化贺卡生成、AI 内容防检测测试、手写风格内容原型设计等需真实感文本图像的场景。",2,"2026-08-12 02:30:14","CREATED_QUERY"]