简介:Lodop打印模板与安装包面向Java Web开发者,提供从控件部署到页面打印调用的完整解决方案。压缩包共58个文件,以52个HTML打印示例为主,配合CSS样式、JavaScript脚本及三套Windows安装程序(32/64位版本齐全),整体约9.42MB,适合需要实现多页打印、条码二维码、图文混排等复杂打印场景的项目参考。包内示例覆盖多种常见业务布局,开发者可从入口页面浏览各模板,对照学习Lodop的初始化、样式控制与打印参数设置;同时附带的安装包解决了不同系统环境下的部署需求。已有942人下载学习,适合正在集成Lodop控件或希望优化现有打印功能的Java工程师快速上手。
1. Lodop 是什么:一套浏览器做不到、得靠本地控件的打印方案
做 Java Web 的开发者,早晚会接到一个表单打印的需求。医院体检回执、仓库出库单、物流面单,页面显示得整整齐齐,一到打印就露馅:原生打印窗口控制不了边距,套打定位偏移,条码扫不出来。Lodop 就是补这个短板的桌面打印控件,页面通过 JavaScript 调本地控件完成打印,不走网页排版,而是按模板里的绝对坐标输出,所以能做到套打、批量连续打印和条码二维码输出。这份资源里的安装包覆盖了常见系统环境,下载后可以直接装;模板文件导入设计器就能改字段,不需要从零折腾打印语句。
适合两类人:一类是刚接手打印模块、被领导要求“下周上线”的 Java 后端工程师,另一类是前端被临时派来救火、连控件都没装过的新手。前者关心接口怎么设计和模板存哪里,后者关心装完控件怎么验证能用。我尽量把这两条线都写透,从安装部署讲到模板维护,中间穿插实际翻车记录。
2. 安装与通信验证:安装包怎么分发、页面怎么确认控件活着
2.1 先搞清楚控件装在哪
Lodop 本质是客户端软件,打印机挂在哪台电脑上,控件就装在哪台电脑上,和你的 Tomcat、Spring Boot 服务没有直接关系。很多第一次用的人把安装包丢到服务器上就以为完事了,结果浏览器打开页面还是提示找不到控件,因为服务器装了没用,要装的是访问页面的那台电脑。
资源包里的安装包适合先拿到测试机上装一遍,装的时候系统可能会弹防火墙提示或本地服务启动提醒,选择允许。企业内网的分发方式我一般会这样处理:把安装包放到文件服务器或一个静态下载地址,页面检测到控件缺失时直接引导用户去下载;公司有域环境的可以把安装包推给域内机器静默安装,省得一台台跑。
提示:装完一定要刷新浏览器页面再调用,控件服务是装完才启动的,不刷新页面拿到的还是旧环境。
2.2 页面怎么确认控件是活的
控件装好了,页面端要有一个桥接文件来处理和本地服务的通信。资源里一般会带一套前端脚本,里面封装了getLodop()方法,方法内部会自动探测控件是否安装、版本是否匹配,然后连上本地端口。下面这段检测代码是我在项目里常用的模板,复制到任意一个测试页面就能用:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Lodop 控件检测页</title> <script src="/js/LodopFuncs.js"></script> </head> <body> <button onclick="checkPrintEnv()">检查打印环境</button> <button onclick="printTest()">打印一张测试页</button> <script> function checkPrintEnv() { var LODOP = getLodop(); if (!LODOP || typeof LODOP.PRINT_INIT !== 'function') { alert('打印控件未安装或未启动'); return; } alert('控件已就绪'); } function printTest() { var LODOP = getLodop(); if (!LODOP || typeof LODOP.PRINT_INIT !== 'function') { alert('没有可用控件,请先安装'); return; } LODOP.PRINT_INIT('测试页'); LODOP.SET_PRINT_PAGESIZE(1, 0, 0, 'A4'); LODOP.ADD_PRINT_TEXT(100, 100, 800, 100, 'Lodop 连接正常'); LODOP.PREVIEW(); } </script> </body> </html>getLodop()返回的对象在控件缺失时可能会缺失核心方法,所以判断要落在typeof LODOP.PRINT_INIT !== 'function'上,比单纯判断对象是否存在更可靠。PRINT_INIT('测试页')是每次打印任务开始前必须调用的一次初始化,参数是任务名,会在打印队列里显示;SET_PRINT_PAGESIZE(1, 0, 0, 'A4')里最后一个参数指定纸张类型,不写的话控件会套打印机默认纸,后面第 5 章会讲这个坑;ADD_PRINT_TEXT(100, 100, 800, 100, '内容')的坐标单位默认是 0.1 毫米,100 就是距离顶部 10 毫米的位置。
2.3 64 位环境与本地 8000 端口
页面和控件之间的通信走的是本地 WebSocket,默认监听 127.0.0.1:8000。如果这个端口被其他程序占用,控件服务会启动失败,页面表现就是明明装了控件,getLodop()还是提示不可用。这时候在 Windows 机器上打开命令行看端口:
netstat -ano | findstr 8000 tasklist | findstr Lodop第一条命令查 8000 端口被哪个进程占用,第二条查控件进程是否在跑。如果看到端口被占用,优先在资源包里找 64 位版本重装;老版本控件在新浏览器里经常因为端口和兼容性问题静默失败,换新版是性价比最高的处理方式。企业内网环境里如果装了安全软件,还要把 8000 端口加入放行名单,否则控件服务起不来,页面也连不上。
3. 打印模板设计与数据绑定:从设计器导出到 ADD_PRINT_HTM 赋值
3.1 两种模板思路,先想清楚再动手
Lodop 的模板落地有两种路线。第一种是纯代码路线:把每个字段用ADD_PRINT_TEXT逐个写死在页面上,比如“姓名”放坐标 (100,100),“年龄”放坐标 (100,200)。这种方式适合字段永远不变的面单,改一个坐标都得动代码,业务字段一多就变成灾难。
第二种是 HTML 模板路线:把整块打印内容做成一个 HTML 片段,业务字段用占位符表示,比如${patientName},然后通过ADD_PRINT_HTM一次性交给控件渲染。字段增删只改模板,不动代码;Java 后端只需要把模板内容和业务数据返回给前端,前端负责替换占位符。这也是这套方案里我最推荐的方式,后面讲的都是这一种。
表格类内容还要注意一个选择:ADD_PRINT_TABLE适合简单的明细列表,出现合并单元格、行高自适应、跨页表头这些复杂场景时,直接用 HTML 模板里的<table>反而更稳。控件对 HTML 表格的解析能力比专用表格控件更灵活,这个经验在真实项目里救过我很多次。
3.2 设计器怎么把“样子”变成模板
资源包里的模板文件建议先用打印设计器打开调整,不要直接手写 HTML。在页面里调用LODOP.PRINT_DESIGN()会弹出设计器窗口,操作流程基本是:把文本、线条、条码图标拖到画布上,双击对象修改字体字号和内容,从纸张设置里选 A4 或自定义尺寸,最后点预览确认效果。
设计完成后不是截图保存,而是要导出。设计器会把画布上的内容生成对应的打印语句,这些语句落成一个 HTML 模板,固定文本留在模板里,变化字段替换成占位符。模板里要放条码或二维码的地方,在设计器里拖一个条码对象,导出的 HTML 会包含对应的条码指令;手写的话需要单独调用ADD_PRINT_BARCODE,参数多且容易写错,不建议新手手写。
注意:设计器预览和打印机实际输出会有偏差,字体缺失、缩放比例、打印机驱动都会影响结果。设计完先拿真实业务数据打一张,别直接上量。
3.3 模板存数据库还是文件
我习惯把模板存数据库,而不是放在服务器文件目录里。原因有三个:一是改模板不用重新发版,运营改个字段位置,后端更新一条记录就行;二是模板和代码生命周期分离,线上出现模板问题可以直接在库里修;三是方便加版本号,这是第 6 章要讲的维护手段。
存储结构至少要包含这几个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| template_code | varchar(64) | 模板编码,业务侧引用 |
| template_version | int | 版本号,每次修改 +1 |
| content | text | 模板 HTML 内容 |
| status | tinyint | 0 停用 / 1 启用 |
模板 HTML 经常带着表格样式,几百字节起步,字段类型直接上 text,用 varchar(255) 的话一段表格就超长。模板数量少且几乎不改的项目,放在 classpath 下也能跑,但一旦出现“模板改错了要回退”的需求,数据库方案的优势就非常明显。
3.4 前端加载模板并替换占位符
前端拿到模板和业务数据后,先做占位符替换,再把替换完成的 HTML 交给控件打印。替换逻辑我用的是正则全局匹配:
function printByTemplate(templateHtml, data) { var html = templateHtml; for (var key in data) { var reg = new RegExp('\\${' + key + '}', 'g'); html = html.replace(reg, data[key]); } var LODOP = getLodop(); if (!LODOP || typeof LODOP.PRINT_INIT !== 'function') { alert('打印控件不可用'); return; } LODOP.PRINT_INIT('单据打印'); LODOP.SET_PRINT_PAGESIZE(1, 0, 0, 'A4'); LODOP.ADD_PRINT_HTM(30, 20, '90mm', '140mm', html); LODOP.PREVIEW(); }RegExp('\\${' + key + '}', 'g')里$在正则中有特殊含义,所以要写成\\$转义,否则匹配不到占位符。ADD_PRINT_HTM(30, 20, '90mm', '140mm', html)前两个参数是左上角坐标,30 表示距离顶部 3 毫米;宽高我习惯带mm单位,避免去记默认数值单位。替换完成后先PREVIEW()预览,确认无误再改成PRINT()。
批量打印时如果发现模板里的字段没替换上,八成是数据 key 和模板占位符拼写不一致。这个排查思路放在这里,后面踩坑章节还会展开。
4. Java 后端对接:接口只吐数据和模板,打印语句交给前端
4.1 先纠正一个认知:不是“Java 控制打印”
搜“lodop java”的人,多半是后端工程师被派来处理打印需求。第一反应往往是:后端怎么调打印机?答案其实很朴素,Lodop 跑在客户端电脑上,Java 后端碰不到它,碰不到就不存在“Java 驱动打印”这回事。后端真正要做的只有两件事:按模板编码查出模板 HTML 内容,按业务单号查出业务数据,然后把这两样东西通过接口返回给前端。
这样说有些后端同事会觉得“那我岂不是没活干”。不是的,后端的活在于保证模板和数据稳定:模板内容要按版本返回,业务数据要按打印场景做裁剪,敏感字段要不要脱敏,打印操作要不要记录日志,这些才是后端该管的。前端只管把数据和模板拼起来调控件。
4.2 接口的数据结构怎么设计
接口返回结构我会固定成三个部分:模板编码、模板 HTML 内容、业务数据。模板和业务数据分开返回,前端各自处理,不要在后端就把数据拼进模板。原因是后端一旦拼好,模板升级和历史单据追溯就全乱套了,前端做替换反而更灵活。
@RestController @RequestMapping("/print") public class PrintController { private final PrintTemplateService templateService; private final OrderService orderService; public PrintController(PrintTemplateService templateService, OrderService orderService) { this.templateService = templateService; this.orderService = orderService; } @GetMapping(value = "/data/{templateCode}", produces = "application/json;charset=UTF-8") public Map<String, Object> printData(@PathVariable String templateCode, @RequestParam String orderId) { PrintTemplate template = templateService.getLatestTemplate(templateCode); Map<String, Object> bizData = orderService.getOrderPrintData(orderId); Map<String, Object> result = new HashMap<>(); result.put("templateCode", template.getTemplateCode()); result.put("templateHtml", template.getContent()); result.put("data", bizData); return result; } }produces = "application/json;charset=UTF-8"这段很容易被漏掉,漏掉之后接口返回中文可能变成乱码,模板里的中文全变问号。业务数据这里用Map<String, Object>平铺返回,不要直接把数据库实体类整个丢出去,前端替换占位符时只需要拿到一层结构,嵌套对象会让前端逻辑很难写。
4.3 中文与编码的三个注意点
Lodop 打印中文踩坑基本都集中在编码上,按下面顺序排查能解决大部分问题。第一个是接口响应编码,上面代码里已经处理;第二个是数据库连接串,Spring Boot 的配置里要确认characterEncoding=utf8,否则模板里的中文从库里取出来就是乱码;第三个是前端拿到 JSON 后的解析,正常fetch加json()解析不会出问题,真正的坑在模板编辑环节。
常见场景是有人从网页设计器里复制模板 HTML 粘贴到数据库工具里,保存后引号被转义,前端读到一串带反斜杠的字符串,整个模板解析失败。这个问题的特征是页面预览时能看到 HTML 源码而不是渲染效果。解决方法是保存模板时统一走接口,不要直接手改数据库,接口层做一次 HTML 转义处理,取出来时再反转义。
提示:后端不要主动在返回前把
data里的值拼进templateHtml,特殊字符转义会变成双重转义,前端替换时处理反而干净。
5. 避坑排查:端口占用、样式丢失、批量卡死的 5 个现场
5.1 先确认控件真的活着
现象:页面调用getLodop()不报错,但PRINT_INIT没反应,或者一直提示“未找到控件”。
原因:本地 8000 端口被其他程序占用,控件服务根本没起来;还有一种情况是浏览器安全设置拦截了网页访问本地端口。
解决:Windows 命令行里执行netstat -ano | findstr 8000,如果有其他进程占用,结束那个进程后重启控件服务。如果是安全软件拦截,把localhost:8000加入信任名单。曾经有个现场排查了一下午,最后发现是某聊天软件占用了 8000 端口。
现象:公司内网电脑装了控件,检测页依然提示“控件未启动”。
原因:域环境防火墙出站入站规则拦截了本地服务,控件装上了但服务拉不起来。
解决:把控件安装目录和 8000 端口加入防火墙放行名单。注意不光是入站,出站有时也会拦,最好两条规则一起加。
5.2 打印出来和预览不一样
现象:预览正常,打印出来的内容整体偏右下角,或者被裁掉一部分。
原因:页面预览和实际打印用的纸张尺寸不一致。预览时按 A4 排版,打印机默认纸是 Letter 或者自定义纸,控件输出时按纸张边界裁切。
解决:在每次打印初始化时显式调用SET_PRINT_PAGESIZE(1, 0, 0, 'A4'),和实际放进去的纸一致;同时检查打印机驱动的“缩放以适应”选项,这个选项会静默调整打印比例,和控件的绝对坐标叠加后位置就飘了。
现象:网页里表格线正常,打印出来只剩文字,边框线全部消失。
原因:模板里的<table>用了外部 CSS 的 class 定义边框,Lodop 解析 HTML 模板时不加载外链样式表,样式丢了。
解决:模板里所有表格边框写成内联样式,比如style="border:1px solid #000"。宁可模板 HTML 看起来冗余,也不要依赖 class。
5.3 批量打印与特殊字符
现象:批量打印 100 张,打到第 20 张控件就卡住,浏览器也跟着假死。
原因:循环里连续PRINT_INIT和PRINT没有给本地服务留缓冲时间,打印队列积压;或者某一张的 HTML 内容特别大,控件渲染超时。
解决:分批打印,每批之间加延时;单张模板 HTML 控制在几十 KB 以内,不要把后台管理页面的整个框架塞进模板。
现象:业务数据里有个字段值是A&B或包含英文引号,打印出来内容被截断,HTML 结构错乱。
原因:模板替换后,特殊字符直接进入了 HTML 标签属性或标签体,浏览器解析时把它当成标签分隔符处理了。
解决:前端替换占位符时统一做一次textContent处理,把<、>、&、引号转成 HTML 实体。后端不要在保存模板时做转义,前端在最终渲染前处理一次就够了。
6. 模板版本管理与批量打印:Lodop 落地后的维护习惯
6.1 模板上库带版本号:数据库结构里留一条后悔药
模板一旦上生产,早晚会遇到“改完模板发现还是旧版好看”的场景。我的习惯是模板表永远保留版本字段,模板修改只做新增,不做更新覆盖。业务单据在打印时记录当时的模板版本,历史单据回查时还能按原版式重新打印。
public PrintTemplate getTemplateForPrint(String code, Integer version) { if (version == null) { return templateMapper.findLatest(code); } return templateMapper.findVersion(code, version); }调用方传订单时带的版本号,没传就直接取最新版。这个逻辑很简单,但能解决两个实际问题:线上模板出错时一键回退到上一版;打印记录里能分清哪张单用的是哪个模板,不会出现旧单据打印格式漂移的情况。
6.2 批量打印先过一遍单条预览
批量打印之前,先拿一条真实业务数据把模板跑一遍预览,这是我吃过大亏之后养成的习惯。有一次图省事直接改了线上模板,没有做单条验证就批量提交,几百张单子全按新样式打了出来,部分字段还是空的,因为新模板的占位符和旧数据 key 对不上,替换失败。从那以后我每次改模板都强制走一遍流程:更新数据库模板加新版本号 → 取一条真实单据预览 → 打印一张测试页 → 再放量批量执行。这套流程多花五分钟,但能避免浪费几百张纸和一上午的返工。
安装包和模板示例都在开头提到的那份资源里,下载后先按第 2 章把控件装好,再用第 3 章的模板改业务字段,基本半天能跑通第一个打印页。希望帮到你。
本文还有配套的精品资源,点击获取