# 晓雪WEB打印控件 **Repository Path**: iteachyou/xiaoxue-web-printer ## Basic Information - **Project Name**: 晓雪WEB打印控件 - **Description**: 基于 SWT 的本机打印控件。启动后无主界面,驻留系统托盘,通过 WebSocket 接收本机页面的打印 / 预览请求,用 OpenHTMLtoPDF 渲染为 PDF 后交给系统打印机。 - **Primary Language**: Java - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-08-26 - **Last Updated**: 2026-10-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 晓雪 WEB 打印控件 **浏览器里的本地打印,一行 SDK 全搞定** [![Version](https://img.shields.io/badge/version-1.0.5-blue)](RELEASE-NOTES.md) [![Java](https://img.shields.io/badge/Java-17%2B-orange)](https://adoptium.net/) [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)](#跨平台) [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE) 📚 **在线文档:[https://iteachyou-wjn.github.io/xiaoxue-web-printer](https://iteachyou-wjn.github.io/xiaoxue-web-printer)** ## 简介 一个驻留在**系统托盘**的本机打印服务。启动后没有主窗口,只在后台静默运行: - 浏览器页面通过 **WebSocket**(仅 `127.0.0.1:54321`)发送打印请求 - 服务端将 `text` / `html` 渲染为 PDF(OpenHTMLtoPDF) - 再交给 **Java Print Service** 输出到真实打印机 同时内置 **BarTender 标签模板**打印能力(模板参数获取、模板图像预览、带参数渲染),适合条码 / 标签 / 出入库单等场景。 ## 核心特性 | 特性 | 说明 | |------|------| | 🖨️ **静默打印** | `doPrint` 一次调用:创建任务 → 渲染 PDF → 送打印机,全程无弹窗 | | 👀 **打印预览** | `doPreview` 打开多页预览窗:缩放、翻页、页面设置、导出 PDF / 图片 | | 🏷️ **BarTender 集成** | 读取 `.btw` 模板参数、渲染模板预览图(base64)、带参数渲染;本机未装 BarTender 时自动降级不影响主流程 | | 📋 **任务管理** | 任务列表 / 状态查询 / 取消 / 清除,状态变化实时广播 `task_list_update` | | 🔒 **仅本机监听** | WebSocket 只绑定回环地址,不对局域网暴露 | | 🔐 **离线授权** | 机器码 + RSA 非对称签名授权码,绑定设备与有效期 | | 🧩 **多框架 SDK** | `xiaoxue-printer-sdk.js` 零依赖,自动重连,Promise 化 API;提供 Vue2 / Vue3 / React / Angular 演示 | | 📦 **一键打包** | 一条命令产出自带 JRE 的 Windows 安装包(ProGuard 混淆 + jpackage + Inno Setup) | ## 工作原理 ``` 浏览器页面 WebSocket Server 打印服务 xiaoxue-printer-sdk.js → 127.0.0.1:54321 → HtmlRenderService ↓ ↓ ┌──────┴──────┐ BartenderManager ↓ ↓ ↓ 任务管理 任务执行 PrintTaskExecutor → 系统打印机 ``` **打印链路**(`doPrint` 与预览窗"打印"按钮共用): 1. `HtmlRenderService` 将 text/html + style 编排为 XHTML(`@page` 定义纸张/边距/页眉页脚,图片内嵌) 2. OpenHTMLtoPDF 渲染为 PDF(Windows 自动注册宋体、微软雅黑等字体) 3. `PrintTaskExecutor` 通过 PDFBox `PDFPageable` + `PrinterJob` 输出到打印机 ## 快速开始 ### 环境要求 - JDK 17+ - Maven 3.6+ - Windows x86_64(默认 SWT 平台包;macOS / Linux 需替换依赖,见[跨平台](#跨平台)) ### 编译运行 ```bash # 克隆项目 git clone https://gitee.com/iteachyou/xiaoxue-web-printer.git cd xiaoxue-web-printer # 编译打包 mvn clean package # 启动服务 java -jar target/dreamer-print-service-1.0.4.jar ``` 或在 IDE 中直接运行 `cc.iteachyou.printservice.MainApp#main`。启动成功后系统托盘会出现打印控件图标。 ### 验证服务 ```bash # 端口监听检查 netstat -ano | findstr 54321 # 浏览器打开演示页(需先启动服务) start demo/index.html ``` 托盘交互: - **双击图标**:打开任务列表 - **右键菜单**:任务列表 / 授权许可 / 关于 / 退出 ## WebSocket 协议 服务地址:`ws://127.0.0.1:54321`,消息为 JSON 文本帧。连接成功后服务端先推送一条欢迎消息: ```json { "type": "welcome", "message": "已连接到打印控件 WebSocket 服务", "port": 54321 } ``` ### 指令一览 | type | 参数 | 响应 type | 说明 | |------|------|----------|------| | `doPrint` | `printer` / `style` / `content` | `doPrint_result` | 提交并执行打印 | | `doPreview` | 同 `doPrint` | `doPreview_result` | 打开预览窗,不打印 | | `printers` | — | `printers_result` | 本机打印机列表 | | `pageSize` | `printerName` | `pageSize_result` | 指定打印机支持的纸张类型 | | `list` | — | `list_result` | 打印任务列表 | | `status` | `taskId` | `status_result` | 单个任务状态 | | `cancel` | `taskId` | `cancel_result` | 取消待打印任务 | | `clear` | — | `clear_result` | 清除已完成 / 失败任务 | | `bartenderInstance` | — | `bartenderInstance_result` | BarTender 可用性与版本 | | `bartenderTemplateParams` | `templatePath` | `bartenderTemplateParams_result` | 获取模板参数定义 | | `bartenderTemplateImage` | `templatePath` / `dpi` | `bartenderTemplateImage_result` | 渲染模板图像(base64) | | `bartenderTemplateImageWithParams` | `templatePath` / `params` / `dpi` | `bartenderTemplateImageWithParams_result` | 带参数渲染模板图像(base64) | 任务状态变化时,服务端向所有已连接客户端广播: ```json { "type": "task_list_update", "tasks": [...] } ``` ### doPrint 载荷示例 ```json { "type": "doPrint", "printer": "默认打印机", "style": { "paper": "A4", "direction": "vertical", "margin": { "top": 1, "right": 1, "bottom": 1, "left": 1 }, "fontFamily": "宋体", "fontSize": 12, "zoom": 1, "paperHeader": "", "paperFooter": "" }, "content": { "type": "text", "value": "打印内容" } } ``` - `content.type`:`text`(纯文本)或 `html`(HTML 片段) - `direction`:`vertical`(纵向)/ `horizontal`(横向) 响应: ```json { "type": "doPrint_result", "success": true, "taskId": "a1b2c3d4", "message": "打印任务已提交" } ``` ## 浏览器 SDK `xiaoxue-printer-sdk.js`(另有压缩版 `.min.js`)零依赖,浏览器直接引入或 Node.js `require` 均可: ```html ``` ### SDK API | 方法 | 返回 | 说明 | |------|------|------| | `doPrint(json)` | `Promise` | 提交打印 | | `doPreview(json)` | `Promise` | 打开预览 | | `getPrinters()` | `Promise<{printers:[]}>` | 打印机列表 | | `getPageSize(name)` | `Promise<{sizes:[]}>` | 打印机支持纸张 | | `getTaskList()` | `Promise<{tasks:[]}>` | 任务列表 | | `getTaskStatus(id)` | `Promise` | 任务状态 | | `cancelTask(id)` / `clearTasks()` | `Promise` | 取消/清除任务 | | `getBartenderInstance()` | `Promise` | 获取 BarTender 实例 | 完整交互示例见 `demo/` 目录(含普通打印与 BarTender 打印):`index.html` 入口导航 + Vue2 / Vue3 / React / Angular 四套框架集成示例。 ## BarTender 标签打印 本机安装 BarTender 软件后,可调用标签模板能力(未安装时自动降级为不可用,不影响普通打印): ```javascript const bt = await sdk.getBartenderInstance(); if (bt.available) { console.log('BarTender 版本:', bt.version); // 1. 获取模板参数定义(参数名 + 控件类型) const { params } = await bt.getTemplateParams('D:/labels/sample.btw'); // params: [{ name: '条码', type: 'Barcode' }, { name: '品名', type: 'Text' }, ...] // 2. 带参数渲染模板预览图(base64,dpi 默认 300) const { image } = await bt.getTemplateImageWithParams('D:/labels/sample.btw', { 条码: '6901234567890', 品名: '灭火器' }); document.querySelector('img').src = 'data:image/png;base64,' + image; } ``` - `type` 为控件类型粒度(Barcode / Text / Picture / RichText / RFID 等,条码不细分 QRCode / Code128) - 模板图像同样可用于前端预览后再决定是否打印 ## 授权机制 采用**离线非对称签名授权**: - 授权码由授权工具(`dreamer-print-service-authorization` 项目)用**私钥**签发,绑定**机器码**与**有效期** - 客户端内置公钥验签:签名无效、机器码不匹配、过期均判定为未授权 - 授权码持久化于 `~/.dreamer-print/license.dat` - 托盘菜单「授权许可」可查看本机机器码、录入授权码、取消授权(含邮箱联系入口) > 私钥仅存于授权工具项目,客户端无法伪造授权码。 ## 打包发布 ### 一键打包(推荐) ```bat build\打包.bat ``` 脚本(`build/build.ps1`)会自动完成:版本号同步 → ProGuard 混淆 → 复制依赖 → jpackage 生成自带 JRE 的绿色版 → Inno Setup 产出安装包。 产物:`build\windows-晓雪WEB打印控件-<版本>-x86_64.exe` ### 手动打包 > ⚠️ **关键前提**:**不要用 shade 生成的 fat jar 打包**。SWT 的 native 库(dll)打进 fat jar 后会加载失败。 1. **构建**:`Run As → Maven build...`,Goals 填 `clean package`。`target` 下产出: - `dreamer-print-service-x.y.z.jar` — fat jar,仅用于 `java -jar` 运行 - `original-dreamer-print-service-x.y.z.jar` — 瘦 jar,jpackage 用这个 2. **复制依赖**: ```bash mvn dependency:copy-dependencies -DoutputDirectory=build/libs ``` 3. **jpackage**: ```bat jpackage --type app-image --name 晓雪WEB打印控件 --app-version x.y.z --vendor iteachyou \ --input build/input --main-jar dreamer-print-service-x.y.z.jar \ --main-class cc.iteachyou.printservice.MainApp \ --icon build/dreamer-print.ico --dest build/app-image ``` 4. **Inno Setup**:`ISCC.exe build/dreamer-print.iss` ### 打包后验证 1. 运行 exe,系统托盘出现图标 2. `netstat -ano | findstr 54321` 有监听 3. 浏览器打开 `demo/index.html`,`getPrinters()` 能返回打印机列表 ## 配置 WebSocket 监听地址在 `MainApp` 中定义: ```java public static final String WS_HOST = "127.0.0.1"; public static final int WS_PORT = 54321; ``` 默认只绑回环地址。若确需局域网访问,改为 `0.0.0.0`,并**自行增加鉴权**。 ### 跨平台 SWT 按平台区分 artifactId,替换 `pom.xml` 中的依赖即可: | 平台 | artifactId | |------|-----------| | Windows x86_64(默认) | `org.eclipse.swt.win32.win32.x86_64` | | macOS aarch64 | `org.eclipse.swt.cocoa.macosx.aarch64` | | Linux x86_64 | `org.eclipse.swt.gtk.linux.x86_64` | ## 项目结构 ``` dreamer-print-service/ ├── pom.xml # Maven 构建配置 ├── xiaoxue-printer-sdk.js # 浏览器 SDK 源码 ├── xiaoxue-printer-sdk.min.js # SDK 压缩混淆版 ├── demo/ # 演示页 │ ├── index.html # 入口导航 │ ├── normal-print-demo.html # 普通打印 + BarTender 示例 │ ├── print-demo-vue2.html # Vue2 集成示例 │ ├── print-demo-vue3.html # Vue3 集成示例 │ ├── print-demo-react.html # React 集成示例 │ └── print-demo-anjular.html # Angular 集成示例 ├── build/ # 打包脚本与产物 │ ├── build.ps1 / 打包.bat # 一键打包脚本 │ ├── dreamer-print.iss # Inno Setup 脚本 │ ├── dreamer-print.ico # 应用图标 │ └── proguard.pro # ProGuard 混淆规则 ├── RELEASE-NOTES.md # 版本历史 └── src/main/ ├── java/cc/iteachyou/printservice/ │ ├── MainApp.java # 启动入口 │ ├── tray/SystemTrayManager.java # 托盘管理 │ ├── websocket/ │ │ ├── PrintWebSocketServer.java# WebSocket 服务 │ │ └── PrintTask.java # 打印任务模型 │ ├── print/ │ │ ├── HtmlRenderService.java # HTML → PDF │ │ ├── PrintTaskExecutor.java # PDF → 打印机 │ │ ├── PaperSizeUtil.java # 纸张尺寸工具 │ │ └── bartender/ # BarTender 集成 │ ├── ui/ # UI 对话框 │ │ ├── AboutDialog.java # 关于对话框 │ │ ├── LicenseDialog.java # 授权对话框 │ │ ├── PreviewDialog.java # 打印预览窗口 │ │ └── TaskListDialog.java # 任务列表窗口 │ └── util/ # 工具类 │ ├── LicenseManager.java # 授权管理 │ ├── MachineCode.java # 机器码生成 │ └── VersionUtil.java # 版本工具 └── resources/ ├── simplelogger.properties # 日志配置 └── version.properties # 版本号 ``` ## 常见问题 ### 托盘图标不显示? SWT `display.getSystemTray()` 在 Windows / macOS 上支持;Linux 部分桌面环境需要安装托盘扩展。 ### 如何开机自启? - **Windows**:快捷方式放入「启动」文件夹,或安装包勾选「开机自启」 - **macOS**:登录项 - **Linux**:systemd 用户服务或 `.desktop` ### 运行 exe 无反应 / SWT 报平台错误? 典型症状 `Libraries for platform win32 cannot be loaded...`,原因是用 fat jar 打包导致 SWT 本地库损坏。解决见[打包发布](#打包发布)中的「瘦 jar + 原始依赖」流程。 ### 为什么不用 dorkbox SystemTray? dorkbox 在 Windows 原生托盘上有已知 bug(`bounds is null`)。本项目改用 SWT 原生托盘,彻底规避。 ### `submit` 指令报错? 旧版 `submit` 已移除,统一使用 `doPrint`。 ## 版本历史 见 [RELEASE-NOTES.md](RELEASE-NOTES.md)。 ## License [MIT](LICENSE)