您的位置:首页 > 编程语言 > Java开发

Highcharts图表导出为pdf的JavaWeb实践

2016-03-31 17:34 661 查看
写给读者的话^_^:

  众所周知,基于Highcharts插件生成的svg图片组(注意这里鄙人指的组是若干图有序组合,并非一张图片,具有业务意义)导出为PDF文档是有难度滴。鄙人也曾“异想天开”用前端技术拍个快照然后转换为pdf文件导出,后来因为能力有限未能完美实现。因此,参照互联网已有的经验和做法,创造出一套较为有操作性的方案,详情见下文。

  

---------------------------------------------------说正事儿分割线----------------------------------------------------

假设需求如下:

如图所示的复杂图表报告

对其进行PDF导出(demo中所有数据为伪造,并无任何价值)

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: