基于奎享写字机(KenjoyDraw)软件逆向工程的 Python3 实现:把文字、图片、SVG/DXF 转成笔式绘图机(pen plotter)的 G-code,并驱动机器绘制。含 Web UI、MCP agent 接口 与摄像头闭环校准。
本次重构:修复了核心管线/设备层/图像处理的一批 bug,Flask → FastAPI,新增视觉与 MCP 支持,全部功能在无硬件时可用内置 mock 控制器运行与测试。
.tfont 是本项目自研的轨迹(笔画)字体格式。与 .gfont(TEA 加密元数据 +
一个 ZIP 打包的逐字形 blob)不同,.tfont 是单个明文二进制,带一个按码点排序
的索引,可随机访问、按需只加载单个字形(不必解压/扫描整包)。每个字形就是若干
条笔画折线(正是笔式绘图机要画的东西)——没有轮廓要填充、没有加密要解。
设计目标:省空间、加载快、坐标精确、支持 >U+FFFF 码点(gfont 的 2 字节码点做不到)。 实测把 74 个 gfont 转成 tfont:208 MB → 106 MB(约一半),字形坐标往返无损。
字节布局(整数均为大端):
magic 6 bytes b"TFONT\x01" (5 字符 + 版本字节)
units_per_em uint16 坐标空间(如 1000)
flags uint8 bit0: y_down (1=y 向下, 绘图机坐标)
name_len uint16
name name_len 字节 (utf-8 字体名)
glyph_count uint32
index glyph_count × (uint32 码点, uint32 偏移, uint32 长度) # 按码点排序, 偏移为文件绝对偏移
glyph 数据块(位于各自偏移处):
advance int16 # 字宽(font units)
stroke_count uint16
每条笔画: point_count uint16, 然后 point_count × (int16 x, int16 y)
坐标为整数 font units,紧凑且精确;索引让读取器可直接 seek 到目标字形。
from cncdraw.tfont import gfont_to_tfont, tfont_to_gfont, ttf_to_tfont
# gfont <-> tfont(坐标往返无损)
gfont_to_tfont("in.gfont", "out.tfont")
tfont_to_gfont("out.tfont", "back.gfont")
# TTF/OTF -> tfont,两种轨迹策略(都是真轨迹,不是栅格填充):
# outline : 轮廓自适应贝塞尔展平——直线段2点、曲线按需加点,精确且省点
# centerline : 骨架中轴单线(写字感),非描双边("画画")
ttf_to_tfont("font.ttf", "ascii.tfont", chars="ABCabc012", mode="outline")
ttf_to_tfont("font.ttf", "mono.tfont", chars="好书写字", mode="centerline")
# 批量把一个目录里的 gfont 全部转成 tfont
from cncdraw.tfont.convert import convert_gfont_dir
convert_gfont_dir("cncdraw/fonts", "cncdraw/fonts_tfont").tfont 是排版引擎的一等字体源——直接把 .tfont 路径填进 PageInfo.font_paths
即可(TextLayout 会按扩展名自动选择读取器)。
cncdraw/fonts/ 下为 .gfont 字体,cncdraw/fonts_tfont/ 为对应的
.tfont 转换结果。也可用上面的 ttf_to_tfont 从任意 TTF 生成轨迹字体。
python3 -m venv .venv
. .venv/bin/activate
pip install -e . # 核心 + Web UI + 视觉
pip install -e ".[mcp]" # 需要 MCP agent 接口时依赖:numpy, Pillow, matplotlib, pyserial, fonttools, ezdxf,
opencv-python-headless, fastapi, uvicorn(mcp 为可选)。
python run_server.py # 监听 0.0.0.0:5000
# 或 KENJOY_PORT=8080 python run_server.py浏览器打开 http://<本机IP>:5000/,可从局域网其它设备访问。
自带 OpenAPI 文档:/docs。
把写字机能力封装成一组 MCP 工具(provider 无关,任何支持 MCP 的模型都能驱动):
python -m cncdraw.mcp_server # stdio transport工具:list_fonts / layout_text / layout_note / import_svg|dxf|image / generate_gcode / save_preview / device_connect|status|command|draw|disconnect / vision_calibrate|verify。工具在服务端维护"当前文档",agent 用极小的调用即可完成
「排版→生成 G-code→驱动机器绘制→拍照核对」。
from cncdraw.service import KenjoyService
svc = KenjoyService()
r = svc.layout_text("你好世界", handwriting=True, align="center")
gcode = svc.generate_gcode(r["paths"], firmware="grbl")
svc.connect("mock:grbl") # 无硬件时用 mock 控制器
svc.send_gcode(gcode) # 带 ok 握手的流控发送所有设备操作都支持 mock 端口(如 mock:grbl / mock:ebb / mock:marlin),
模拟控制器的 ok 握手、状态回报与掉线,用于开发、测试与 Web/MCP 演示。
- 字体:解析
.gfont(TEA 元数据 + ZIP 字形),TTF→GFont,字体预览/合并/子集化。 - 排版:自动换行、对齐、中文标点 kerning、手写扰动、笔记/多页/网格/表格模式。
- 图像→线稿:边缘检测、单线字(骨架化)、实心填充(扫描线)、抖动点阵、波浪图 (numpy 向量化,OpenCV 轮廓/骨架追踪)。
- 导入:SVG(path/几何图形/viewBox 缩放/贝塞尔展平) 与 DXF(基于 ezdxf)。
- G-code:Grbl / EBB / Marlin,最近邻路径优化,页面中心旋转。
- 设备:串口线程化读写,
ok流控发送 + 进度/取消,掉线通知,四种固件。 - 视觉闭环:USB 摄像头(或图片文件)采集,纸张矩形/ArUco 标记检测 → 相机像素↔机器 毫米的单应矩阵;绘制后拍照与规划路径对比核对(IoU/覆盖率 + 叠加图)。
- Web UI:FastAPI + 原生前端,实时预览、设备面板、WebSocket 绘制进度。
cncdraw/
├── service.py # 高层门面:Web 与 MCP 共用的唯一业务逻辑
├── mcp_server.py # MCP 工具层(agent 接口)
├── gfont/ # .gfont 读写 / TEA / 生命周期
├── layout/ # 文字/高级排版、SVG/DXF 导入、路径数据
├── gcode/generator.py # G-code 生成 + 路径优化
├── image/ # 图像处理(边缘/骨架/填充/抖动/波浪)
├── device/ # 串口(+mock) 与四种固件控制器
├── vision/ # 摄像头采集 + 标定/核对
├── preview/renderer.py # matplotlib 预览
└── web/ # FastAPI 后端 + static/ 前端
.venv/bin/python -m pytest tests/ -q # 全套
.venv/bin/python -m pytest tests/test_pipeline.py -q # 重构后的端到端用例
.venv/bin/python scripts/smoke_pipeline.py # 冒烟:字体→排版→gcode→预览.gfont 文件格式:
int version (5)
int body_length
byte[body_length] TEA 加密元数据 (key=[1,9,8,9,0,8,2,6,1,9,9,2,0,8,2,8])
ZIP archive 全部字形(entry 名为十进制 Unicode 码点)
字形:char(2B) + int(坐标数) + float[坐标数] BE + byte[点数] + int(优先级)
| 固件 | 抬笔 | 落笔 | 移动 |
|---|---|---|---|
| Grbl | M5 | M3S<power> | G0/G1 X.. Y.. F.. |
| EBB | SP,1,<delay> | SP,0,<delay> | XM,<ms>,<stepsX>,<stepsY> |
| Marlin | M280 P0 S<up> | M280 P0 S<down> | G0/G1 X.. Y.. F.. |
tools/ 为逆向工程用的原始 Windows 软件产物(CFR 反编译器、TEA 密钥来源等),非运行时依赖。