# EasyPrint **Repository Path**: camark/easy-print ## Basic Information - **Project Name**: EasyPrint - **Description**: Lodop + JasperReports 打印工具类 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 15 - **Created**: 2020-11-25 - **Last Updated**: 2024-01-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # EasyPrint Lodop + JasperReports 打印模块 ## 前言 ### Lodop Lodop是什么? 有人说她是Web打印控件,因为她能打印、在浏览器中以插件的形式出现,用简单一行语句就把整个网页打印出来; 有人说她是打印编程接口,因为她介于浏览器和打印设备之间,是个通道和桥梁,几乎能想到的打印控制事项都能做到; 有人说她是JavaScript的扩展,因为她所有功能就那么几个语句,和JS语法一样,一看就明白个究竟; 有人说她是报表打印工具,因为那个add_print_table语句把报表统计的那点事弄了个明明白白; 有人说她是条码打印工具,因为用了她再也不用后台生成条码图片了,前端一行指令就动态输出清晰准确的条码,一维二维都行; 有人说她是图表打印工具,因为用她能输出几乎能想象的任何图表,虽然没那么豪华,但什么饼图、折线图、柱图甚至复合图等等都不在话下; 有人说她是个小玩意,因为她体积太小了,才2M多,她所包含的其中任何一个对照工具都是她的好几倍(例如条码打印控件、图表控件等); 有人说她是套打教案,因为以Lodop+JS实现套打这种模式,在网上已被吵吵为教科书般的解决方案; 有人说她是Web打印控件的“终结者”,因为接触“她”后再不想别的“她”; 有人说她就是一个Web编程小工具,因为有了她,在BS下的打印终于像cs下那种随意而高效了; 但我们说,她是全国1000多家软件公司的智慧结晶,诞生10年了,几乎每个功能细节都蕴藏着无数开发者的期待和汗水; 她就是Lodop(读音“劳道谱”),没有别的名称,她是web开发的必选伴侣; 现在,她又添了个小兄弟,名叫C-Lodop(可编程的云打印),未来将由他开创...... 相关链接: * 官网:[跳转地址](http://www.lodop.net/index.html) ### JasperReports JasperReports是一个基于Java的开源报表工具,它可以在Java环境下像其它IDE报表工具一样来制作报表。JasperReports 支持PDF、HTML、XLS、CSV和XML文件输出格式。JasperReports是当前Java开发者最常用的报表工具。 相关链接: * [开发手册](./doc/JasperReport) * [报表jar包](https://community.jaspersoft.com/project/jasperreports-library) * [报表设计器](https://community.jaspersoft.com/project/jaspersoft-studio) ## 项目结构 ``` EasyPrint ├─ main │ ├─ java │ │ └─ cn │ │ └─ sjx │ │ └─ print │ │ ├─ constants │ │ │ └─ PrintConstant.java 常量类 │ │ ├─ entity │ │ │ ├─ HtmlRenderData.java 模板填充数据对象 │ │ │ ├─ HtmlRenderDataSource.java 模板数据来源 枚举类 │ │ │ ├─ PaperStyle.java 纸张样式 枚举类 │ │ │ └─ PrintConfig.java 打印风格对象(如纸张样式,内容边距......) │ │ ├─ Printer.java lodop打印调用类(组装打印代码供前端使用) │ │ ├─ service │ │ │ ├─ B64ImgReplacedElementFactory.java │ │ │ ├─ BaseRenderFactoryHandler.java │ │ │ └─ customer 定制化接口 │ │ │ ├─ impl │ │ │ │ └─ JasperReportRenderHtmlHandlerDefaultImpl.java JasperReport生成html默认实现 │ │ │ ├─ JasperReportParametersCover.java - jasperReport报表渲染数据处理接口 │ │ │ └─ JasperReportRenderHtmlHandler.java - JasperReport生成html处理接口 │ │ └─ util │ │ ├─ HtmlRenderFactoryUtil.java │ │ ├─ PdfRenderFactoryUtil.java │ │ └─ SpringContextHolder.java │ └─ resources │ ├─ config 配置文件目录 │ │ └─ print-config.setting │ ├─ print-template 模板文件目录 │ │ ├─ demo4.html - 测试样例(beetl) │ │ └─ Test_A4.jrxml - 测试样例(JasperReport) │ └─ static │ ├─ lodop_install 打印控件下载路径 │ │ ├─ CLodop_Setup_for_Win32NT.exe │ │ ├─ install_lodop32.exe │ │ └─ install_lodop64.exe │ └─ vendors │ ├─ html2canvas html转图片插件 │ │ ├─ html2canvas.js │ │ └─ html2canvas.min.js │ └─ lodop lodop打印插件 │ ├─ LodopFuncs.js - lodop打印核心代码 │ └─ Lodop_print.js - 自定义调用方法 └─ test DEMO运行示例 ``` ## 开发教程 ### 引入jar包 ```xml cn.sjx EasyPrint 0.01-SNAPSHOT cn.hutool hutool-all ${hutool.version} com.alibaba fastjson ${fastjson.version} com.alibaba druid-spring-boot-starter ${druid.version} ``` ### 安装软件 * [JaspersoftStudio-6.12.2](https://nchc.dl.sourceforge.net/project/jasperstudio/JaspersoftStudio-6.12.2/TIB_js-studiocomm_6.12.2_windows_x86_64.zip) * [Lodop6.226](http://www.lodop.net/download/Lodop6.226_Clodop4.088.zip) *注:JaspersoftStudio使用教程请参考根目录doc文件夹下的pdf文档。iReport是JaspersoftStudio的前身,用法几乎一致。* ### JasperReport模板 在`resource`下新建`print-template`打印模板存放目录。 模板文件如下: Test_A4.jrxml ```xml ``` *注:* *1. 主表中每增加一个子表需要在`Parameters`下新增一个`SUB_REPORT_[0,1,2.....]`的`net.sf.jasperreports.engine.JasperReport`对象参数。* *2. 子表的数据可以定义在`Parameters`下,参数类型为`Map`或`List`* ### 后端代码 关键代码: ```java // 模板渲染 String html = HtmlRenderFactoryUtil.render("Test_A4", new HtmlRenderData(paramMaps, detailList)); System.out.println("==================== JasperReport模板渲染后的Html代码 ===================="); System.out.println(html); // 打印参数配置 PrintConfig printConfig = new PrintConfig(html); // 生成打印控件需要的代码 String str = new Printer(printConfig).print(); System.out.println("==================== Lodop打印javascript脚本语句 ===================="); System.out.println(str); ``` 举个web调用例子: ```java /** * 打印 * * @param id id 记录ID * @return {@link String} */ @PostMapping("{id}/print") public Response print(@PathVariable("id") String id) { Response response = null; try { // 获取待渲染的数据 Map dataMap = printData(id); // 生成html表格 String html = HtmlRenderFactoryUtil.render("hospitality", new HtmlRenderData( MapUtil.get(dataMap, "entertainmentExpensesMap", new TypeReference>() {}), HtmlRenderDataSource.DATA_FROM_COLLECTION_MAP ) ); // 打印配置,如横向/纵向 PrintConfig printConfig = new PrintConfig(html); response = Response.ok(); // new Printer(printConfig).print() 该语句将生成调用Lodop控件的脚本 response.put("expression", new Printer(printConfig).print()); } catch (Exception e) { logger.error("======> 打印出错", e); response = Response.error(e.getMessage()); } return response; } ``` 详细方法用例: ```java package cn.sjx.test; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TimeInterval; import cn.hutool.core.lang.Console; import cn.sjx.print.Printer; import cn.sjx.print.entity.HtmlRenderData; import cn.sjx.print.entity.PrintConfig; import cn.sjx.print.service.customer.JasperReportParametersCover; import cn.sjx.print.service.customer.JasperReportRenderHtmlHandler; import cn.sjx.print.util.HtmlRenderFactoryUtil; import cn.sjx.print.util.PdfRenderFactoryUtil; import com.forte.util.utils.MockUtil; import org.junit.Test; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author sjx * @date 2020年05月18日 0018 15:05:05 */ public class PrintTest { /** * 示例1 * * @throws Exception 异常 */ @Test public void demo1() throws Exception { System.out.println(">>>>>> 示例1"); TimeInterval timer = DateUtil.timer(); Map paramMaps = new HashMap(1); paramMaps.put("TABLE_TITLE", "测试标题"); List> detailList = new ArrayList>(20); for (int i = 0; i < 20; i++) { Map tempMap = new HashMap(); tempMap.put("FIELD_ACCOUNT", MockUtil.title(5)); tempMap.put("FIELD_REAL_NAME", MockUtil.cname()); detailList.add(tempMap); } // 模板渲染 String html = HtmlRenderFactoryUtil.render("Test_A4", new HtmlRenderData(paramMaps, detailList)); System.out.println("==================== JasperReport模板渲染后的Html代码 ===================="); System.out.println(html); // 打印参数配置 PrintConfig printConfig = new PrintConfig(html); // 生成打印控件需要的代码 String str = new Printer(printConfig).print(); System.out.println("==================== Lodop打印javascript脚本语句 ===================="); System.out.println(str); System.out.println("打印运行耗时 " + timer.intervalSecond() + " 秒"); } /** * 示例2 * * @throws Exception 异常 */ @Test public void demo2() { System.out.println(">>>>>> 示例2"); TimeInterval timer = DateUtil.timer(); Map paramMaps = new HashMap(1); paramMaps.put("TABLE_TITLE", "测试•标题"); List> detailList = new ArrayList>(20); for (int i = 0; i < 20; i++) { Map tempMap = new HashMap(); tempMap.put("FIELD_ACCOUNT", MockUtil.title(5) + ">"); tempMap.put("FIELD_REAL_NAME", MockUtil.cname()); detailList.add(tempMap); } // 模板渲染 File pdfFile = PdfRenderFactoryUtil.render("Test_A4", new HtmlRenderData(paramMaps, detailList)); Console.log("======> 文件路径 = {}", pdfFile.getAbsolutePath()); System.out.println("打印运行耗时 " + timer.intervalSecond() + " 秒"); } /** * 示例3 * * @return */ @Test public void demo3() { System.out.println(">>>>>> 示例3"); String s = " 测试标题帐号真实姓名Vtvjo席皂斋Ltoix连雪空Jazsj屈速Tzans谷梁成Eqakb寿呈Izgmw萧菜铣Vizmk隆噶Nhbkv宦譬Hhpzy郇欠放Xyupx蔺换稍Usczd祖玩该Wkfkc韶芥写Qgrvq綦毋恋Dmkmb鲁驯Yzlsc籍仿Nfwkd都筒Nsegj莘喇距Mvnln覃知Tkbkb谈酸Zynak边捷 "; File file = PdfRenderFactoryUtil.htmlToPdf(s); Console.log("======> 文件路径 = {}", file.getAbsolutePath()); } /** * 示例4 * * @throws Exception 异常 */ @Test public void demo4() throws Exception { System.out.println(">>>>>> 示例4"); TimeInterval timer = DateUtil.timer(); Map paramMaps = new HashMap(1); paramMaps.put("title", "测试标题"); paramMaps.put("date", "2020-01-01 至 2020-02-28"); List> dataList = new ArrayList>(3); for (int i = 0; i < 5; i++) { HashMap stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("orgPosition", "测试" + i); stringObjectHashMap.put("opinion", "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试"); stringObjectHashMap.put("createByName", "姓名_" + i); stringObjectHashMap.put("createDateStr", "2020-09-30"); dataList.add(stringObjectHashMap); } paramMaps.put("dataList", dataList); // 模板渲染 String html = HtmlRenderFactoryUtil.render("demo4.html", paramMaps); System.out.println("==================== beetl模板渲染后的Html代码 ===================="); System.out.println(html); // 打印参数配置 PrintConfig printConfig = new PrintConfig(html); // 生成打印控件需要的代码 String str = new Printer(printConfig).print(); System.out.println("==================== Lodop打印javascript脚本语句 ===================="); System.out.println(str); System.out.println("打印运行耗时 " + timer.intervalSecond() + " 秒"); } /** * 示例5 * * @throws Exception 异常 */ @Test public void demo5() throws Exception { System.out.println(">>>>>> 示例5"); TimeInterval timer = DateUtil.timer(); Map paramMaps = new HashMap(1); paramMaps.put("TABLE_TITLE", "测试标题"); List> detailList = new ArrayList>(20); for (int i = 0; i < 5; i++) { Map tempMap = new HashMap(); tempMap.put("FIELD_ACCOUNT", MockUtil.title(5)); tempMap.put("FIELD_REAL_NAME", MockUtil.cname()); detailList.add(tempMap); } // 模板渲染 List htmlList = HtmlRenderFactoryUtil.renderList("Test_A4", new HtmlRenderData(paramMaps, detailList), 4, new JasperReportParametersCover() { @Override public HtmlRenderData cover(HtmlRenderData htmlRenderData) { Map paramMap = htmlRenderData.getParamMap(); paramMap.put("TABLE_TITLE", "测试标题2"); Console.log("======> {}", htmlRenderData.getDataList()); return htmlRenderData; } }); System.out.println("==================== JasperReport模板渲染后的Html代码 ===================="); Console.log(htmlList); // 打印参数配置 PrintConfig printConfig = new PrintConfig(htmlList); System.out.println("==================== Lodop打印javascript脚本语句 ===================="); // 生成打印控件需要的代码 String str = new Printer(printConfig).printList(); System.out.println(str); System.out.println("打印运行耗时 " + timer.intervalSecond() + " 秒"); } /** * 示例6 * * @throws Exception 异常 */ @Test public void demo6() throws Exception { System.out.println(">>>>>> 示例6"); TimeInterval timer = DateUtil.timer(); Map paramMaps = new HashMap(1); paramMaps.put("TABLE_TITLE", "测试标题"); List> detailList = new ArrayList>(20); for (int i = 0; i < 20; i++) { Map tempMap = new HashMap(); tempMap.put("FIELD_ACCOUNT", MockUtil.title(5)); tempMap.put("FIELD_REAL_NAME", MockUtil.cname()); detailList.add(tempMap); } // 模板渲染 String html = HtmlRenderFactoryUtil.render("Test_A4", new HtmlRenderData(paramMaps, detailList), new JasperReportRenderHtmlHandler() { @Override public String handle(String htmlStr) { return "我的关注推荐导航完成我的导航(21)\uE61A\uE611分类显示编辑置顶\uE614\uE612易语言吧\uE614\uE612福州17中吧\uE614\uE612vb吧\uE614\uE612天下电子书天下电子书\uE614\uE612金山快盘金山快盘\uE614\uE612QQ空间\uE614\uE612李毅吧\uE614\uE612BILIBILIbilibili\uE614\uE612纯音乐吧\uE614\uE612百度网盘\uE614\uE612机锋网机锋网\uE614\uE612安智网安智网\uE614\uE612idea_pocket吧\uE614\uE612华为c8812吧\uE614\uE612优酷\uE614\uE612windows8吧\uE614\uE612windows7吧\uE614\uE612编程论坛编程论坛\uE614\uE612淘宝网\uE614\uE612Base64加密解密\uE614\uE612淘宝返利频道\uE60B添加导航{\"dirs\":[{\"dirId\":\"0\",\"dirName\":\"\"}],\"isClassify\":\"0\",\"isSimp\":\"0\",\"hasSimp\":\"\",\"newAdd\":\"\",\"navCount\":\"21\"}\uE60B添加导航您还没有关注任何内容,赶快点击添加吧!添加{\"tplOffSet\":[{\"id\":\"12\",\"name\":\"我的股票\",\"ename\":\"stock\"}],\"tplOnSet\":[{\"id\":\"1\",\"name\":\"我的导航\",\"ename\":\"nav\"}]}\uE60B自定义内容加载中...\uE615{\"data\":[{\"id\":1,\"name\":\"导航\",\"state\":\"1\",\"ismenu\":\"1\"},{\"id\":2,\"name\":\"推荐\",\"state\":\"1\",\"ismenu\":\"1\"},{\"id\":20,\"name\":\"\",\"state\":\"1\",\"ismenu\":\"\"},{\"id\":3,\"name\":\"音乐\",\"state\":\"1\",\"ismenu\":\"1\"},{\"id\":100,\"name\":\"我的关注\",\"state\":\"1\",\"ismenu\":\"0\"}],\"isEnd\":\"0\",\"illegal\":\"\"}扫码下载百度APP有事搜一搜 没事看一看返回顶部"; } }); System.out.println("==================== JasperReport模板渲染后的Html代码 ===================="); System.out.println(html); // 打印参数配置 PrintConfig printConfig = new PrintConfig(html); // 生成打印控件需要的代码 String str = new Printer(printConfig).print(); System.out.println("==================== Lodop打印javascript脚本语句 ===================="); System.out.println(str); System.out.println("打印运行耗时 " + timer.intervalSecond() + " 秒"); } } ``` ### 前端代码 引入javascript插件: ```html ``` 调用方法: ```javascript // html超文本内容打印 let htmlContent = $(layero).find('iframe').eq(0).contents().find('#detail').html(); printPreviewHtml(htmlContent); // 图片打印 html2canvas($(layero).find('iframe').eq(0).contents().find('#detail').get(0)).then(canvas => { // 截图质量(0-1) printPreviewImg(canvas.toDataURL("image/png", 1), '5mm', 5, '99%', '50%'); }); // 模板渲染打印 printPreview("${adminPath}/oa/travelReimbursement/" + rowId + "/print", top.layer); ``` 提供调用的javascript脚本: ```javascript /** * 打印预览,用于后台返回数据打印 * * @param ajaxUrl ajax请求地址 * @param layerAlert layer弹窗组件对象 */ function printPreview(ajaxUrl, layerAlert) { try { // 检查是否安装lodop checkIsInstall(); let loadIndex; $.ajax({ type: "POST", url: ajaxUrl, dataType: "json", beforeSend: function() { // 开启遮罩 loadIndex = layerAlert.msg('打印模板渲染中...', { icon: 16, shade: 0.6, time: 120000 }); }, success: function(data) { // 关闭遮罩 layerAlert.close(loadIndex); if (data.code == 500) { layerAlert.alert("打印异常:" + data.msg, {icon: 2}); return; } // 打印 eval(data.expression); LODOP.PREVIEW(); } }); } catch (e) { console.log(e); } } /** * 打印预览,用于网页内容打印 * * @param htmlContent 超文本标记内容 */ function printPreviewHtml(htmlContent) { try { checkIsInstall(); LODOP.PRINT_INIT("网页内容打印"); LODOP.ADD_PRINT_HTM(0, 0, "100%", "100%", htmlContent); LODOP.PREVIEW(); } catch (e) { console.log(e); } } /** * 打印预览,用于图片打印 * * @param base64ImgStr 图片的base64码 * @param pageTop 上边距 * @param pageLeft 左边距 34px(1px=1/96英寸) * @param imgWidth 图片宽度(默认百分百),支持数值跟百分比 * @param imgHeight 图片高度(默认百分百),支持数值跟百分比 * @param zooming 1:(可变形)扩展缩放模式;2:按原图比例(不变形)缩放模式; */ function printPreviewImg(base64ImgStr, pageTop = '5mm', pageLeft = 34, imgWidth = '100%', imgHeight = '100%', zooming = 2) { try { checkIsInstall(); LODOP.PRINT_INIT("图片打印"); LODOP.ADD_PRINT_IMAGE(pageTop, pageLeft, imgWidth, imgHeight, base64ImgStr); // 按原图比例(不变形)缩放模式 LODOP.SET_PRINT_STYLEA(0, 'Stretch', zooming); LODOP.PREVIEW(); } catch (e) { console.log(e); } } ``` ## test代码执行结果 
您还没有关注任何内容,赶快点击添加吧!