Highcharts是一款优秀的图表图形工具,支持几乎所有的图表图形,相信很多朋友都在项目中使用过该组件,然而我想我们不仅仅是要用它做展示还要讲分析出来的图表进行导出(word或pdf),本人最近的项目就遇到了这样的一个问题,需要将highcharts生成的图表导出到word和pdf中,于是我就简单的研究了一下,下面是我做的一个Demo;


导出word那一部分我就不多讲了,不了解的可以参考我的前一篇文章()


相关jar包下载地址:


同样是一个SpringMvc的project


svg转换为图片工具类(png)

package com.tgb.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

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;

/**
 *@Description: 将svg转换为png格式的图片
 */
public class SvgPngConverter {
 
    /**
     *@Description: 将svg字符串转换为png
     *@Author:
     *@param svgCode svg代码
     *@param pngFilePath  保存的路径
     *@throws IOException io异常
     *@throws TranscoderException svg代码异常
    */
    public static void convertToPng(String svgCode,String pngFilePath) throws IOException,TranscoderException{

        File file = new File (pngFilePath);

        FileOutputStream outputStream = null;
        try {
            file.createNewFile ();
            outputStream = new FileOutputStream (file);
            convertToPng (svgCode, outputStream);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close ();
                } catch (IOException e) {
                    e.printStackTrace ();
                }
            }
        }
    }
     
    /**
     *@Description: 将svgCode转换成png文件,直接输出到流中
     *@param svgCode svg代码
     *@param outputStream 输出流
     *@throws TranscoderException 异常
     *@throws IOException io异常
     */
    public static void convertToPng(String svgCode,OutputStream outputStream) throws TranscoderException,IOException{
    	try {
            byte[] bytes = svgCode.getBytes ("UTF-8");
            PNGTranscoder t = new PNGTranscoder ();
            TranscoderInput input = new TranscoderInput (new ByteArrayInputStream (bytes));
            TranscoderOutput output = new TranscoderOutput (outputStream);
            t.transcode (input, output);
            outputStream.flush ();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close ();
                } catch (IOException e) {
                    e.printStackTrace ();
                }
            }
        }
    }
}




IText操作word和pdf工具类


package com.tgb.util;

import java.awt.Color;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.rtf.RtfWriter2;
import com.tgb.entity.News;

/**
 * IText操作类
 * @author shyh
 *
 */
public class ItextManager {

	private Font font;
	private BaseFont bfChinese;

	public ItextManager() throws Exception {
		// 设置中文字体
		bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
		font = new Font(bfChinese);
		font.setSize(15);
		font.setStyle(FontFactory.HELVETICA);
//		font.setStyle(Font.BOLD);//加粗
		font.setColor(new Color(0,0,0));
		
	}

	public static ItextManager getInstance() throws Exception {
		return new ItextManager();
	}

	public void createRtfContext(List<News> newsList, List<String> imgList, OutputStream out,String type) {
		Document doc = new Document(PageSize.A4, 20, 20, 20, 20);
		try {
			if("word".equals(type)){
				RtfWriter2.getInstance(doc, out);
			}else if("pdf".equals(type)){
				PdfWriter.getInstance(doc, out);
			}
			doc.open();
			News news = null;
			Paragraph title1 = null;
			for (int i = 0; i < newsList.size(); i++) {
				news = newsList.get(i);
				// 标题
				Paragraph title = new Paragraph(news.getTitle(), font);
				title.setAlignment(Element.ALIGN_LEFT);
				doc.add(title);

				// 换行
				title1 = new Paragraph("\n");
				doc.add(title1);

				// 正文
				Paragraph content = new Paragraph(news.getContent(), font);
				content.setAlignment(Element.ALIGN_LEFT);
				doc.add(content);

				// 换行
				title1 = new Paragraph("\n");
				doc.add(title1);

				// 站点
				Paragraph site = new Paragraph(news.getSite(), font);
				content.setAlignment(Element.ALIGN_LEFT);
				doc.add(site);

				// 换行
				title1 = new Paragraph("\n");
				doc.add(title1);

				// 发布时间
				Paragraph publishTime = new Paragraph(news.getPublishTime(), font);
				content.setAlignment(Element.ALIGN_LEFT);
				doc.add(publishTime);

				// 换行
				title1 = new Paragraph("\n");
				doc.add(title1);

			}

			Image img = null;
			for (int j = 0; j < imgList.size(); j++) {
				// 图片
				img = Image.getInstance(imgList.get(j));
				float heigth = img.getHeight();
				float width = img.getWidth();
				int percent = getPercent2(heigth, width);
				img.setAlignment(Image.MIDDLE);
				img.scalePercent(percent + 3);// 表示是原来图像的比例;
				doc.add(img);
			}

			doc.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 第一种解决方案 在不改变图片形状的同时,判断,如果h>w,则按h压缩,否则在w>h或w=h的情况下,按宽度压缩
	 * 
	 * @param h
	 * @param w
	 * @return
	 */

	public static int getPercent(float h, float w) {
		int p = 0;
		float p2 = 0.0f;
		if (h > w) {
			p2 = 297 / h * 100;
		} else {
			p2 = 210 / w * 100;
		}
		p = Math.round(p2);
		return p;
	}

	/**
	 * 第二种解决方案,统一按照宽度压缩 这样来的效果是,所有图片的宽度是相等的,自我认为给客户的效果是最好的
	 * 
	 * @param args
	 */
	public static int getPercent2(float h, float w) {
		int p = 0;
		float p2 = 0.0f;
		p2 = 530 / w * 100;
		p = Math.round(p2);
		return p;
	}
}





控制层:

@RequestMapping(value="/exportword")
	public String exportWord(HttpServletRequest request,HttpServletResponse response) throws Exception{
		String type = request.getParameter("type");
		response.setContentType("application/octet-stream; charset=UTF-8");  
		if("word".equals(type)){
			response.setHeader("content-disposition", "attachment;filename=" + new SimpleDateFormat("yyyyMMddHH:mm:ss").format(new Date()) + ".doc");
		}else if("pdf".equals(type)){
			response.setHeader("content-disposition", "attachment;filename=" + new SimpleDateFormat("yyyyMMddHH:mm:ss").format(new Date()) + ".pdf");
		}
		
		String svgCode = request.getParameter("svg");//highcharts图表svgCode
		String svg [] = svgCode.split("_");
		String path[] = new String[svg.length];
		OutputStream out = response.getOutputStream();
		ItextManager tm = ItextManager.getInstance();
		List<News> newsList = userService.getData();
		List<String> imageList = new ArrayList<String>();
		
		if(svg!=null){
			for(int k=0;k<svg.length;k++){
				String picName = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())+".png";
				path[k] = request.getSession().getServletContext().getRealPath("/upload/"+picName);
				imageList.add(path[k]);
				SvgPngConverter.convertToPng(svg[k], path[k]);
			}
		}
		
		tm.createRtfContext(newsList,imageList,out,type);
		
		out.flush();
		out.close();
		return null;
	}


注意:我的svg是一个数组,因为我有多个图表,而且我首先将svg转换成图片然后存储到如下路径:

path[k] = request.getSession().getServletContext().getRealPath("/upload/"+picName);

最后,将图片写入到word中(图片数组):

tm.createRtfContext(newsList,imageList,out,type);


业务层

@Override
	public List<News> getData() {
		List<News> newsList = new ArrayList<News>();
		News news1 = new News();
		news1.setTitle("标题:国泰君安*公司研究*广发证券:定增完成,如虎添翼*000776*投资银行业与经纪业行业*梁静");
		news1.setContent("正文:报告类型=公司事件点评公司简称=广发证券公司代码=000776报告日期=Thu Aug 25 09:05:29 CST 2011研究员 =梁静报告标题=定增完成,如虎添翼【报告摘要】8月25日,广发证券成功向揭阳市信宏资产、汇添富基金、上海海博鑫惠、兴业基金等10家机构定向增发4.526亿股、募集资金121.8亿元,募集资金净额120亿元。");
		news1.setSite("站点:新浪网");
		news1.setPublishTime("发布时间:2014-05-12");
		
		News news2 = new News();
		news2.setTitle("标题:[申万销售夏敬慧] 基金仓位周报----开基仓位下降1.51%");
		news2.setContent("正文:理财产品部分析师: 杨鹏(18930809297) 开基仓位有所下降:本周,开放式基金平均仓位继续下降。");
		news2.setSite("站点:腾讯网");
		news2.setPublishTime("发布时间:2014-05-25");
		
		newsList.add(news1);
		newsList.add(news2);
		
		return newsList;
	}




展示层


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>导出word</title>
<script type="text/javascript" src="js/jquery/jquery-1.7.1.js"></script>
<script src="js/highcharts/4.0.1/js/highcharts.js"></script>
<script src="js/highcharts/4.0.1/js/modules/exporting.js"></script>
<script type="text/javascript">
	$(function() {
		Highcharts.wrap(Highcharts.Chart.prototype, 'getSVG', function (proceed) {
		    return proceed.call(this)
		        .replace(
		            /(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, 
		            '$1="rgb($2)" $1-opacity="$3"'
		        );
		});
		
		$('#container').highcharts({
			title : {
				text : 'Monthly Average Temperature',
				x : -20
			//center
			},
			subtitle : {
				text : 'Source: WorldClimate.com',
				x : -20
			},
			xAxis : {
				categories : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
			},
			yAxis : {
				title : {
					text : 'Temperature (°C)'
				},
				plotLines : [ {
					value : 0,
					width : 1,
					color : '#808080'
				} ]
			},
			tooltip : {
				valueSuffix : '°C'
			},
			legend : {
				layout : 'vertical',
				align : 'right',
				verticalAlign : 'middle',
				borderWidth : 0
			},
			series : [ {
				name : 'Tokyo',
				data : [ 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6 ]
			}, {
				name : 'New York',
				data : [ -0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5 ]
			}, {
				name : 'Berlin',
				data : [ -0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0 ]
			}, {
				name : 'London',
				data : [ 3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8 ]
			} ]
		});

		$('#container_pie').highcharts({
			chart : {
				plotBackgroundColor : null,
				plotBorderWidth : null,
				plotShadow : false
			},
			title : {
				text : 'Browser market shares at a specific website, 2010'
			},
			tooltip : {
				pointFormat : '{series.name}: <b>{point.percentage:.1f}%</b>'
			},
			plotOptions : {
				pie : {
					allowPointSelect : true,
					cursor : 'pointer',
					dataLabels : {
						enabled : true,
						color : '#000000',
						connectorColor : '#000000',
						format : '<b>{point.name}</b>: {point.percentage:.1f} %'
					}
				}
			},
			series : [ {
				type : 'pie',
				name : 'Browser share',
				data : [ [ 'Firefox', 45.0 ], [ 'IE', 26.8 ], {
					name : 'Chrome',
					y : 12.8,
					sliced : true,
					selected : true
				}, [ 'Safari', 8.5 ], [ 'Opera', 6.2 ], [ 'Others', 0.7 ] ]
			} ]
		});

	});
</script>
<script type="text/javascript">
	function exportHighcharts(type){
		var chart_line = $("#container").highcharts();
		var chart_pie = $("#container_pie").highcharts();
		var svg_line = chart_line.getSVG();
		var svg_pie = chart_pie.getSVG();
		var svg = svg_line+"_"+svg_pie;
		$("#svg").val(svg);
		$("#form1").prop("action", "exportword.do?type="+type+"").submit();
	}
</script>
</head>
<body>
	<form id="form1" action="exportword.do" method="post">
		<div>
			<input type="hidden" name="svg" id="svg" /> 
			<input id="btn_word" type="button" value="导出word" οnclick="exportHighcharts('word');"/> 
			<input id="btn_pdf" type="button" value="导出pdf" οnclick="exportHighcharts('pdf');"/>
		</div>
	</form>

	<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
	<div id="container_pie" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
</body>
</html>



java 后台生成echarts 图片 java导出echarts图表到word_pdf