简介:这是一份围绕Lodop打印控件的JS打印预览资源包,面向需要为网页增加打印预览功能的Web开发者,解决浏览器端复杂打印任务与预览控制问题。包内共56个文件,以50个HTML示例页面为主体,配套LodopFuncs.js脚本、CSS样式以及Windows安装程序,涵盖打印初始化、添加内容、预览调用等典型用法,rar压缩包大小约9.52MB。已有2137人学习下载。资源通过大量可运行的示例展示了从控件安装、对象实例化到PRINT_INIT、ADD_PRINT_HTM、PREVIEW等核心API的完整调用流程,同时包含不同打印场景的页面模板,可直接修改复用。值得一提的是,这些示例代码清晰地演示了ActiveX与NPAPI两种插件环境下的兼容写法,并附带Lodop6.198版本压缩包,方便本地部署测试,帮助开发者快速掌握Lodop在网页打印中的应用要点。
1. js打印预览(LodopFuncs.js)到底解决什么问题
业务系统做单据打印,最痛苦的不是把内容填进页面,而是让输出方向和打印机、纸张、预览窗口完全对齐。浏览器原生window.print()只能按整页打,iframe 方案嵌套多了就失去坐标精度,连续纸、自定义纸张、套打定位几乎无从下手。LodopFuncs.js是 Lodop 打印控件在网页端的 JavaScript 入口文件,页面引入它之后,通过getLodop()拿到 LODOP 对象,之后所有打印任务都以这个对象为入口,PRINT_INIT建任务、ADD_PRINT_*放内容、PREVIEW出预览。它的核心价值不是把按钮变好看,而是把浏览器环境判断、控件安装检测、实例化封装成一套稳定入口,让业务代码只关心打印内容。适合 ERP 单据、医院处方、物流面单、证书打印这类对纸张尺寸、元素坐标、预览交互有明确要求的系统。选这条路,基本等于接受了一种成熟、能快速验证的本地打印方案。
2. LodopFuncs.js 是什么:getLodop() 与页面里那个“凭空出现”的 LODOP 对象
2.1 它做的是“设备侧”的探测与连接
很多第一次接触这个文件的同学会以为LodopFuncs.js是一个排版引擎,其实它只是打印控件在浏览器里的“接线员”。电脑上必须先安装 Lodop 或 C-Lodop 的本地程序,这个程序向浏览器暴露对象或本地服务,LodopFuncs.js里的getLodop()负责去探测这两个通道到底通不通。
在 IE 和旧式 Edge 里,它走 ActiveX 通道,直接new ActiveXObject("Lodop.Lodop");在 Chrome、Firefox 这类现代浏览器里,它依赖本机上的 C-Lodop 服务,通过 WebSocket 访问localhost端口,再回调一个createCLODOP()方法来创建打印对象。两边的返回值最终都赋给同一个全局变量,业务代码不用关心底层通道,拿到的都是一个看起来“凭空出现”的 LODOP 对象。
// 这是 LodopFuncs.js 内部常见的探测思路,不是逐字源码,但结构一致 var CreatedOKLodop7766 = null; function getLodop() { // 1. IE/Edge 兼容模式:ActiveX 通道 if (navigator.appVersion.indexOf("MSIE") > -1 || navigator.userAgent.indexOf("Trident") > -1) { if (CreatedOKLodop7766 == null) { try { CreatedOKLodop7766 = new ActiveXObject("Lodop.Lodop"); } catch (e) { CreatedOKLodop7766 = null; } } } // 2. 现代浏览器:由 C-Lodop 服务加载 CLodopfuncs.js 后回调 if (CreatedOKLodop7766 == null) { if (window.createCLODOP) { CreatedOKLodop7766 = window.createCLODOP(); } else { // 内部会去请求 http://localhost:8000/CLodopfuncs.js } } return CreatedOKLodop7766; }逻辑说明:前半段是 ActiveX 回退,后半段是 WebSocket 服务通道。现代 Chrome 环境下,把代码写进onload事件再执行更稳妥,能避免页面 DOM 未就绪、脚本加载顺序不对导致getLodop()拿不到对象甚至直接抛错。参数上CreatedOKLodop7766是全局变量,重复调用会复用同一个实例,避免每次创建新对象浪费资源。
2.2 用之前先判空,别把 getLodop() 当普通构造函数
不少业务代码直接var LODOP = getLodop()就往下写,结果在没装控件的测试机上报出一串看不懂的错误。getLodop()不是创建一个大而全的对象,它只是在“探测控件是否存在”,探测失败时返回的是null,不是空对象。对null继续调用PRINT_INIT,自然就是“对象不支持此方法”。
一个负责人的调用姿势是这样:
function createTask() { var LODOP = getLodop(); if (!LODOP) { alert("打印控件未启动,请先安装 Lodop 或 C-Lodop"); return; } LODOP.PRINT_INIT("出库单"); LODOP.ADD_PRINT_TEXT(30, 20, 200, 22, "出库单号:CK20250701"); LODOP.PREVIEW(); }逻辑说明:先判空再执行,这是所有 Lodop 业务代码的第一道防线。判空之后的PRINT_INIT是任务初始化,参数是任务名,会显示在打印任务的标题里;ADD_PRINT_TEXT的前四个参数分别是上边距、左边距、宽度、高度,单位是像素;PREVIEW负责弹出预览窗口。这个顺序不能乱,PRINT_INIT必须第一个调用,后续所有添加内容的 API 都由它派生。参数单位这里最容易踩坑:坐标系原点在页面左上角,(30, 20)表示距离纸张顶部 30 像素、左侧 20 像素。
2.3 为什么不直接用 window.print()
浏览器打印和 Lodop 之间隔着好几个量级的差异。先用表格把适用性摊开看:
| 对比项 | window.print() | LODOP.PREVIEW() |
|---|---|---|
| 打印范围 | 整个当前页面 | 当前打印任务内的元素 |
| 自定义纸张 | 依赖介质设置,难精确 | 可指定 A4/A5/B5/自定义尺寸 |
| 连续纸 | 基本不支持 | 支持连续纸和高度自定义 |
| 坐标定位 | 用 CSS 凑合 | 像素级绝对定位 |
| 条码/二维码 | 需要额外插件或 canvas | 提供原生条码接口 |
| 预览交互 | 浏览器自带打印预览 | 可带份数、打印机选择、页边距调整 |
实际项目里,window.print()适合“把整张页面打下来当附件存档”这种粗需求;一旦牵扯到套打、针式打印机、面单尺寸、流水号条码,不选 Lodop 这类控件,后面会反复改布局。选 Lodop 之后,预览又分为两类:一种是PREVIEW()弹独立预览窗口,另一种是直接用PRINTA()跳过预览,两种可以共存,按业务按钮区分。同一页面上如果想在预览前弹一次自己的遮罩层让用户勾选份数,也可以先做业务遮罩,再把结果带进PRINT_SET_PRINT_COPIES或PRINT_SET_PRINT_MODE相关参数。
3. 最小可用的打印预览:PRINT_INIT 到 PREVIEW 一次跑通
3.1 引入 LodopFuncs.js 并弹出第一个预览窗口
写一个最精简的 HTML 页面,目的只有一个:点按钮,弹预览。把控件安装好之后,这个页面就是后续改模板的底座。脚本的加载顺序必须注意,LodopFuncs.js要放在业务脚本前面,否则业务代码执行getLodop()时函数还没被定义。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>js打印预览最小示例</title> <script src="LodopFuncs.js"></script> </head> <body> <button onclick="doPreview()">打印预览</button> <script> function doPreview() { var LODOP = getLodop(); if (!LODOP) { alert("打印控件未启动,请先安装"); return; } LODOP.PRINT_INIT("出库单"); LODOP.ADD_PRINT_TEXT(30, 20, 200, 22, "出库单号:CK20250701"); LODOP.ADD_PRINT_LINE(55, 0, 55, 700, 1); LODOP.PREVIEW(); } </script> </body> </html>逻辑说明:ADD_PRINT_LINE的五个参数依次是上边距、左边距、线宽起始点、横向长度、线宽,这里是在文本下方画一条分隔线,让预览里能看到“定位”的效果。PREVIEW()弹出预览后,还能在预览窗口里看到打印机选择、份数设置、纸张设置,这比纯window.print()的预览完整得多。如果你的验证环境还没装控件,预览不会弹出来,而是走进alert分支,这是预期的。要注意的是,预览窗口在当前主流浏览器里是独立弹窗,有的浏览器会把弹窗拦截,需要放行一次。
3.2 打印项内容类型的选型:文本、HTML、还是表格
Lodop 的ADD_PRINT_*系列接口按内容类型分成好几个,选错类型会导致排版失效。我用得最多的是这三个:
| API | 内容类型 | 适用场景 | 注意事项 |
|---|---|---|---|
| ADD_PRINT_TEXT | 纯文本 | 字段值、单据号、日期 | 不会解析 HTML,换行要写 \r\n |
| ADD_PRINT_HTM | HTML 片段 | 富文本、样式较多的区域 | 会解析标签,但外部 CSS 不一定生效 |
| ADD_PRINT_TABLE | 表格 HTML | 明细行、订单列表 | 会自动分页,列宽由 HTML 决定 |
纯文本场景写起来最直接:
LODOP.ADD_PRINT_TEXT(110, 20, 300, 22, "收货人:" + receiverName); LODOP.ADD_PRINT_TEXT(135, 20, 300, 22, "联系电话:" + receiverPhone);逻辑说明:参数(110, 20)是位置,300和22是宽高。如果receiverName是用户输入,需要注意里面可能有换行符,Lodop 会原样渲染,所以业务层要么过滤换行,要么用\r\n控制它。表格类内容用ADD_PRINT_TABLE时,表格 HTML 里的<thead>会自动在每页重复,这是做明细分页的核心特性,但前提是表格必须放在一个独立的 div 里传给接口,不能混杂其他文字。
3.3 PRINT_INIT 之后的初始化参数不要随手写
PRINT_INIT("出库单")这个任务名看着不起眼,但它有两个隐藏作用:一是预览窗口的标题,二是后续打印任务的管理凭据。如果你在PRINT_INIT之前调用了任何ADD_PRINT_*,Lodop 会直接忽略,而且不会报错,表现就是预览窗口空白或只有部分内容。这是一个非常容易踩的坑,代码里最好把初始化写成一个独立函数,防止业务代码在多个入口同时触发。
function buildPrintTask(LODOP, taskName) { if (!LODOP) return false; LODOP.PRINT_INIT(taskName); return true; }逻辑说明:把初始化抽出来之后,每个业务入口只有一条调用路径,不会出现“某个按钮在前面加了一行 ADD_PRINT_TEXT 导致整体失效”的情况。任务名建议包含业务类型和日期,例如“出库单-20250701-128”,这样在预览窗口和生产环境中排查问题时能快速定位。
4. 打印预览前的排版参数:纸型、坐标、旋转与单位换算
4.1 PRINT_SET_PRINT_PAGESIZE 的纸型类型与自定义纸张
预览窗口里的纸张大小是由PRINT_SET_PRINT_PAGESIZE控制的,它接收纸型类型和宽高参数。固定纸型类型从 1 开始,不同厂商驱动下表现略不一致,我按常见版本整理如下:
| iType | 含义 | 固定尺寸 |
|---|---|---|
| 1 | A4 竖版 | 210mm × 297mm |
| 2 | A4 横版 | 297mm × 210mm |
| 3 | A3 竖版 | 297mm × 420mm |
| 4 | A3 横版 | 420mm × 297mm |
| 5 | A5 竖版 | 148mm × 210mm |
| 6 | A5 横版 | 210mm × 148mm |
| 7 | B5 竖版 | 176mm × 250mm |
| 8 | B5 横版 | 250mm × 176mm |
| 11 | 自定义纸张 | 由第 2、3 参数决定 |
代码里的写法分固定纸型和自定义纸型:
// 固定 A4 竖版,第二三个参数传 0 即可 LODOP.PRINT_SET_PRINT_PAGESIZE(1, 0, 0, "A4"); // 自定义纸型,宽度 100mm,高度 150mm LODOP.PRINT_SET_PRINT_PAGESIZE(11, 1000, 1500, "自定义");参数说明:自定义纸型的第二、三参数单位是 0.1mm,也就是 100mm 写 1000,150mm 写 1500。这里有一个血泪经验:老版本 Lodop 曾经把 iType=5 映射成自定义纸型,后来版本又改回 A5 竖版,所以迁移老项目时一定要看当前机器控件版本的文档,别把旧代码里的数字直接复制过来,否则预览里纸张尺寸会变成 A5。建议在新页面里先打印一个“测试纸型任务”验证一下尺寸。
4.2 坐标体系与毫米换算:为什么预览和实际打印会差几毫米
Lodop 的ADD_PRINT_*系列接口坐标严格意义上不是毫米也不是 CSS 像素,而是基于 1200 DPI 的打印坐标体系换算出来的整数。很多人在这个点上翻车:在 Chrome 的预览窗口里看着位置合适,拿到针式打印机上打出来偏了 2~3 毫米。
原因是打印机的物理分辨率(通常是 203 DPI 或 300 DPI)和屏幕的 DPI 不一致,Lodop 在内部会按当前纸张尺寸重新换算。如果业务里要按毫米定位,先把毫米换算成坐标:1 毫米约等于 47.24 个单位(1200 / 25.4),取整后写进参数。比如距纸左 20mm、上 30mm,参数应该是 (1417, 944),但实际项目中很少有人这么算,更多是拿一张测试纸反复调像素值。我的习惯是先按屏幕预览调到“看着差不离”,再用真实打印机打样,记录偏移量,最后在坐标上加上一个固定修正值,这个修正值针对每台打印机都要单独标定一次。
4.3 旋转、边距与字体的三个高频设置
打印方向不是靠PRINT_SET_PRINT_PAGESIZE(2)就能覆盖所有业务的,比如横幅标签、竖直面单,需要在任务上设置旋转角度。旋转一般通过SET_PRINT_MODE或PRINT_SET_PRINT_MODE来实现:
LODOP.PRINT_INIT("横幅标签"); LODOP.PRINT_SET_PRINT_PAGESIZE(11, 6000, 2000, "自定义横幅"); LODOP.SET_PRINT_MODE("PRINT_PAGE_ROTATE", 90); LODOP.ADD_PRINT_TEXT(30, 20, 2000, 40, "旋转打印测试"); LODOP.PREVIEW();参数说明:PRINT_PAGE_ROTATE的旋转角度按 0/90/180/270 传,90 表示逆时针旋转 90 度,适合把横向自定义纸打出竖版内容。边距则由SET_PRINT_MODE的另一组参数控制,比如"PRINT_PAGE_PERCENT"设置缩放比例、"PRINT_PAGE_LEFT"设置左边距。字体方面,SET_PRINT_STYLE("FontSize", 14)和SET_PRINT_STYLE("FontName", "SimSun")是最常用的一组,位置要放在ADD_PRINT_*之后才能只影响当前打印项,放在前面则影响之后所有项。这里的“先后顺序”是 Lodop 里比较容易搞混的规则:SET_PRINT_STYLE是状态型设置,碰到下一个ADD_PRINT_*时继续生效,所以每个打印项前最好都显式设置一遍,避免上一个项的样式污染下一个项。
5. 打印预览常见问题与避坑记录
5.1 现象:控制台报 LODOP is not defined
现象:页面点击预览按钮后,F12 控制台直接报“LODOP is not defined”,后续代码不执行。
原因:最常见是LodopFuncs.js没有被正确引入,或者引入了但业务脚本先于它执行;还有一种情况是业务代码里把LODOP写成了小写lodop,而getLodop()返回的对象是全局属性,大小写不一致自然拿不到。
解决:检查<script src="LodopFuncs.js"></script>是否放在业务脚本之前;业务脚本中使用var LODOP = getLodop()不要自己重新定义var LODOP = {}覆盖掉。如果项目构建工具做了静态资源合并,确认合并顺序里LodopFuncs.js排在业务模块前面。再有一种隐蔽情况是项目里存在两个同名函数,后加载的覆盖了先加载的,控制台输入typeof getLodop确认是否存在。
5.2 现象:getLodop() 返回 null,预览按钮没反应
现象:代码判空逻辑生效,弹了 alert,但用户反馈“我已经装控件了,为什么还提示未启动”。
原因:装了老版 Lodop,但浏览器是非 IE 内核,老版 Lodop 不支持 WebSocket 协议,访问不到本机服务;或者 C-Lodop 服务被安全软件拦截、端口被占用。
解决:先访问http://localhost:8000/CLodopfuncs.js,能打开说明服务在跑,不能打开就是服务没起来。查看任务管理器里是否有CLodopSvc相关进程,没有则重新安装 C-Lodop。另一个排查点是 8000 端口被占用,常见被 Nginx 或本地服务占用,需要改 C-Lodop 配置或停掉冲突进程。判断控件是否安装成功,可以直接看浏览器是否弹出 LODOP 对象可用的提示,或者用官方安装包自带的环境检测页面验证。
5.3 现象:HTTPS 页面下预览弹不出来或一直转圈
现象:页面从 HTTP 切到 HTTPS 后,预览按钮点了没反应,状态栏一直显示连接本机服务。
原因:HTTPS 页面里访问本机 HTTP 服务属于混合内容,浏览器默认拦截;C-Lodop 的 WebSocket 连接地址是ws://而非wss://,在被拦截后无法建立连接。
解决:C-Lodop 新版本支持https://localhost:8000的访问方式,需要在页面部署时把getLodop()内部的地址改掉;如果项目没有条件改地址,就在部署层把打印页面限制在 HTTP 域,或者通过网关反向代理本机端口。这个坑在这个方案里很典型,属于环境层面,跟代码无关,但排错了方向会浪费几个小时。
5.4 现象:预览效果和实际打印结果错位、缩放不一致
现象:在预览窗口里布局是对的,打印到纸张上所有元素整体左移或缩小,尤其是针式打印机。
原因:针式打印机默认的打印区域比 A4 纸小一圈,驱动默认留了非打印边距;另外在 Chrome 预览里调用PREVIEW()时,界面上的缩放比例和真实打印机的 DPI 无关,预览显示的只是近似结果。
解决:在PRINT_SET_PRINT_PAGESIZE里把纸张类型落到打印机驱动支持的具体纸张,比如“A4_EXTRA”或“自定义宽度”并指定完整边到边的尺寸。对偏移量做一个固定修正值,先打印测试页量出偏移,再把所有排版坐标整体加或减修正值。这类问题不要靠肉眼连续调,每次改动只动一个变量,打印一张测试纸标注数字,效率最高。
5.5 现象:条码/二维码在预览里清晰,打印出来模糊或扫不出来
现象:预览窗口里条码线条锋利,但实体打印出来后成一片灰色,扫码枪无法识别。
原因:条码作为位图被拉大或缩小,Lodop 的位图打印在低分辨率打印机上会出现锯齿;也可能是用了ADD_PRINT_IMAGE传入小尺寸图片,却设置了过大的宽高,导致物理分辨率不足。
解决:条码优先用ADD_PRINT_BARCODE接口生成,而不是用图片代替;如果业务一定要用图片,图片原始尺寸必须大于等于打印尺寸对应的像素数。二维码同理,内容较多时要适当放大模块大小并增加纠错级别。这道坎属于硬件和软件配合问题,预览窗口永远是正确的,最终以打印样张为准。
6. 把 LodopFuncs.js 封装成业务无关的打印工具
6.1 一个可复用的封装函数
LodopFuncs.js 本身只是入口,如果业务代码里到处直接调getLodop(),后续换版本、加权限都会很痛苦。我习惯把它封装成独立的打印工具模块,导出两个方法:previewTask()和printTask(),统一处理判空、任务初始化和错误回调。
// printTool.js export function getLodopInstance() { const LODOP = window.getLodop ? window.getLodop() : null; if (!LODOP) { throw new Error("Lodop控件未启动,请先安装"); } return LODOP; } export function previewTask(taskName, setupFn) { const LODOP = getLodopInstance(); LODOP.PRINT_INIT(taskName); if (typeof setupFn === "function") { setupFn(LODOP); } LODOP.PREVIEW(); } export function printTask(taskName, setupFn) { const LODOP = getLodopInstance(); LODOP.PRINT_INIT(taskName); if (typeof setupFn === "function") { setupFn(LODOP); } LODOP.PRINTA(); }逻辑说明:setupFn是回传出去的初始化函数,业务代码在回调里添加ADD_PRINT_*内容,这样封装层和业务层职责分开。参数taskName要保证每次不同,避免连续打印时任务名重复导致预览缓存。这种封装在 Vue、React 工程里都能用,只需要把LodopFuncs.js挂到window上,或者在入口文件里先执行一次全量引入。
6.2 上线前自查三步
我习惯在每次交付打印功能时做三件事:第一,用环境检测页面确认控件和服务双通;第二,在放行打印前手动预览一次,看纸张尺寸是不是目标纸型;第三,把坐标修正值单独写成一个配置文件,不要散落在业务代码里。做打印功能,最怕的就是“预览正常但打印机不认”,这个矛盾只能靠打样解决,没有捷径。
过去踩过的一个教训:某次连续针式打印需求,预览和驱动都正常,最后发现是打印任务名带了特殊字符导致预览缓存错乱,改成纯数字加日期编码后稳定运行。从那以后我养成了一个习惯,任务名里只允许出现字母、数字、中划线。这篇文章里的思路和代码,能帮你把 Lodop 打印预览这条路走得更稳,希望帮到你。
本文还有配套的精品资源,点击获取