Skip to main content
Glama
jiangmuran
by jiangmuran

CNCDraw — 写字机 Python 工具集

基于奎享写字机(KenjoyDraw)软件逆向工程的 Python3 实现:把文字、图片、SVG/DXF 转成笔式绘图机(pen plotter)的 G-code,并驱动机器绘制。含 Web UI、MCP agent 接口 与摄像头闭环校准。

本次重构:修复了核心管线/设备层/图像处理的一批 bug,Flask → FastAPI,新增视觉与 MCP 支持,全部功能在无硬件时可用内置 mock 控制器运行与测试。

.tfont 轨迹字体格式

.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 会按扩展名自动选择读取器)。

Related MCP server: inkscape-mcp

字体说明

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 为可选)。

快速开始

Web UI(FastAPI)

python run_server.py                 # 监听 0.0.0.0:5000
# 或   KENJOY_PORT=8080 python run_server.py

浏览器打开 http://<本机IP>:5000/,可从局域网其它设备访问。 自带 OpenAPI 文档:/docs

MCP agent 接口

把写字机能力封装成一组 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 端口(如 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 密钥来源等),非运行时依赖。

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jiangmuran/CNCDraw'

If you have feedback or need assistance with the MCP directory API, please join our Discord server