264 lines
9.5 KiB
Markdown
264 lines
9.5 KiB
Markdown
# XSL-PrintDot Client 使用指南与规范
|
||
|
||
## 1. 项目简介
|
||
|
||
**XSL-PrintDot Client** 是一个基于 Wails (Go + Vue 3) 开发的本地打印中间件。它充当浏览器(或其他客户端)与操作系统打印机之间的桥梁,通过 WebSocket 协议接收打印指令,并调用系统打印机进行打印。
|
||
|
||
主要功能:
|
||
- 自动获取操作系统已安装的打印机列表。
|
||
- 启动 WebSocket 服务监听打印请求(默认端口 1122)。
|
||
- 支持自定义服务端口和安全密钥(Secret Key)。
|
||
- **打印内容要求**:接收 Base64 编码的 PDF 文件内容,并调用系统打印命令进行打印。
|
||
- 支持高级打印参数:打印份数、份数间隔,以及更多打印设置。
|
||
- 提供可视化的管理界面,实时查看日志和打印机状态。
|
||
- **日志实时监控**:支持实时查看系统日志。
|
||
|
||
---
|
||
|
||
## 2. 启动与配置
|
||
|
||
### 2.1 启动应用
|
||
可以直接运行编译后的可执行文件(如 `XSL-PrintDot.exe`),或在开发环境中使用:
|
||
|
||
```bash
|
||
wails dev
|
||
```
|
||
|
||
**注意**: 程序启动时会自动开启 WebSocket 服务(默认端口 1122)。
|
||
|
||
**Windows 打印说明**:
|
||
- 请确保 Windows 打印后端可用且程序有权限访问。
|
||
|
||
### 2.2 界面配置
|
||
启动后,界面提供以下配置项:
|
||
- **Port**: WebSocket 服务监听端口(默认 `1122`)。
|
||
- **Secret Key**: 安全密钥(可选)。如果设置了密钥,客户端在连接或发送请求时必须通过鉴权。
|
||
- **Start/Stop Server**: 点击按钮即可启动或停止 WebSocket 服务。
|
||
- **Connection URL**: 服务启动后,界面会显示完整的连接地址(如 `ws://localhost:1122/ws?key=...`)。
|
||
|
||
**操作说明**:
|
||
- **退出程序**: 使用菜单栏 `Menu` -> `Quit` (Ctrl+Q),或使用托盘菜单的 `Quit` 可完全退出程序。
|
||
- **后台运行**: 点击主窗口关闭按钮 (X) 不会退出程序,而是将程序最小化到系统托盘区。程序启动后会在系统托盘区显示图标,可用于快速唤起窗口或退出。
|
||
- **托盘菜单**: 在系统托盘图标上右键单击,可选择 `Show Main Window` 显示主窗口或 `Quit` 退出程序。
|
||
- **打开设置**: 使用菜单栏 `Menu` -> `Settings` (Ctrl+I) 可打开设置窗口。
|
||
- **查看日志**: 使用菜单栏 `Menu` -> `System Logs` (Ctrl+L) 可查看实时日志.
|
||
|
||
---
|
||
|
||
## 3. WebSocket 接口规范
|
||
|
||
### 3.1 连接信息
|
||
- **协议**: WebSocket (`ws://`)
|
||
- **地址**: `ws://localhost:<PORT>/ws`
|
||
- **鉴权**:
|
||
- 如果设置了 `Secret Key`,建议在连接 URL 中携带:`ws://localhost:1122/ws?key=YOUR_PASSWORD`
|
||
- 如果连接时未携带 key,也可以在发送的消息体中包含 `key` 字段(但不推荐,连接可能被拒绝)。
|
||
|
||
### 3.2 消息类型
|
||
|
||
#### 3.2.1 连接成功响应 (Server -> Client)
|
||
连接建立后,服务端会立即发送当前的打印机列表:
|
||
```json
|
||
{
|
||
"type": "printer_list",
|
||
"data": [
|
||
{"name": "Microsoft Print to PDF", "isDefault": true},
|
||
{"name": "ZDesigner GK888t", "isDefault": false}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 3.2.2 获取打印机列表 (Client -> Server)
|
||
客户端可以随时发送以下 JSON 消息主动获取最新的打印机列表:
|
||
```json
|
||
{
|
||
"type": "get_printers"
|
||
}
|
||
```
|
||
服务端将回复与 **3.2.1** 相同格式的 `printer_list` 消息。
|
||
|
||
#### 3.2.3 获取打印机能力 (Client -> Server)
|
||
客户端可以请求指定打印机的可用参数:
|
||
```json
|
||
{
|
||
"type": "get_printer_caps",
|
||
"printer": "Microsoft Print to PDF"
|
||
}
|
||
```
|
||
服务端响应示例:
|
||
```json
|
||
{
|
||
"type": "printer_caps",
|
||
"printer": "Microsoft Print to PDF",
|
||
"data": {
|
||
"paperSizes": ["A4", "Letter"],
|
||
"printerPaperNames": ["A4", "Letter"],
|
||
"duplexSupported": false,
|
||
"colorSupported": true
|
||
}
|
||
}
|
||
```
|
||
> 说明:不同系统返回字段会有差异,Windows 返回 Win32_Printer/Win32_PrinterConfiguration 信息;Linux/macOS 返回 lpoptions 解析结果。
|
||
|
||
#### 3.2.4 发送打印任务 (Client -> Server)
|
||
客户端发送的 JSON 数据包结构如下(按功能分类):
|
||
|
||
```json
|
||
{
|
||
"printer": "Microsoft Print to PDF", // [必填] 目标打印机名称
|
||
"content": "data:application/pdf;base64,JVBERi...", // [必填] Base64 编码的 PDF 内容 (支持带前缀或纯 Base64)
|
||
"key": "123456", // [选填] 鉴权密钥 (若连接时已验证可省略)
|
||
"job": {
|
||
"name": "My Print Job 001", // [选填] 任务名称 (仅用于日志记录)
|
||
"copies": 2, // [选填] 打印份数,默认 1
|
||
"intervalMs": 1000 // [选填] 份数间延迟(毫秒),用于手动隔张打印
|
||
},
|
||
"pages": {
|
||
"range": "1-3,5", // [选填] 页码范围 (支持 N / N-M / N,M / 反向区间)
|
||
"set": "odd" // [选填] odd | even
|
||
},
|
||
"layout": {
|
||
"scale": "fit", // [选填] noscale | shrink | fit
|
||
"orientation": "portrait" // [选填] portrait | landscape
|
||
},
|
||
"color": {
|
||
"mode": "color" // [选填] color | monochrome
|
||
},
|
||
"sides": {
|
||
"mode": "duplex" // [选填] simplex | duplex | duplexshort | duplexlong
|
||
},
|
||
"paper": {
|
||
"size": "A4" // [选填] A4 | letter | legal | tabloid | statement | A2 | A3 | A5 | A6
|
||
},
|
||
"tray": {
|
||
"bin": "2" // [选填] 纸盒编号或名称,例如 2 / Manual
|
||
}
|
||
}
|
||
```
|
||
|
||
> **注意**:
|
||
> 1. `content` 字段必须是 **PDF 文件的 Base64 编码字符串**。
|
||
> - 支持标准 Data URI 格式:`data:application/pdf;base64,JVBERi...`
|
||
> - 也支持纯 Base64 字符串:`JVBERi...`
|
||
> - 服务端会自动去除 `data:` 前缀(如果有)并校验解码后的内容是否以 `%PDF` 开头。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| :--- | :--- | :--- |
|
||
| `printer` | String | 目标打印机名称。 |
|
||
| `content` | String | **Base64 编码的 PDF 内容**。 |
|
||
| `job.name` | String | 打印任务名称。 |
|
||
| `job.copies` | Integer | **打印份数**。`intervalMs` 为 0 时由系统命令一次性处理。 |
|
||
| `job.intervalMs` | Integer | **隔张间隔 (ms)**。大于 0 时服务端逐份打印。 |
|
||
| `pages.range` | String | **页码范围**:`N` / `N-M` / `N,M` / 反向区间。 |
|
||
| `pages.set` | String | **奇偶页**:`odd` / `even`。 |
|
||
| `layout.scale` | String | **缩放**:`noscale` / `shrink` / `fit`。 |
|
||
| `layout.orientation` | String | **方向**:`portrait` / `landscape`。 |
|
||
| `color.mode` | String | **颜色模式**:`color` / `monochrome`。 |
|
||
| `sides.mode` | String | **单双面**:`simplex` / `duplex` / `duplexshort` / `duplexlong`。 |
|
||
| `paper.size` | String | **纸张**:如 `A4`、`letter`、`legal`。未提供时会尝试从 PDF 的 MediaBox 自动识别常见尺寸。 |
|
||
| `tray.bin` | String | **纸盒**:编号或名称。 |
|
||
|
||
#### 3.2.3.1 平台支持说明
|
||
**Windows**
|
||
- `pages.range` / `pages.set` / `layout.scale` / `layout.orientation` / `color.mode` / `sides.mode` / `paper.size` / `tray.bin` / `job.copies` 会被转换为 Windows 打印选项。
|
||
|
||
**Linux/macOS (lp/CUPS)**
|
||
- `pages.range` -> `-P`。
|
||
- `pages.set` -> `-o page-set=odd|even`。
|
||
- `layout.scale` -> `fit-to-page` / `scaling=100`(`shrink` 使用默认行为)。
|
||
- `layout.orientation` -> `-o orientation-requested=3|4`(驱动可能忽略)。
|
||
- `color.mode` -> `-o ColorModel=Gray` / `-o ColorModel=RGB`(部分驱动可能忽略)。
|
||
- `sides.mode` -> `-o sides=...`。
|
||
- `paper.size` -> `-o media=...`。
|
||
- `tray.bin` -> `-o InputSlot=...`(依赖驱动支持)。
|
||
|
||
#### 3.2.4 服务端响应 (Server -> Client)
|
||
服务端会返回每次打印的结果:
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"status": "success",
|
||
"message": "Printed successfully"
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"status": "error",
|
||
"message": "Content must be a PDF file"
|
||
}
|
||
```
|
||
|
||
**Windows 队列跟踪说明:**
|
||
Windows 下服务端会等待打印任务进入打印队列并完成后才返回 `success`。
|
||
若 120 秒内未入队或 5 分钟内未完成,将返回 `error` 并给出超时提示。
|
||
|
||
---
|
||
|
||
## 4. 调用示例 (JavaScript)
|
||
|
||
```javascript
|
||
const socket = new WebSocket('ws://localhost:1122/ws?key=123456');
|
||
|
||
socket.onopen = () => {
|
||
console.log('已连接');
|
||
};
|
||
|
||
socket.onmessage = (event) => {
|
||
const msg = JSON.parse(event.data);
|
||
if (msg.type === 'printer_list') {
|
||
console.log('可用打印机:', msg.data);
|
||
|
||
const targetPrinter = msg.data.find(p => p.isDefault) || msg.data[0];
|
||
|
||
// 示例:主动刷新打印机列表
|
||
// socket.send(JSON.stringify({ type: 'get_printers' }));
|
||
|
||
// 获取打印机能力(纸张/单双面/彩色等)
|
||
socket.send(JSON.stringify({
|
||
type: 'get_printer_caps',
|
||
printer: targetPrinter?.name
|
||
}));
|
||
} else if (msg.type === 'printer_caps') {
|
||
const caps = msg.data || {};
|
||
const sizes = caps.printerPaperNames || caps.paperSizes || [];
|
||
const paperSize = sizes[0] || 'A4';
|
||
|
||
// 发送打印任务(按功能分组)
|
||
socket.send(JSON.stringify({
|
||
printer: msg.printer,
|
||
content: "JVBERi0xLjQKJ...", // Base64 PDF Data
|
||
job: {
|
||
name: "Test Job",
|
||
copies: 2,
|
||
intervalMs: 0
|
||
},
|
||
pages: {
|
||
range: "1-3,5",
|
||
set: "odd"
|
||
},
|
||
layout: {
|
||
scale: "fit",
|
||
orientation: "portrait"
|
||
},
|
||
color: {
|
||
mode: "color"
|
||
},
|
||
sides: {
|
||
mode: "duplex"
|
||
},
|
||
paper: {
|
||
size: paperSize
|
||
},
|
||
tray: {
|
||
bin: "2"
|
||
}
|
||
}));
|
||
} else {
|
||
console.log('收到回复:', msg);
|
||
}
|
||
};
|
||
```
|