p1-printer-sdk
v1.0.1
Published
Node.js and TypeScript SDK with template rendering for Paperang P1 thermal printers.
Maintainers
Readme
Paperang P1 Printer SDK
这是一个面向 Paperang P1(喵喵机 P1)的 Node.js 22 + TypeScript SDK,同时提供本地可视化模板编辑器。
你可以在浏览器编辑器中设计 384 点宽的打印模板,把模板 JSON 保存在自己的项目里,再通过 模板 ID + 变量数据 从 Node.js 后端、Nuxt 服务端接口、Electron 主进程或自动化脚本中打印。
本项目已在 Windows 上通过蓝牙 SPP 虚拟 COM 端口和真实 Paperang P1 测试。
功能
- 384 点宽可视化模板编辑器和 1:1 黑白打印预览。
- 模板 JSON 保存在当前项目中,并可通过下拉列表切换。
- 支持固定文字、变量文字、固定图片和变量图片。
- 支持字号、字重、对齐方式和行距。
- 支持图片亮度、对比度、裁切方式、阈值、Floyd-Steinberg 和 Bayer 抖动。
- 支持自动纸张高度和手动纸张高度。
- 根据模板生成 TypeScript 类型,自动提示模板 ID 和变量名。
- 支持 Windows COM 端口配置、连接状态和详细打印日志。
- 内置 P1 协议分包、CRC32、位图转换、打印浓度和走纸处理。
使用条件
- Node.js 22 或更高版本。
- 推荐使用 Windows。
- 一台已经与电脑完成蓝牙配对的 Paperang P1。
- Windows 为打印机创建的蓝牙 SPP 传出 COM 端口。
SDK 需要在 Node.js 环境中访问串口和处理图片,不能直接运行在纯浏览器代码中。Nuxt 应放在服务端接口中,Electron 应放在主进程中。
新手教程
第 1 步:配对打印机并找到 COM 口
- 打开 Paperang P1 电源。
- 在 Windows 蓝牙设置中完成配对。
- 打开高级蓝牙设置,找到属于打印机的传出 COM 端口。
- 记住端口号,例如
COM13。
如果 Windows 显示多个蓝牙端口,请选择与打印机对应的“传出”串行端口(SPP),不要选择传入端口。
第 2 步:安装 SDK
进入你的 Node.js、Nuxt 或 Electron 项目目录,运行:
npm install p1-printer-sdknpm 可能会隐藏依赖包的 postinstall 提示。即使安装后没有显示提示,也可以直接继续初始化。
第 3 步:初始化项目
npx p1-printer init初始化程序会依次询问:
Template directory [./printer-templates]:
Printer COM port [COM13]:
Add an npm run printer script? [y]:直接按 Enter 会采用方括号中的默认值,也可以输入自己的模板目录和 COM 口。初始化完成后会在当前项目中创建:
你的项目/
├── p1-printer.config.json
├── printer-templates/
│ └── hello-world.json
└── package.json模板不会保存到 node_modules,也不会保存到操作系统的隐藏目录中。
如果要跳过提问并直接使用 ./printer-templates 和 COM13:
npx p1-printer init --yes第 4 步:打开可视化编辑器
如果初始化时选择了添加 npm 脚本,运行:
npm run printer否则运行:
npx p1-printer editor命令会启动本地服务器、自动选择一个可用端口并打开浏览器。服务器只监听 127.0.0.1,不会暴露给局域网中的其他电脑。
在编辑器中按下面的顺序操作:
- 从下拉列表选择已有模板,或点击 New 新建模板。
- 添加文字或图片元素。
- 在左侧设计画布中拖动元素。
- 在属性面板中调整内容、位置和样式。
- 在右侧 1:1 预览中检查最终黑白效果。
- 点击 Save,模板会写入配置的模板目录。
- 确认右上角 COM 口,点击 Test Print 测试打印。
Import 用来导入外部模板 JSON,Export 用来下载当前模板 JSON,Delete 用来删除当前模板目录中的选中模板。
第 5 步:理解固定元素和变量元素
每个文字或图片元素都有一种角色:
static:内容保存在模板中,每次打印都不会变化。variable:打印时由你的代码通过变量名传入真实内容。
例如,一个变量文字元素可以设置为:
Binding: customerName
Placeholder: 预览姓名Binding 是代码传值时使用的变量名。Placeholder 只用于编辑器预览,正式打印时必须传入 customerName;缺少变量会直接报错,不会偷偷打印占位内容。
变量图片支持本地文件路径、HTTP/HTTPS 地址和 Base64 Data URL。
第 6 步:生成 TypeScript 自动提示
保存或修改模板后运行:
npx p1-printer generate-types命令会扫描模板目录,并默认生成 p1-printer.generated.ts。如果模板中包含 customerName 和 avatar,生成的类型类似:
export type P1TemplateRegistry = {
"customer-label": {
customerName: string | number | boolean | null
avatar: string
}
}这样 TypeScript 就能在运行前发现不存在的模板 ID、拼错的变量名和缺少的变量值。
第 7 步:从 Node.js 或 TypeScript 打印
import { createP1PrinterSDK, loadP1Config } from "p1-printer-sdk"
import type { P1TemplateRegistry } from "./p1-printer.generated.js"
const config = await loadP1Config()
const sdk = createP1PrinterSDK<P1TemplateRegistry>({
printer: {
port: config.printer.port
},
templates: {
dir: config.templatesPath
}
})
await sdk.printTemplate("hello-world", {
text: "你好,喵喵机 P1"
})请从包含 p1-printer.config.json 的项目中启动程序。配置加载器会从当前目录开始向上查找配置文件。
配置文件
npx p1-printer init 会创建 p1-printer.config.json:
{
"templatesDir": "./printer-templates",
"printer": {
"port": "COM13"
},
"generatedTypes": "./p1-printer.generated.ts"
}| 字段 | 作用 |
| ---------------- | ---------------------------------------------------------------- |
| templatesDir | 模板 JSON 所在目录,可以使用项目相对路径或绝对路径。 |
| printer.port | Windows COM 口或其他受支持的串口/蓝牙目标。 |
| generatedTypes | 生成的 TypeScript 类型文件路径,可以使用项目相对路径或绝对路径。 |
编辑器启动时会读取这份配置。你也可以在编辑器中临时修改 COM 口。
模板规则
- 文件名就是模板 ID:
customer-label.json对应customer-label。 - JSON 内的
id必须和去掉.json后的文件名一致。 - ID 可以包含字母、数字、下划线和连字符。
- Paperang P1 画布宽度固定为
384点。 - 变量元素必须设置唯一、容易理解的
binding。 - 无效的 JSON 会在模板列表中显示为无效项,不会导致整个编辑器崩溃。
模板元素示例:
{
"id": "el_customer_name",
"name": "Customer Name",
"type": "text",
"role": "variable",
"binding": "customerName",
"placeholder": "预览姓名",
"x": 24,
"y": 24,
"width": 336,
"height": 52,
"style": {
"fontSize": 32,
"fontWeight": 700,
"align": "center",
"lineSpacing": 1.15
}
}CLI 命令
npx p1-printer init
npx p1-printer editor
npx p1-printer templates list
npx p1-printer generate-types常用选项:
init --yes 直接采用初始化默认值
init --templates <path> 指定模板目录
init --port <COM13|13> 指定打印机端口
init --no-script 不添加 npm run printer
editor --templates <path> 临时覆盖模板目录
editor --port <COM13|13> 临时覆盖打印机端口
editor --port-number <number> 指定本地网页服务器端口
editor --no-open 启动后不自动打开浏览器
generate-types --out <path> 临时覆盖类型文件输出位置Nuxt 服务端示例
打印代码应放在 Nuxt 服务端接口中,不要放在浏览器中的 Vue 组件里:
// server/api/print.post.ts
import { createP1PrinterSDK, loadP1Config } from "p1-printer-sdk"
import type { P1TemplateRegistry } from "../../p1-printer.generated.js"
export default defineEventHandler(async (event) => {
const body = await readBody<{ text: string }>(event)
const config = await loadP1Config()
const sdk = createP1PrinterSDK<P1TemplateRegistry>({
printer: { port: config.printer.port },
templates: { dir: config.templatesPath }
})
await sdk.printTemplate("hello-world", {
text: body.text
})
return { ok: true }
})Nuxt 服务端必须运行在连接打印机的 Windows 电脑上。部署在远程服务器上的程序无法访问你电脑上的 COM 口。
主要 SDK API
createP1PrinterSDK(options)
创建高级 SDK 实例。只需要配置一次打印机目标和模板目录。
sdk.printTemplate(templateId, data, options?)
渲染并打印指定模板。可选参数可以临时覆盖 density 和 feedLines。
sdk.renderTemplatePreview(templateId, data)
只渲染黑白 PNG Buffer,不会连接打印机。
sdk.listTemplates() / sdk.getTemplate(id)
列出或读取配置目录中的模板。
sdk.saveTemplate(template)
验证并保存模板 JSON。
P1Printer
当你不需要模板 SDK 时,可以使用这个类进行更底层的连接和图片打印。
常见问题
找不到 p1-printer.config.json
在你的项目目录中运行:
npx p1-printer init无法打开 COM 口
- 确认打印机已经开机并完成配对。
- 在 Windows 蓝牙设置中再次确认传出 COM 口。
- 修改
p1-printer.config.json中的printer.port。 - 关闭可能占用同一个 COM 口的其他程序。
预览正常,但打印时提示缺少变量
预览可以显示 Placeholder,正式打印要求 data 包含所有变量元素的 Binding。修改 Binding 后请重新生成类型。
图片打印效果不好
threshold适合文字、二维码和边缘清晰的图形。floyd-steinberg适合照片。bayer适合规则网点效果。- 可以继续调整图片元素的亮度、对比度和阈值。
安装 bluetooth-serial-port 时出现警告
它是可选依赖。Windows 推荐使用下面的连接方式:
Windows 蓝牙配对 -> 传出虚拟 COM 口 -> serialport协议参考
协议实现参考公开 Python 项目 ihciah/miaomiaoji-tool。
- 经典蓝牙 SPP / RFCOMM。
- 固定宽度 384 点,每行 48 字节。
- 黑点为
1,白点为0。 - 使用 CRC32 协议分包。
- 单个数据包最大 payload 为 2016 字节。
