Highcharts图表导出为pdf的JavaWeb实践
写给读者的话^_^:
众所周知,基于Highcharts插件生成的svg图片组(注意这里鄙人指的组是若干图有序组合,并非一张图片,具有业务意义)导出为PDF文档是有难度滴。鄙人也曾“异想天开”用前端技术拍个快照然后转换为pdf文件导出,后来因为能力有限未能完美实现。因此,参照互联网已有的经验和做法,创造出一套较为有操作性的方案,详情见下文。
---------------------------------------------------说正事儿分割线----------------------------------------------------
假设需求如下:
- 如图所示的复杂图表报告
- 对其进行PDF导出(demo中所有数据为伪造,并无任何价值)
此图仅作为demo展示,不涉及商业数据,所有数据均为构造假数据
那么问题来了,肿么导出哩,先看下导出后的效果,卖个关子,如下图:
当然,不可否认的是图像质量会打折。但是效果终究实现了。接下来我们去看看前端怎么写,然后提交到后台又如何处理返回一个文档输出流。
- 前端html自定义元素属性,如下:
<div class="timeFenBuPic" id="timeFenBuPic">
<div class="timeFenBuOne" id="timeFenBuOne" softOrHard="hard" position="center" getSvg="true" h4="VR眼镜使用饱和度">
</div>
</div>例如:其中position咱们可以定义给它三个值属性:left,center,right代表了在文档中,每一组svg图的相对位置,其余几个属性自己结合后台程序使用即可。
- 前端html自定义元素属性,如下:
- 前端js脚本获取并且组织svg图像元素并提交给服务端(这里我们用的服务端时Java写的struts2作为控制器层的服务端接口),js写法如下:
function PDFExecute(){
//循环拿到各个绘图区域id
$("#svgPDF").empty();
$.each($("[getSvg='true']"),function(index,ele){
//根据每个绘图区域的id获取svg,position,softOrHard等属性
var svg = $(this).highcharts();
if(typeof(svg)=='undefined'||svg==null){
svg = 'noData';
}else{
svg = svg.getSVG();
}
$("#svgPDF").append("<input id='SVG"+$(this).attr("id")+"' name='svg' type='hidden' value='' />");
$("#SVG"+$(this).attr("id")).val(
$(this).attr("id")+
"___"+$(this).attr("position")+
"___"+encodeURI($(this).attr("h4")+getSvgUnit($(this).parents('li').children('ul').children('li .curr').text()))+
"___"+$(this).attr("softOrHard")+
"___"+svg);
});
$("#svgPDF").append("<input name='logoT' type='hidden' value='"+encodeURI($(".logoT").text())+"' />");
//处理文本锚点异常错误
// $('[text-anchor="undefined"]').attr('text-anchor','');
$("#svgPDF").submit(); } - 服务端处理
pdf导出接口
/**
* PDF导出入口方法
* 参数要求:
* 1.一个页面的title(encode后的)
* 2.所有highcharts的svg
* 3.页面所有查询参数(用于表格类型的数据查询,因为表格类型前端无法传给后台)
* 4.svg详述:
* svg为一个数组
* svg的每个数组元素为字符串,且包含多个信息,以三个连续英文半角的下划线___做split操作,得到数组,具体内容如下:
* 页面每个hicharts图的绘制id___此图在水平方向的相对位置(left还是right)___encode后的每两个图组成的title标题
* (例如xx投放趋势)___此图为软广还是硬广(soft还是hard)___svg字符串用来转换图片输出流
* 因此 svg.split("___")结果为:
* ["charts图id","left/right","xx趋势图","soft/hard","<svg.../>"]
* 5.使用时修改ByteArrayOutputStream方法下参数及布局规则
*/
public String svgPDF(){
try {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
Map<String,Object> map = new HashMap<String,Object>();
String logoT = request.getParameter("logoT");
if(StringUtils.isNotEmpty(logoT)){
logoT = URLDecoder.decode(logoT,"utf-8");
} downloadFileName= URLEncoder.encode(logoT,"utf-8")+".pdf";
String[] svg = request.getParameterValues("svg");
map.put("svg", svg);
map.put("logoT", logoT); //实例化文档绘制工具类
ComprehensivePdfUtil cpu = new ComprehensivePdfUtil(); ByteArrayOutputStream buff = cpu.getPDFStream(request,response,map);
inputStream = new ByteArrayInputStream(buff.toByteArray());
buff.close();
return "success";
} catch (IOException e) {
e.printStackTrace();
return null;
}
}此接口响应来自客户端的http请求并返回输出流
- PDF文档绘制工具类
package com.demo.utils; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder; import com.itextpdf.text.BadElementException;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPRow;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter; /**
* @Description XXX分析页面PDF导出工具方法
*/
public class ComprehensivePdfUtil {
/**
* 获得PDF字节输出流及pdf布局业务逻辑
* @param request
* @param response
* @param resultMap 包含参数:svg(绘图svg参数及hicharts图布局参数) logoT(页面总标题)
* @param list 页面包含植入栏目排行表格图,该list存储绘制表格所用的数据
* @param tableTh 页面包含植入栏目排行表格图,该字符串作为表格表头
* @param tableTd 页面包含植入栏目排行表格图,该字符串作为表格内容填充时,实体类反射值所用的方法名(必须与实体方法严格一致)
* @return
*/
public ByteArrayOutputStream getPDFStream(HttpServletRequest request,
HttpServletResponse response,
Map<String,Object> resultMap){
try {
//图片变量定义
String noData = "/style/images/noData.png";//无数据左右图
String noDataCenter = "/style/images/noDataCenter.png";//无数据中间图
String waterMark = "/style/images/PDFSHUIYIN.png";//PDF导出文件水印图片
String [] svgName = (String[]) resultMap.get("svg");//导出PDF页面所有svg图像
Document document = new Document(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
PdfWriter pdfWriter = PdfWriter.getInstance(document, buffer); //设置页面大小
int pageHeight = 2000;
Rectangle rect = new Rectangle(0,0,1200,pageHeight);
rect.setBackgroundColor(new BaseColor(248,248,248));//页面背景色
document.setPageSize(rect);//页面参数 //页边空白
document.setMargins(20, 20, 30, 20);
document.open(); //设置页头信息
if(null!=resultMap.get("logoT")){
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font FontChinese = new Font(bfChinese,20, Font.BOLD);
Paragraph paragraph = new Paragraph((String)resultMap.get("logoT"),FontChinese);
paragraph.setAlignment(Element.ALIGN_CENTER);
document.add(paragraph);
} PdfPTable table = null;
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; //开始循环写入svg图像到pdf文档对象
for(String str:svgName){
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//positionAndSvg数组中元素说明:
//positionAndSvg[0]表示svg图像所在页面的div的id
//positionAndSvg[1]表示svg图像在水平方向的相对位置:
// 1.left(水平方向两张图,居左且占比50%)
// 2.right(水平方向两张图,居右且占比50%)
// 3.center(水平方向一张图,居中且占比100%)
//positionAndSvg[2]表示svg图像模块的标题如:xxx走势图
//positionAndSvg[3]表示soft/hard即软广图或者硬广图,当无数据时为无数据提示效果图提供判断依据
//positionAndSvg[4]表示svg图像元素,形如<svg...../>
//////////////////////////////////////////////////////////////////////////////////////////////////////////
String[] positionAndSvg = str.split("___"); Image image1 = null;
boolean havaData = true; if("noData".equals(positionAndSvg[4])){//无数据时
image1 = Image.getInstance(basePath+noData);
havaData = false;
}else{//有数据
image1 = Image.getInstance(highcharts(request,response,positionAndSvg[4]).toByteArray());
havaData = true;
} if("left".equals(positionAndSvg[1])){
String title1 = URLDecoder.decode(positionAndSvg[2],"utf-8");
setTitleByCharts(document,30,title1,"",0,87,55,Element.ALIGN_LEFT,headfont);
if(!"cooperateProporOne".equals(positionAndSvg[0])){
setTitleByCharts(document,0,"左图","右图",248,248,248,Element.ALIGN_CENTER,blackTextFont);
}else{
setTitleByCharts(document,0,"","",248,248,248,Element.ALIGN_CENTER,blackTextFont);
}
table = new PdfPTable(2); float[] wid ={0.50f,0.50f}; //列宽度的比例
table.setWidths(wid);
table = PdfPTableImage(table,image1,80f);
}else if("right".equals(positionAndSvg[1])){
table = PdfPTableImage(table,image1,80f);
table.setSpacingBefore(10);
table=setTableHeightWeight(table,360f,1000);
document.add(table);
table = null;
}else if("center".equals(positionAndSvg[1])){//总览全局
String title1 = URLDecoder.decode(positionAndSvg[2],"utf-8");
setTitleByCharts(document,30,title1,"",0,87,55,Element.ALIGN_LEFT,headfont);
setTitleByCharts(document,0,"","",248,248,248,Element.ALIGN_CENTER,blackTextFont);
table = new PdfPTable(1);
float[] wid ={1.00f}; //列宽度的比例
table.setWidths(wid);
if(havaData){
table = PdfPTableImageTable(table,image1,1000f,600f);
}else{
table = PdfPTableImageTable(table,Image.getInstance(basePath+noDataCenter),1000f,600f);
}
table=setTableHeightWeight(table,400f,1000);
document.add(table);
table=null;
}
} //添加水印Start---------------------------------------------------------------------------------------------
PdfFileExportUtil pdfFileExportUtil = new PdfFileExportUtil();
pdfWriter.setPageEvent(pdfFileExportUtil.new PictureWaterMarkPdfPageEvent(basePath+waterMark));
// pdfWriter.setPageEvent(pdfFileExportUtil.new TextWaterMarkPdfPageEvent("xxx科技"));
//添加水印End-----------------------------------------------------------------------------------------------
document.close();
return buffer;
} catch (BadElementException e) {
e.printStackTrace();
return null;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (DocumentException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 设置图片类型Cell属性
* @param table
* @param image1
* @param imgPercent
* @return
* @throws Exception
*/
private PdfPTable PdfPTableImage(PdfPTable table,Image image1,float imgPercent){
table = useTable(table,Element.ALIGN_CENTER);
PdfPCell cellzr = createCellImage(image1,imgPercent);
cellzr.setBorder(0);
cellzr.setBackgroundColor(new BaseColor(248,248,248));
table.addCell(cellzr);
return table;
}
/**
* 设置图片类型Table的Cell属性
* @param table
* @param image1
* @param imgPercentWidth
* @param imgPercentHeight
* @return
* @throws Exception
*/
private PdfPTable PdfPTableImageTable(PdfPTable table,Image image1,float imgPercentWidth,float imgPercentHeight){
table = useTable(table,Element.ALIGN_CENTER);
PdfPCell cellzr = createCellImageTable(image1,imgPercentWidth,imgPercentHeight);
cellzr.setBorder(0);
cellzr.setBackgroundColor(new BaseColor(248,248,248));
table.addCell(cellzr);
return table;
} /**
* 设置表头
* @param document
* @param SpacingBefore
* @param title1
* @param title2
* @param r1
* @param r2
* @param r3
* @param ele
* @param font
* @throws Exception
*/
private void setTitleByCharts(Document document,int SpacingBefore,String title1,String title2,int r1,int r2,int r3,int ele,Font font){
try {
float[] titlewidthsLeft = {0.50f,0.50f};
PdfPTable zrfbtitleTable = createTable(titlewidthsLeft);
PdfPCell cellzr = createCellLeft(title1,font,ele);
cellzr.setBorder(0);
cellzr.setBackgroundColor(new BaseColor(r1,r2,r3));
zrfbtitleTable.addCell(cellzr); PdfPCell cellzr1 = createCellLeft(title2,font,ele);
cellzr1.setBorder(0);
cellzr1.setBackgroundColor(new BaseColor(r1,r2,r3));
zrfbtitleTable.addCell(cellzr1);
zrfbtitleTable.setSpacingBefore(SpacingBefore);
zrfbtitleTable=setTableHeightWeight(zrfbtitleTable,30f,1000); document.add(zrfbtitleTable);
} catch (DocumentException e) {
e.printStackTrace();
}
} /**
* 导出Pdf所用字体静态变量
*/
private static Font headfont ;// title字体
private static Font blackTextFont ;// 黑色字体
private static Font colorfont;
int maxWidth = 500;
static{
BaseFont bfChinese;
try {
bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
headfont = new Font(bfChinese, 15, Font.BOLD);// 设置字体大小
headfont.setColor(BaseColor.WHITE);
blackTextFont = new Font(bfChinese, 11, Font.BOLD);// 设置字体大小
blackTextFont.setColor(BaseColor.BLACK);
colorfont = new Font(bfChinese, 11, Font.NORMAL);// 设置字体大小
colorfont.setColor(BaseColor.RED);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 创建指定内容背景色的Table元素Cell
* @param value
* @param font
* @param c1
* @param c2
* @param c3
* @return
*/
public PdfPCell createCell(String value,Font font,int c1,int c2, int c3){
PdfPCell cell = new PdfPCell();
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setPhrase(new Phrase(value,font));
cell.setBackgroundColor(new BaseColor(c1,c2,c3));
cell.setFixedHeight(33.33f);
cell.setBorder(0);
return cell;
}
/**
* 创建指定位置的Table元素Cell
* @param value
* @param font
* @param ele
* @return
*/
public PdfPCell createCellLeft(String value,Font font,int ele){
PdfPCell cell = new PdfPCell();
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(ele);
cell.setPaddingLeft(10);
cell.setPhrase(new Phrase(value,font));
return cell;
}
/**
* 创建内容为Image的Table元素Cell
* @param image
* @param imgPercent
* @return
*/
public PdfPCell createCellImage(Image image,float imgPercent){
image.scalePercent(imgPercent);
PdfPCell cell = new PdfPCell(image,false);
cell.setUseAscender(true);
cell.setUseDescender(true);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setPaddingLeft(10);
return cell;
}
/**
* 创建table元素cell
* @param image
* @param imgPercentWidth
* @param imgPercentHeight
* @return
*/
public PdfPCell createCellImageTable(Image image,float imgPercentWidth,float imgPercentHeight){
image.scaleAbsoluteWidth(imgPercentWidth);
if(imgPercentHeight==410f){
image.scaleAbsoluteHeight(imgPercentHeight);
} PdfPCell cell = new PdfPCell(image,false);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
return cell;
} /**
* 创建Table
* @param widths 列宽比例
* @return
*/
public PdfPTable createTable(float[] widths){
for(int i=0;i<widths.length;i++){
widths[i] = widths[i]*maxWidth;
}
PdfPTable table = new PdfPTable(widths);
try{
table.setTotalWidth(maxWidth);
table.setLockedWidth(true);
table.setHorizontalAlignment(Element.ALIGN_CENTER);
table.getDefaultCell().setBorder(1);
}catch(Exception e){
e.printStackTrace();
}
return table;
}
/**
* 设置table参数
* @param table
* @param position
* @return
*/
public PdfPTable useTable(PdfPTable table,int position){
try{
table.setTotalWidth(maxWidth);
table.setLockedWidth(true);
table.setHorizontalAlignment(position);
table.getDefaultCell().setBorder(0);
}catch(Exception e){
e.printStackTrace();
}
return table;
} /**
* 设置PdfTable行高
* @param table
* @param maxHeight
* @param maxWidth
* @return
*/
public PdfPTable setTableHeightWeight(PdfPTable table,float maxHeight,float maxWidth){
table.setTotalWidth(maxWidth);
List<PdfPRow> list=new ArrayList<PdfPRow>();
list=table.getRows();
for(PdfPRow pr:list){
pr.setMaxHeights(maxHeight);
}
return table;
} /**
* 根据SVG字符串得到一个输出流
* @param request
* @param response
* @param svg
* @return
* @throws Exception
*/
public ByteArrayOutputStream highcharts(HttpServletRequest request,HttpServletResponse response,String svg){
try {
request.setCharacterEncoding("utf-8");// 注意编码
//转码防止乱码
byte[] arrayStr = svg.getBytes("utf-8");
svg = new String(arrayStr, "UTF-8"); ByteArrayOutputStream stream = new ByteArrayOutputStream(); try {
stream=this.transcode(stream, svg);
} catch (Exception e) {
e.printStackTrace();
}
return stream;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
} /**
* 对svg进行转码
* @param stream
* @param svg
* @return
* @throws Exception
*/
public synchronized ByteArrayOutputStream transcode(ByteArrayOutputStream stream, String svg){
try {
TranscoderInput input = new TranscoderInput(new StringReader(svg)); TranscoderOutput transOutput = new TranscoderOutput(stream); PNGTranscoder transcoder = new PNGTranscoder(); transcoder.transcode(input, transOutput);
return stream;
} catch (TranscoderException e) {
e.printStackTrace();
return null;
}
} }PDF文档绘制工具类
此工具类可以根据前端传来的svg信息,前文中提到的自定义position等属性,布局完成所要输出的PDF文档,因时间有限,不再一一赘述,有想研究的可以下载demo,我已做了一个demo供各位交流学习,下载地址:http://yun.baidu.com/share/link?shareid=2976350494&uk=657798452
服务端处理采用itext作为pdf生成第三方工具包,然后返回一个输出流到前端
Highcharts图表导出为pdf的JavaWeb实践的更多相关文章
- 把页面上的图表导出为pdf文件,分享一种请求下载文件的方法
最近客户提出一个需求,就是把页面上的图表导出为pdf文件. 找了很多资料.终于有了点头绪.最主要是参考了HighCharts的做法.http://www.hcharts.cn/ 实现原理:把页面图表的 ...
- 在asp.net中如何自己编写highcharts图表导出到自己的服务器上来
1.准备工作:网上下载highcharts导出的关键dll. 1).Svg.dll:因为highcharts的格式其实就是一个xml,采用svg的方式画图: 2).itextsha ...
- 基于ITextSharp插件在ASP.NET MVC中将图表导出为PDF
样本: 在这个示例中,我们使用的是微软给我们提供的数据库,也就是家喻户晓的Northwind数据库.要下载Microsoft的免费样本Northwind数据库,您需要访问以下URL.下载Northwi ...
- highCharts图表入门简介
一.Highcharts简介 Highcharts:功能强大.开源.美观.图表丰富.兼容绝大多数浏览器的纯js图表库 Highcharts是一款纯javascript编写的图表库,能够很简单便捷的在W ...
- highcharts自定义导出文件格式(csv) highcharts的一些使用心得
highcharts是国外的一个图表插件,包括各种数据图形展示,柱形图,线性图等等,是手机端和pc端最好的图表插件之一,相比于百度的echarts更加轻便和易懂.链接http://www.hchart ...
- highCharts图表应用-实现多种图表的显示
在数据统计和分析业务中,有时需要在一个图表中将柱状图.饼状图.曲线图的都体现出来,即可以从柱状图中看出具体数据.又能从曲线图中看出变化趋势,还能从饼状图中看出各部分数据比重.highCharts可以轻 ...
- Saiku图表导出时中文显示问题的解决方法
Saiku图表导出时png,jpg,pdf三种格式的中文显示都有问题,目前找到一种不太完善的解决方法(中文可以显示但不清晰),需要修改Saiku项目下的ExporterResource.java文件, ...
- HighCharts 图表高度动态调整
HighCharts 图表高度动态调整 前言 在使用HighCharts控件过程中,发现图表可以自适应div的高度,无法根据图表x.y轴的数量动态调整div高度,否则图标挤在一起,看起来非常不美观,也 ...
- highCharts图表应用-模拟心电图
通过前两章的学习,相信大家对highcharts已经有了初步的了解.这一章将通过一个例子来模拟Highcharts如何实现经常变化的数据显示. 比如说股票的涨停.实时篮球比分以及A选手和B选手的支持率 ...
随机推荐
- IndexReader已解决的问题
设计和实时搜索的发展,IndexReader饮酒数成为0当调用doClose,和SegmentReader再有一个addCoreClosedListener控制的方法SegmentCoreReader ...
- Java String类的比较运算
面试题:(多选)以下返回true的有() A. "beijing" == "beijing" B. "beijing".equals(new ...
- OpenCV2学习笔记(十四):基于OpenCV卡通图片处理
得知OpenCV有一段时间.除了研究的各种算法的内容.除了从备用,据导游书籍和资料,尝试结合链接的图像处理算法和日常生活,第一桌面上(随着摄像头)完成了一系列的视频流处理功能.开发平台Qt5.3.2+ ...
- cocos2d-x3.0 解释具体的新的物理引擎setCategoryBitmask()、setContactTestBitmask()、setCollisionBitmask()
转载请注明出处:游戏开发实验室http://blog.csdn.net/u010019717/article/details/32942641 我在编写游戏的时候遇到了这个问题. 物理引擎其它的内容 ...
- SQL入门学习0-数据库与SQL
1.1 DBMS DatabaseManagermentSystem 数据库管理系统 DBMS种类 层次型数据库(HDB) 最古老的数据库之一,把数据通过层次结构的方式表现. 关系型数据库(RDB) ...
- php小写金额转大写
public static function amountInWords($num) { if (!is_numeric($num) || empty($num)) ...
- JavaEE入境后在做什么——公共入口疑问的答案
hi.大家好, 随着学生毕业的下一个学期,传智播客收集了许多优异的成绩或就业或普通医生分享工作经验,现在是时候让大家从发展的角度真正去聊天. 什么技术开发.我们传智播客的学员扮演什么样的角色,以及详细 ...
- Dev GridView RowCellClick活动MouseDown事件
GridView可编辑.在无声的思想左键点击"进入编辑". 将GridView的OptionsColumn.AllowEdit至false离开时触发RowCellClick. 但有 ...
- 编程获取linuxservercpu、内存和磁盘使用
proc文件系统简介 /proc文件系统是一个伪文件系统.它是唯一的,其中存储器,如果不采取外部存储空间. 它是文件系统提供了与内核进程进行通信的接口的方法.用程序能够通过/proc得到系统的信息.并 ...
- Java内部类详解(转)
说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本 ...