简介:这份资源面向需要在网页或桌面程序中实现打印功能的开发者,提供 Lodop 6.0 打印控件的安装包与配套示例集合,可用于快速验证票据、报表、标签等场景下的打印调用方式。压缩包共 43 个文件,约 1.55MB,以 40 个 html 示例页面为主体,覆盖多种打印样例与参数演示,另含 1 个 exe 安装程序、1 个 htm 说明文档和 1 个 js 脚本文件,安装、查阅说明与调用示例相互配套,便于对照理解控件接口。资源已有 1254 人学习下载,具备一定的参考基础。读者可通过示例页面了解打印初始化、页面设置与输出效果之间的对应关系,结合说明文档与脚本文件梳理调用流程,为实际项目中的打印模块开发提供可复用的参考思路,适合初中级开发者查阅借鉴。
1. 打印控件选型:为什么很多老系统绕不开 Lodop
做过 Web 打印的同行大概都有过这种经历:浏览器自带的window.print()打出来的东西,边距飘、分页乱、条码糊,客户一句“打出来跟预览不一样”就能让你改到半夜。尤其是票据、快递面单、标签、报表这类对定位精度要求高的场景,纯前端方案基本压不住。这时候很多人会翻出一个老牌方案——Lodop,一个基于本地服务的打印控件,通过 JS 调用本地打印能力,能精确控制纸张、边距、分页、条码和图形。它解决的核心问题就一句话:让浏览器里的页面按毫米级精度落到打印机上。适合谁?维护存量业务系统的开发者、做票据和标签打印的团队,以及需要在 IE 或内嵌浏览器里跑打印的老项目。这篇不聊虚的,就讲这份 Lodop 资源包怎么落地、参数怎么设、哪些坑我踩过。
2. 资源包拆解与本地服务跑通:从文件结构到第一个打印任务
拿到一个 Lodop 相关的资源包,第一步不是急着写业务代码,而是先搞清楚它到底由哪几块组成。Lodop 的运行模型和普通 JS 库不一样,它不是纯前端脚本,而是一个“前端 JS 接口 + 本地打印服务”的组合。前端那部分负责接收你的指令,本地服务负责真正和操作系统、打印机打交道。理解这个分层,后面排错才不会抓瞎。
2.1 文件构成与各部件职责
一个典型的 Lodop 资源包,常见会包含这几类文件:主控 JS 文件(通常叫LodopFuncs.js之类)、一个用于安装本地服务的可执行程序、若干 ActiveX 或 NPAPI 时代的兼容文件(老项目会用到),以及示例页面。不同版本命名会有差异,但职责划分是一致的。
| 部件 | 作用 | 落地时的注意点 |
|---|---|---|
| 主控 JS | 暴露LODOP对象,封装打印指令 | 必须最先引入,且路径要对 |
| 本地服务程序 | 常驻后台,接收 JS 指令并驱动打印机 | 需安装并确保服务在运行 |
| 兼容组件 | 老浏览器/内嵌内核的桥接 | 新内核项目一般用不到 |
| 示例页 | 验证环境是否通的参照 | 先跑通示例再改业务 |
我一般会先把示例页跑起来,确认“环境通”这件事,再动业务代码。因为如果示例都打不出来,问题一定在服务或权限层,跟你的业务逻辑无关,这个判断顺序能省掉大量无效调试。
2.2 引入主控脚本并初始化
前端接入的第一步是引入主控 JS,然后拿到LODOP实例。常见写法如下:
// 引入主控脚本后,获取打印对象 // 注意:脚本路径要指向资源包里的实际位置 const LODOP = getLodop(); // 初始化:设置打印机与纸张 LODOP.PRINT_INIT("测试任务"); // 初始化一个打印任务,参数是任务名 LODOP.SET_PRINT_PAGESIZE(1, 0, 0, "A4"); // 1 表示纵向,后两个 0 表示用默认宽高 LODOP.ADD_PRINT_TEXT(30, 20, 200, 30, "第一行测试文本"); // 上、左、宽、高、内容 LODOP.PREVIEW(); // 预览,先看效果再真打这段代码的逻辑很直白:PRINT_INIT开一个任务,SET_PRINT_PAGESIZE定纸张,ADD_PRINT_TEXT往页面上“贴”一个文本块,最后PREVIEW预览。参数里最容易搞错的是ADD_PRINT_TEXT的四个位置尺寸,单位是“打印单位”,默认是 0.1 毫米,也就是说30代表 3 毫米。很多人第一次用会以为是像素,结果打出来位置全偏,这是血泪经验里最常见的一条。
SET_PRINT_PAGESIZE的第一个参数是方向,1纵向、2横向;后面两个参数是自定义宽高,传0就用所选纸张的默认值。如果你要打的是自定义票据纸,比如 80mm 宽的小票,就得把宽高按单位填进去,而不是指望它自动识别。
2.3 用预览验证链路是否打通
预览是排查链路的第一道关卡。PREVIEW()能弹出一个预览窗口,如果预览正常但实际打印错位,问题在打印机驱动或纸张设置;如果预览都出不来,问题在服务没起、脚本没加载或权限被拦。我习惯在业务代码里保留一个调试开关,开发阶段默认走预览,上线再切成直接打印:
const DEBUG_PREVIEW = true; // 开发期置 true,上线改 false function doPrint(LODOP) { LODOP.PRINT_INIT("业务单据"); LODOP.SET_PRINT_PAGESIZE(1, 0, 0, "A4"); LODOP.ADD_PRINT_TEXT(30, 20, 300, 30, "单号:TEST-0001"); if (DEBUG_PREVIEW) { LODOP.PREVIEW(); // 预览模式,不消耗纸张 } else { LODOP.PRINT(); // 直接打印 } }这个开关看着简单,但能让你在客户现场少打废几十张纸。参数上没什么玄学,关键是养成“先预览、后打印”的习惯,尤其是第一次接新打印机的时候。
2.4 确认本地服务状态
如果预览窗口弹不出来,先别怀疑代码,去看本地服务。常见做法是打开系统的服务管理界面,找对应的打印服务进程是否在运行;或者直接访问本地服务暴露的端口看有没有响应。服务没起的原因通常是安装没完成、被杀毒软件拦了、或者开机自启被禁用。这一步确认了,后面 90% 的“打不出来”都能定位。资源包里如果带了服务安装程序,按它的默认流程装完,重启一次机器再试,比反复改代码有效得多。
3. 打印模板参数化:纸张、边距、分页与条码的实操配置
环境通了之后,真正花时间的是把一张业务单据“摆”到纸上。Lodop 的强项就在这里——它给你一套坐标系统,你按毫米级去放元素。但强项也是坑源,参数设错一点,打出来就歪。这一章把最常调的几类参数拆开讲。
3.1 纸张与边距的单位换算
前面提过,默认打印单位是 0.1 毫米。这意味着你脑子里想的“左边距 10 毫米”,代码里要写100。我见过不少项目在代码里写10,结果打出来贴着纸边,还以为是驱动问题。稳妥的做法是封装一个换算函数,业务层用毫米,底层转单位:
const MM = 10; // 1 毫米 = 10 个打印单位 // 把毫米转成打印单位,避免业务代码里到处写 0 function mm(v) { return Math.round(v * MM); } LODOP.ADD_PRINT_TEXT(mm(5), mm(10), mm(80), mm(8), "收货人:张三"); // 上边距 5mm,左边距 10mm,宽 80mm,高 8mm这样写的好处是可读性,别人接手一眼能看懂位置。边距这块还有个隐藏点:打印机本身有“物理不可打印区”,一般上下左右各有几毫米打不到。你把内容贴到 0 边距,实际会被裁掉。所以业务上留 3 到 5 毫米的安全边距是常规操作,别跟物理极限较劲。
3.2 分页控制与表格打印
打报表最头疼的是分页。Lodop 提供了表格打印能力,能自动分页,但自动分页的断点未必符合业务预期。常见做法是用ADD_PRINT_TABLE打明细表,配合SET_PRINT_STYLE控制表头重复:
// 打印明细表格,top/left/width/height 同样用打印单位 LODOP.ADD_PRINT_TABLE(mm(40), mm(10), mm(180), mm(120), tableHtml); // 设置表格跨页时表头重复(参数按实际版本接口为准) LODOP.SET_PRINT_STYLEA(0, "TableHeightScope", 1);tableHtml是一段 HTML 表格字符串,Lodop 会把它渲染到指定区域。这里的关键参数是高度,给太小会提前分页,给太大又可能溢出。我的经验是按“每页可容纳行数 × 行高”估算,再留一行余量。分页断点如果实在不理想,可以手动插入分页标记,把一张大表拆成多个ADD_PRINT_TABLE调用,虽然笨但可控。
3.3 条码与二维码的精度设置
票据和面单离不开条码。Lodop 支持一维码和二维码,调用方式类似:
// 一维码:类型、上、左、宽、高、内容 LODOP.ADD_PRINT_BARCODE(mm(50), mm(10), mm(60), mm(15), "128", "SN20240001"); // 二维码 LODOP.ADD_PRINT_BARCODE(mm(50), mm(80), mm(25), mm(25), "QRCode", "https://example.local/order/1");条码最容易翻车的是“打得出来但扫不出”。原因通常有三个:尺寸太小、分辨率不够、或者内容里混了非法字符。一维码的宽高别抠太狠,宽度低于 40 毫米、高度低于 10 毫米,很多扫码枪就吃力了。二维码则要注意纠错级别,内容长的时候适当放大尺寸。打印前一定用真机扫一遍,预览看着清楚不代表扫得出来,这是另一个玄学点。
3.4 多打印机与默认打印机选择
一个收银台接两台打印机是常态,一台打小票一台打标签。Lodop 可以指定打印机:
// 先枚举可用打印机,再按名称选择 const printers = LODOP.GET_PRINTER_NAME ? LODOP.GET_PRINTER_NAME() : ""; LODOP.SET_PRINTER_INDEX("标签打印机"); // 传打印机名称或索引SET_PRINTER_INDEX接受名称或序号,传名称更稳,因为序号会随系统变化。坑在于打印机名称必须和系统里显示的完全一致,多一个空格都选不中。我一般会在初始化时把枚举结果打到控制台,对照着填,别凭记忆写。
4. 避坑与排查:那些让打印任务翻车的具体问题
打印这东西,代码写对了不代表能打对,环境、驱动、权限任何一环出问题都白搭。下面几条是我实际踩过的,按“现象 → 原因 → 解决”整理,照着排查能少走弯路。
4.1 预览正常但实际打印错位
现象:预览窗口里位置精准,点打印后整体偏移几毫米。原因多半是打印机驱动的纸张设置和代码里SET_PRINT_PAGESIZE不一致,驱动按自己的边距又裁了一刀。解决:先在系统打印机属性里把默认纸张设成和代码一致,再检查驱动里的“缩放”选项是否被打开,缩放会破坏坐标。改完重启打印服务再试。
4.2 本地服务被杀毒软件拦截
现象:昨天还能打,今天预览都弹不出来,控制台报连接失败。原因:本地服务进程被安全软件当成可疑程序拦了,或者开机自启被清理。解决:把服务程序加入白名单,重新安装一次服务并确认自启项存在。这类问题在客户现场特别常见,交付前最好把这一步写进部署清单。
4.3 条码打得出来但扫不出
现象:肉眼看着清晰,扫码枪反复失败。原因:条码尺寸压得太小,或打印分辨率(DPI)不足导致边缘模糊。解决:把一维码宽度提到 50 毫米以上、高度 12 毫米以上,二维码边长不低于 25 毫米;同时确认打印机 DPI 设置,热敏机低于 203 DPI 时细条码本身就难扫。改完用真机复扫,别只看预览。
4.4 分页断在表格中间
现象:明细表跨页时,某一行被从中间劈开,或者表头没重复。原因:表格高度估算不准,自动分页断点落在行内;表头重复样式没设对。解决:按行高精确计算可容纳行数并留余量,必要时手动拆表;表头重复用SET_PRINT_STYLEA配合表格高度范围设置,参数按实际接口核对。
4.5 内嵌浏览器里脚本加载失败
现象:在某个内嵌浏览器或老内核里,getLodop()返回空。原因:内核兼容性或脚本加载顺序问题,主控脚本没在业务脚本之前完成加载。解决:确认脚本引入顺序,必要时在DOMContentLoaded之后再初始化;老内核项目要确认资源包里的兼容组件是否已正确注册。这类问题没有通用解,只能按内核版本逐个试。
5. 进阶技巧:把打印能力封装成可复用的业务模块
单张单据调通之后,真正提升效率的是把打印逻辑收成一个模块,业务层只传数据、不碰坐标。我一般的做法是定义一个模板配置对象,把纸张、元素位置、字段映射都抽出来,打印函数只负责“套模板”。这样新增一种单据只是加一份配置,不用复制粘贴一堆ADD_PRINT_*。
// 模板配置:位置全部用毫米,打印时统一换算 const TEMPLATES = { receipt: { paper: { orient: 1, width: 80, height: 0, name: "小票纸" }, fields: [ { type: "text", top: 5, left: 5, w: 70, h: 8, key: "title" }, { type: "text", top: 15, left: 5, w: 70, h: 6, key: "orderNo" }, { type: "barcode", top: 25, left: 5, w: 60, h: 15, code: "128", key: "sn" } ] } }; function renderTemplate(LODOP, tpl, data) { LODOP.PRINT_INIT("模板打印"); LODOP.SET_PRINT_PAGESIZE(tpl.paper.orient, mm(tpl.paper.width), mm(tpl.paper.height), tpl.paper.name); tpl.fields.forEach(f => { const v = data[f.key] || ""; if (f.type === "text") { LODOP.ADD_PRINT_TEXT(mm(f.top), mm(f.left), mm(f.w), mm(f.h), v); } else if (f.type === "barcode") { LODOP.ADD_PRINT_BARCODE(mm(f.top), mm(f.left), mm(f.w), mm(f.h), f.code, v); } }); }这段封装的价值在于把“坐标”和“数据”彻底分开。业务层调用时只关心data里有没有orderNo、sn,位置调整全在配置里改。参数上要注意paper.height传0表示用纸张默认高度,小票纸这种连续纸通常这么处理;如果是有固定高度的标签,就填实际毫米数。
验证封装是否可靠,我有个笨办法但很有效:准备一组边界数据——超长文本、空字段、特殊字符、超长条码内容——逐个跑预览。超长文本会暴露高度不够导致的截断,空字段能看出有没有做兜底,特殊字符(比如&、中文标点)能验证渲染是否正常。这套边界用例跑一遍,比在客户现场试错强太多。
还有一个容易被忽略的点是打印任务的清理。连续打印多张时,如果每次都PRINT_INIT却不释放,某些环境下会堆积任务导致后面打不出来。稳妥做法是每张单据独立初始化、打完即结束,不要在一个任务里塞太多逻辑。另外,批量打印时加一个短延时或串行队列,避免指令发太快被本地服务吞掉,这个在低配收银机上尤其明显。
从那以后我每次接新的打印需求,都强制先跑一遍“示例页 → 预览 → 边界数据 → 真机扫码”这条链路,任何一环没过就不往下写业务代码。打印这行的坑,八成都能在这条链路上提前暴露。希望帮到你。
本文还有配套的精品资源,点击获取