Java后台phantomjs导出复杂图表PDF

6 篇文章 0 订阅

当前导出PDF工具

java后端如何导出像前端用echarts那么漂亮而且还有带有图表的PDF呢,或者图片。目前后端导出word或者excel都是有县城的jar,导出简单的图表也有的简单的jfreechart

总结一下用java生成PDF的方法:
A、itext-PdfStamper pdfStamper(俗称抠模板):代码简单 模板要先提供,且字段长度固定、不灵活
B、itext-Document document(正常代码撰写):模板可根据代码调整、但样式不如C灵活 要维护的后台代码较多,整个过程全手工代码插入表格
C、wkhtmltopdf(使用工具):模板样式可根据前端随意调整,要维护的前台代码较多,原理是后端命令行启动无窗webkit,中文需要依赖字体库,html渲染完成后导出文件,
D、phantomjs和C类似,只是它的侧重点除了导出文件,还可以通过webkit执行js脚本,进行定制化操作,但是目前已经不再更新,一般用于爬虫python中较多。

C,D的原理相同都是使用了webkit渲染html后导出文件

需要依赖的jar和参考文档

A/B: itext-pdfa-5.5.6.jar、itext-xtra-5.5.6.jar、itext-5.5.6.jar、itext-asian.jar 简单模板和表格
C: wkhtmltopdf-0.12.4、python2.7.14、pdfkit 参考文档:https://www.jianshu.com/p/4d65857ffe5e/

wkhtmltopdf --page-height 420mm --page-width 297mm /path/src/edit.html /path/src/edit.pdf

D:https://phantomjs.org/download

#可以直接命令行执行js脚本
phantomjs -v 

phantomjs导出PDF

流程如下

  • 编写html文件
  • 编写phantomjs 的js脚本,主要是打开该文件,并导出图片或者PDF
  • 命令行执行没问题后,可以在Java中执行cmd命令
/***
 * echarts-convert.js
 * https://phantomjs.org/download
 * https://echarts.apache.org/zh/builder.html
 *官网下载http://phantomjs.org/download.html 国内镜像http://npm.taobao.org/dist/phantomjs/
 * EChartsConvert: eharts to PDF
 *auhtor :zhanghl@deepbulue.com
 *time:2020-12-31 14:26
 */


var system = require('system');
var fs = require('fs');
var config = {
    // define the location of js files
    VUE: 'vue.min.js',
    JQUERY: 'jquery-1.9.1.min.js',
    ECHARTS: 'echarts.min.js',
    DEEPBLUE: 'deepblue.js',
    DEFAULT_WIDTH: '740',
    DEFAULT_HEIGHT: 'auto'
}, parseParams, render;


var webPage = require('webpage');
var page = webPage.create();

page.viewportSize = { width: 640,height: 768 };
// page.paperSize = {format: 'A4', orientation: 'portrait',width: '740px', height: "500px", margin: '0px' }
page.paperSize = {
  width: '8.5in',
  height: '11in',
 margin: {
    top: '5px',
    left: '20px'
  },
  header: {
    height: "1cm",
    contents: phantom.callback(function(pageNum, numPages) {
      return "<h5>  <span style='color: #93a1a1;float:right'>deepblueAI-" + pageNum + " / " + numPages + "</span></h5>";
    })
  },
  footer: {
    height: "1cm",
    contents: phantom.callback(function(pageNum, numPages) {
      return "<h5>  <span style='color: #93a1a1;float:right'>deepblueAI-" + pageNum + " / " + numPages + "</span></h5>";
    })
  }
}

page.onLoadFinished = function () {
    console.log("html dom onLoadFinished");
}

var usage = function () {
    console.log("\nUsage: phantomjs echarts-convert.js -url xxx.html -outfile filename -options options" +
        "\n-outfile save file path" +
        "\n-options json str \n");
};

var getParams = function () {
    console.log("getParams for cmd \n");
    var map = {}, i, key;
    if (system.args.length < 2) {
        usage();
        phantom.exit();
    }
    for (i = 0; i < system.args.length; i += 1) {
        if (system.args[i].charAt(0) === '-') {
            key = system.args[i].substr(1, i.length);
            var vals = system.args[i + 1];
            console.log(key + " =  " + vals);
            if (key === 'url') {
                try {
                    map[key] = vals;
                } catch (e) {
                    console.log('Error: cannot find url, ' + system.args[i + 1]);
                    phantom.exit();
                }
            }else if (key === 'outfile') {
                try {
                    map[key] = vals;
                } catch (e) {
                    console.log('Error: cannot find outfile, ' + system.args[i + 1]);
                    phantom.exit();
                }
            } else {
                map[key] = vals;
            }

        }
    }

    console.log("options obj =  " + map[key]);

    return map;
};


function createChart(params) {
    var  options =  eval('(' + params + ')');
    deepblue.init(options);
    return {height: jQuery(document).height(),width : jQuery(document).width(),container : jQuery("#container").width()};
};



//getParams
var paramMap =getParams();

page.open(paramMap.url , function(status) {

    page.injectJs(config.JQUERY);
    page.injectJs(config.ECHARTS);
    page.injectJs(config.DEEPBLUE);


    console.log("http status:"+status);


    // get the dom height
    var viewRect= page.evaluate(createChart, paramMap.options);
    console.log('width ='+viewRect.width+ ' height='+viewRect.height + ' container='+viewRect.container   );
    // page.paperSize.height= height+"px";

    if( status == 'success' ) {

        setTimeout(function(){
            page.clipRect = {
                top : 0,
                left : 0,
                width : 840,
                height : viewRect.height
            };
            // page.render('test/example1.pdf');
            page.render(paramMap.outfile, {format: 'pdf', quality: '50'});
            console.log('success' );
            page.close();
            phantom.exit();
        }, 500);

    }else {
        console.log('render failed' );
        // exit
        phantom.exit();
    }
});

phantomjs /ads-monitor/html/echarts-convert.js -url file:ads-monitor/html/contractDashboard.html -outfile /ads-monitor/pdf/PDF-10-20210104165711.pdf -options {\"groupListData\":[{\"groupId\":22,\"groupName\":\"虹桥机场T1一楼屏幕\",\"materialUrlList\":[{\"materialName\":\"000\",\"materialTags\":\"5555\",\"materialType\":\"picture\",\"materialUrl\":\"http://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/16081164972335.jpg\",\"rid\":72}],\"palanResult\":{\"days\":16,\"nubPerDay\":1,\"totalNub\":24},\"pieData\":[{\"type\":\"normal\",\"value\":\"833.33\"},{\"type\":\"abnormal\",\"value\":\"-733.33\"}],\"planTimeIntervalList\":[{\"endTime\":1606705735000,\"purchaseCount\":10,\"rid\":24,\"startTime\":1605841734000},{\"endTime\":1608267599000,\"purchaseCount\":10,\"rid\":30,\"startTime\":1607961601000},{\"endTime\":1609394916000,\"purchaseCount\":4,\"rid\":37,\"startTime\":1609135712000}],\"progress\":\"12.50\",\"realResult\":{\"abnormalNub\":25,\"abnormalRate\":\"100.00\",\"days\":3,\"normalNub\":0,\"normalRate\":\"0.00\",\"nubPerDay\":0,\"totalNub\":3},\"taskTimeIntervalList\":[{\"endTime\":1608009134000,\"rid\":36,\"startTime\":1608098231000},{\"endTime\":1608220804000,\"rid\":42,\"startTime\":1608015979000},{\"endTime\":1607961600000,\"rid\":50,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":52,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":53,\"startTime\":1608134399000},{\"endTime\":1608134400000,\"rid\":54,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":55,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":56,\"startTime\":1608134399000},{\"endTime\":1608267599000,\"rid\":60,\"startTime\":1607961601000},{\"endTime\":1609394916000,\"rid\":66,\"startTime\":1609135712000}]},{\"groupId\":21,\"groupName\":\"浦东机场T1一楼屏幕\",\"materialUrlList\":[{\"materialName\":\"iphone12-白色\",\"materialTags\":\"白色|iphone\",\"materialType\":\"picture\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/2b13b64ded702c2c.jpg?Expires=1921200484&OSSAccessKeyId=LTAI4GDBhXMfswkafRVMiybj&Signature=dB3GKh9XQ3nVp515NoKOyP1fkVc%3D\",\"rid\":34},{\"materialName\":\"iphone12-黑色\",\"materialTags\":\"黑色|iphone12\",\"materialType\":\"picture\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/681fe3b14009da2f.jpg?Expires=1921200515&OSSAccessKeyId=LTAI4GDBhXMfswkafRVMiybj&Signature=gCgDFY%2B40K7NZW00gf7tDmOgr%2FU%3D\",\"rid\":35},{\"materialName\":\"testbyhanhao\",\"materialTags\":\"test\",\"materialType\":\"picture\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/ct5%E7%94%B7%E5%AD%A9.jpg?Expires=1922168178&OSSAccessKeyId=LTAI4GDBhXMfswkafRVMiybj&Signature=x8rtpIeQqF7XcydyrFhgp0I2M6I%3D\",\"rid\":44},{\"materialName\":\"花屏广告测试1\",\"materialTags\":\"8888\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160811557329912.mp4\",\"rid\":70},{\"materialName\":\"videoTest测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度\",\"materialTags\":\"333\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160914457154710.mp4\",\"rid\":103},{\"materialName\":\"videoTest测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度测试名称长度\",\"materialTags\":\"444\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160914461256915.mp4\",\"rid\":104},{\"materialName\":\"03\",\"materialTags\":\"test\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160861441998817.mp4\",\"rid\":98},{\"materialName\":\"testbyhanhao888\",\"materialTags\":\"888\",\"materialType\":\"picture\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/ct5%E7%94%B7%E5%AD%A9.jpg?Expires=1923121164&OSSAccessKeyId=LTAI4GDBhXMfswkafRVMiybj&Signature=SWFYT0qF4imIEd7FiBDDPVClkt4%3D\",\"rid\":52},{\"materialName\":\"花屏广告测试1\",\"materialTags\":\"6666\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160811543745915.mp4\",\"rid\":69},{\"materialName\":\"360-胡宁\",\"materialTags\":\"360\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160852502804915.mp4\",\"rid\":94},{\"materialName\":\"Linux先行者-冯晓焰\",\"materialTags\":\"linux\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/160851738926817.mp4\",\"rid\":93},{\"materialName\":\"面霸-左耳朵耗子\",\"materialTags\":\"左耳朵耗子\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/16085169580984.mp4\",\"rid\":92},{\"materialName\":\"花屏广告测试1\",\"materialTags\":\"444444444444444444444444444444|88888888888888888888888888888888888\",\"materialType\":\"video\",\"materialUrl\":\"https://degao-oss.oss-cn-shanghai.aliyuncs.com/materials/dev/16091450261533.mp4\",\"rid\":105}],\"palanResult\":{\"days\":16,\"nubPerDay\":1,\"totalNub\":24},\"planTimeIntervalList\":[{\"endTime\":1606705735000,\"purchaseCount\":10,\"rid\":24,\"startTime\":1605841734000},{\"endTime\":1608267599000,\"purchaseCount\":10,\"rid\":30,\"startTime\":1607961601000},{\"endTime\":1609394916000,\"purchaseCount\":4,\"rid\":37,\"startTime\":1609135712000}],\"progress\":\"0\",\"realResult\":{\"abnormalNub\":0,\"abnormalRate\":\"0\",\"days\":0,\"normalNub\":0,\"normalRate\":\"0\",\"nubPerDay\":0,\"totalNub\":0},\"taskTimeIntervalList\":[{\"endTime\":1608009134000,\"rid\":36,\"startTime\":1608098231000},{\"endTime\":1608220804000,\"rid\":42,\"startTime\":1608015979000},{\"endTime\":1607961600000,\"rid\":50,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":52,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":53,\"startTime\":1608134399000},{\"endTime\":1608134400000,\"rid\":54,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":55,\"startTime\":1608134399000},{\"endTime\":1607961600000,\"rid\":56,\"startTime\":1608134399000},{\"endTime\":1608267599000,\"rid\":60,\"startTime\":1607961601000},{\"endTime\":1609394916000,\"rid\":66,\"startTime\":1609135712000}]}],\"headData\":{\"brandZnName\":\"淘宝\",\"consumerZnName\":\"测试创建1\",\"contractNo\":\"100001\",\"hqAriportPosition\":{\"lcdNub\":0,\"ledNub\":3,\"positionTotal\":3},\"pdAriportPosition\":{\"lcdNub\":1,\"ledNub\":7,\"positionTotal\":8},\"positionTotal\":11,\"rid\":10},\"playData\":{\"barOptionList\":[{\"time\":\"20201221\",\"type\":\"normal\",\"value\":0},{\"time\":\"20201221\",\"type\":\"abnormal\",\"value\":11},{\"time\":\"20201225\",\"type\":\"normal\",\"value\":0},{\"time\":\"20201225\",\"type\":\"abnormal\",\"value\":2}],\"lineOptionList\":[{\"time\":\"20201221\",\"value\":10},{\"time\":\"20201225\",\"value\":10}]}}

导出PDF样图如下
在这里插入图片描述

后续

过了一年多了吧,再次看到该文想补充下后续。

虽然导出的PDF样式很漂亮,但是放在服务器上强依赖安装webkit和字体库,对服务器软件有强依赖且导出会影响服务器性能。由于这是在项目中的一种尝试,特别是我们的项目在使用K8S时如果对安装基础软件有依赖,基础镜像都需要安装这些没必要都会使用的依赖,因此在后面的落地中并没有使用该方案。强制前端使用浏览器打印功能导出PDF,避免不必要的性能繁琐和性能开销。[更新于2022-4-15]

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值