新增 XSLPrintDot 项目,包含打印服务的核心功能和相关配置。实现打印机查询、打印任务处理、远程转发功能,并支持多平台设备ID获取。优化打印数据准备逻辑,增强系统的可维护性和扩展性,同时更新工作区配置以支持新项目。

This commit is contained in:
geht
2026-05-14 12:04:18 +08:00
parent 296bc2a4b2
commit 687b9bebed
65 changed files with 9080 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@@ -0,0 +1,257 @@
# XSL-PrintDot Client Usage Guide
## 1. Introduction
**XSL-PrintDot Client** is a local printing middleware developed based on Wails (Go + Vue 3). It acts as a bridge between browsers (or other clients) and the operating system's printers, receiving print instructions via the WebSocket protocol and invoking the system printer for printing.
Key Features:
- Automatically retrieves the list of installed printers in the operating system.
- Starts a WebSocket service to listen for print requests (default port 1122).
- Supports custom service ports and security keys (Secret Key).
- **Print Content Requirement**: Accepts Base64 encoded PDF content and invokes system printing commands.
- Supports advanced print parameters: copies, interval between copies, and more print settings.
- Provides a visual management interface to view logs and printer status in real-time.
- **Real-time Log Monitoring**: View system logs in real time.
---
## 2. Startup and Configuration
### 2.1 Starting the Application
You can run the compiled executable file (e.g., `XSL-PrintDot.exe`) directly.
**Note**: The program automatically starts the WebSocket service (default port 1122) upon startup.
**Windows printing note**:
- Ensure the Windows printing backend is available and accessible by the app.
### 2.2 Interface Configuration
After startup, the interface provides the following configuration options:
- **Port**: WebSocket service listening port (default `1122`).
- **Secret Key**: Security key (optional). If a key is set, clients must authenticate when connecting or sending requests.
- **Start/Stop Server**: Click the button to start or stop the WebSocket service.
- **Connection URL**: After the service starts, the interface displays the full connection address (e.g., `ws://localhost:1122/ws?key=...`).
**Operation Instructions**:
- **Exit Program**: Use the menu bar `Menu` -> `Quit` (Ctrl+Q) or the tray menu `Quit` to completely exit the program.
- **Run in Background**: Clicking the main window close button (X) will not exit the program but minimize it to the system tray. An icon appears in the system tray area, which can be used to quickly recall the window or exit.
- **Tray Menu**: Right-click the system tray icon to select `Show Main Window` or `Quit`.
- **Open Settings**: Use the menu bar `Menu` -> `Settings` (Ctrl+I) to open the settings window.
- **View Logs**: Use the menu bar `Menu` -> `System Logs` (Ctrl+L) to view logs in real time.
---
## 3. WebSocket Interface Specification
### 3.1 Connection Information
- **Protocol**: WebSocket (`ws://`)
- **Address**: `ws://localhost:<PORT>/ws`
- **Authentication**:
- If a `Secret Key` is set, it is recommended to carry it in the connection URL: `ws://localhost:1122/ws?key=YOUR_PASSWORD`
- If the key is not carried during connection, it can also be included in the message body sent (but not recommended, as the connection might be rejected).
### 3.2 Message Types
#### 3.2.1 Connection Success Response (Server -> Client)
After the connection is established, the server immediately sends the current printer list:
```json
{
"type": "printer_list",
"data": [
{"name": "Microsoft Print to PDF", "isDefault": true},
{"name": "ZDesigner GK888t", "isDefault": false}
]
}
```
#### 3.2.2 Get Printer List (Client -> Server)
The client can actively request the latest printer list at any time by sending the following JSON message:
```json
{
"type": "get_printers"
}
```
The server will reply with a `printer_list` message in the same format as **3.2.1**.
#### 3.2.3 Get Printer Capabilities (Client -> Server)
Request capabilities for a specific printer:
```json
{
"type": "get_printer_caps",
"printer": "Microsoft Print to PDF"
}
```
Example response:
```json
{
"type": "printer_caps",
"printer": "Microsoft Print to PDF",
"data": {
"paperSizes": ["A4", "Letter"],
"printerPaperNames": ["A4", "Letter"],
"duplexSupported": false,
"colorSupported": true
}
}
```
> Note: fields vary by platform. Windows returns Win32_Printer/Win32_PrinterConfiguration info; Linux/macOS returns parsed lpoptions data.
#### 3.2.4 Send Print Job (Client -> Server)
The JSON payload is grouped by feature:
```json
{
"printer": "Microsoft Print to PDF", // [Required] Target printer name
"content": "data:application/pdf;base64,JVBERi...", // [Required] Base64 encoded PDF content (Supports Data URI prefix or raw Base64)
"key": "123456", // [Optional] Auth key (can be omitted if verified during connection)
"job": {
"name": "My Print Job 001", // [Optional] Job name (for logging only)
"copies": 2, // [Optional] Number of copies, default 1
"intervalMs": 1000 // [Optional] Delay between copies (ms)
},
"pages": {
"range": "1-3,5", // [Optional] Page range (N / N-M / N,M / reverse ranges)
"set": "odd" // [Optional] odd | even
},
"layout": {
"scale": "fit", // [Optional] noscale | shrink | fit
"orientation": "portrait" // [Optional] portrait | landscape
},
"color": {
"mode": "color" // [Optional] color | monochrome
},
"sides": {
"mode": "duplex" // [Optional] simplex | duplex | duplexshort | duplexlong
},
"paper": {
"size": "A4" // [Optional] A4 | letter | legal | tabloid | statement | A2 | A3 | A5 | A6
},
"tray": {
"bin": "2" // [Optional] Tray number or name, e.g. 2 / Manual
}
}
```
> **Note**:
> 1. The `content` field must be a **Base64 encoded string of a PDF file**.
> - Supports standard Data URI format: `data:application/pdf;base64,JVBERi...`
> - Also supports raw Base64 string: `JVBERi...`
> - The server automatically strips the `data:` prefix (if present) and validates that the decoded content starts with `%PDF`.
| Field | Type | Description |
| :--- | :--- | :--- |
| `printer` | String | Target printer name. |
| `content` | String | **Base64 encoded PDF content**. |
| `job.name` | String | Print job name. |
| `job.copies` | Integer | **Number of copies**. If `intervalMs` is 0, handled by the system command. |
| `job.intervalMs` | Integer | **Interval (ms)**. When > 0, the server prints one copy per run. |
| `pages.range` | String | **Page range**: `N` / `N-M` / `N,M` / reverse ranges. |
| `pages.set` | String | **Odd/Even**: `odd` / `even`. |
| `layout.scale` | String | **Scaling**: `noscale` / `shrink` / `fit`. |
| `layout.orientation` | String | **Orientation**: `portrait` / `landscape`. |
| `color.mode` | String | **Color mode**: `color` / `monochrome`. |
| `sides.mode` | String | **Duplex**: `simplex` / `duplex` / `duplexshort` / `duplexlong`. |
| `paper.size` | String | **Paper size**: e.g. `A4`, `letter`, `legal`. If omitted, we try to auto-detect common sizes from PDF MediaBox. |
| `tray.bin` | String | **Tray**: number or name. |
#### 3.2.3.1 Platform Support
**Windows**
- `pages.range` / `pages.set` / `layout.scale` / `layout.orientation` / `color.mode` / `sides.mode` / `paper.size` / `tray.bin` / `job.copies` are converted to Windows print options.
**Linux/macOS (lp/CUPS)**
- `pages.range` -> `-P`.
- `pages.set` -> `-o page-set=odd|even`.
- `layout.scale` -> `fit-to-page` / `scaling=100` (`shrink` uses default behavior).
- `layout.orientation` -> `-o orientation-requested=3|4` (may be ignored by drivers).
- `color.mode` -> `-o ColorModel=Gray` / `-o ColorModel=RGB` (may be ignored by drivers).
- `sides.mode` -> `-o sides=...`.
- `paper.size` -> `-o media=...`.
- `tray.bin` -> `-o InputSlot=...` (depends on driver support).
#### 3.2.4 Server Response (Server -> Client)
The server returns the result of each print:
**Success Response:**
```json
{
"status": "success",
"message": "Printed successfully"
}
```
**Failure Response:**
```json
{
"status": "error",
"message": "Content must be a PDF file"
}
```
**Windows queue tracking note:**
On Windows, the server waits for the print job to appear in the printer queue and complete before returning `success`.
If the job does not appear within 120 seconds, or does not complete within 5 minutes, it returns `error` with a timeout message.
### 3.3 Client Code Example
```javascript
const socket = new WebSocket('ws://localhost:1122/ws?key=123456');
socket.onopen = () => {
console.log('Connected');
};
socket.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'printer_list') {
console.log('Available printers:', msg.data);
const targetPrinter = msg.data.find(p => p.isDefault) || msg.data[0];
// Example: Actively refresh printer list
// socket.send(JSON.stringify({ type: 'get_printers' }));
// Fetch printer capabilities (paper/duplex/color, etc.)
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';
// Send print job (grouped by feature)
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('Response:', msg);
}
};
```

View File

@@ -0,0 +1,263 @@
# 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);
}
};
```