freemarker+Jfreechart生成Word文档(含图片)

这几天再弄一个报表,要统计一些信息最终的部分展示结果如下:

基本工具freemarker,jfreechart

工程的部分结构如下


与生成Word有关的类主要有FreemarkerConfiguration和WordGenerator代码如下:

  1. import com.bqs.ares.common.utils.CommonUtils;  
  2. import freemarker.template.Configuration;  
  3.   
  4. import java.io.File;  
  5. import org.slf4j.Logger;  
  6. import org.slf4j.LoggerFactory;  
  7.   
  8. /** 
  9.  * Created by lenovo on 2016/9/27. 
  10.  */  
  11. public class FreemarkerConfiguration {  
  12.     private static Logger log = LoggerFactory.getLogger(FreemarkerConfiguration.class);  
  13.     private final static String filepath = "/freemarkerTemplate";  
  14.     private static Configuration configuration=null;  
  15.     public static Configuration getConfiguration(){  
  16.         if(configuration==null){  
  17.             configuration=new Configuration();  
  18.             try {  
  19.                 configuration.setDirectoryForTemplateLoading(new File(CommonUtils.class.getResource(filepath).getFile()));  
  20.             }catch (Exception e){  
  21.                 log.error(e.getMessage());  
  22.             }  
  23.         }  
  24.         return  configuration;  
  25.     }  
  26. }  

  1. import com.bqs.risk.dvp.common.PDFUtils.freemarker.FreemarkerConfiguration;  
  2. import freemarker.template.Configuration;  
  3. import freemarker.template.Template;  
  4.   
  5. import java.io.*;  
  6. import java.util.Map;  
  7.   
  8. /**  
  9.  * Created by lenovo on 2016/10/9.  
  10.  */  
  11. public class WordGenerator {  
  12.     /**  
  13.      * Generate html string.  
  14.      *  
  15.      * @param template   the name of freemarker teamlate.  
  16.      * @param variables  the data of teamlate.  
  17.      * @return htmlStr  
  18.      * @throws Exception  
  19.      */  
  20.     public static void generate(String template, Map<String,Object> variables, String htmlName) throws Exception{  
  21.         String basePath=HtmlGenerator.class.getResource("/").getPath()+"/freemarkerTemplate/word/";  
  22.         Configuration config = FreemarkerConfiguration.getConfiguration();  
  23.         config.setDefaultEncoding("UTF-8");  
  24.         Template tp = config.getTemplate(template);  
  25.         tp.setEncoding("UTF-8");  
  26.   
  27.         String htmlPath=basePath+htmlName+".doc";  
  28.         File file = new File(htmlPath);  
  29.         if (!file.exists())  
  30.             file.createNewFile();  
  31.         Writer out = new BufferedWriter(new OutputStreamWriter(  
  32.                 new FileOutputStream(file), "utf-8"));  
  33.         tp.process(variables, out);  
  34.         out.flush();  
  35.         out.close();  
  36.     }  
  37. }  

用jfreeChart生成折线图和饼图的代码如下:

  1. import com.bqs.risk.dvp.common.InfoPoint;  
  2. import org.jfree.chart.ChartFactory;  
  3. import org.jfree.chart.ChartUtilities;  
  4. import org.jfree.chart.JFreeChart;  
  5. import org.jfree.chart.axis.NumberAxis;  
  6. import org.jfree.chart.labels.ItemLabelAnchor;  
  7. import org.jfree.chart.labels.ItemLabelPosition;  
  8. import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;  
  9. import org.jfree.chart.labels.StandardPieSectionLabelGenerator;  
  10. import org.jfree.chart.plot.CategoryPlot;  
  11. import org.jfree.chart.plot.PiePlot;  
  12. import org.jfree.chart.plot.PlotOrientation;  
  13. import org.jfree.chart.renderer.category.LineAndShapeRenderer;  
  14. import org.jfree.chart.title.TextTitle;  
  15. import org.jfree.data.category.DefaultCategoryDataset;  
  16. import org.jfree.data.general.DefaultPieDataset;  
  17. import org.jfree.ui.TextAnchor;  
  18.   
  19. import java.awt.*;  
  20. import java.io.File;  
  21. import java.io.IOException;  
  22. import java.text.NumberFormat;  
  23. import java.util.*;  
  24. import java.util.List;  
  25.   
  26. /** 
  27.  * Created by lenovo on 2016/9/28. 
  28.  */  
  29. public class JfreeChartUtils {  
  30.     private static Map<String,String> relationNameMap=null;  
  31.     private static DefaultCategoryDataset createDataset(int[] data) {  
  32.         DefaultCategoryDataset linedataset = new DefaultCategoryDataset();  
  33.         // 曲线名称  
  34.         String series = "时间-次数";  // series指的就是报表里的那条数据线  
  35.         //因此 对数据线的相关设置就需要联系到serise  
  36.         //比如说setSeriesPaint 设置数据线的颜色  
  37.         // 横轴名称(列名称)  
  38.         String[] time = new String[24];  
  39.   
  40.         for (int i = 0; i < 24; i++) {  
  41.             time[i] = i + "";  
  42.         }  
  43.         //添加数据值  
  44.         for (int i = 0; i < data.length; i++) {  
  45.             linedataset.addValue(data[i],  //值  
  46.                     series,  //哪条数据线  
  47.                     time[i]); // 对应的横轴  
  48.         }  
  49.         return linedataset;  
  50.   
  51.     }  
  52.   
  53.     //生成事件统计图  
  54.     public static String createChart(String eventTypevalue, int[] data, String imageName) {  
  55.         String returnImagePath = "";  
  56.         Calendar calendar = Calendar.getInstance();  
  57.         calendar.setTime(new Date());  
  58.         calendar.add(Calendar.MONTH, -1);  
  59.         if (data == null || data.length <= 0) {  
  60.             return null;  
  61.         }  
  62.         if (imageName == null || imageName.equals("")) {  
  63.             return null;  
  64.         }  
  65.         try {  
  66.             //定义图标对象  
  67.             JFreeChart chart = ChartFactory.createLineChart(null,// 报表题目,字符串类型  
  68.                     "时间"// 横轴  
  69.                     "次数"// 纵轴  
  70.                     createDataset(data), // 获得数据集  
  71.                     PlotOrientation.VERTICAL, // 图标方向垂直  
  72.                     false// 显示图例  
  73.                     false// 不用生成工具  
  74.                     false // 不用生成URL地址  
  75.             );  
  76.             chart.setTitle(calendar.get(Calendar.YEAR) + "年" + (calendar.get(Calendar.MONTH) + 1) + "月" + eventTypevalue + "统计 ");  
  77.             chart.setTitle(new TextTitle(chart.getTitle().getText(),new Font("宋体"113)));  
  78.             //整个大的框架属于chart  可以设置chart的背景颜色  
  79.             // 生成图形  
  80.             CategoryPlot plot = chart.getCategoryPlot();  
  81.             // 图像属性部分  
  82.             plot.setBackgroundPaint(Color.WHITE);  
  83.             plot.setDomainGridlinesVisible(true);  //设置背景网格线是否可见  
  84.             plot.setDomainGridlinePaint(Color.BLACK); //设置背景网格线颜色  
  85.             plot.setRangeGridlinePaint(Color.WHITE);  
  86.             plot.setNoDataMessage("没有数据");//没有数据时显示的文字说明。  
  87.             // 数据轴属性部分  
  88.             NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();  
  89.             rangeAxis.setLabelFont(new Font("宋体"112));  
  90.             rangeAxis.setTickLabelFont((new Font("宋体"112)));  
  91.             rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());  
  92.             rangeAxis.setAutoRangeIncludesZero(true); //自动生成  
  93.             rangeAxis.setUpperMargin(0.20);  
  94.             rangeAxis.setLabelAngle(Math.PI / 2.0);  
  95.             rangeAxis.setAutoRange(false);  
  96.             // 数据渲染部分 主要是对折线做操作  
  97.             LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();  
  98.             renderer.setBaseItemLabelsVisible(true);  
  99.             renderer.setSeriesPaint(0, Color.CYAN);    //设置折线的颜色  
  100.             renderer.setBaseShapesFilled(true);  
  101.             renderer.setBaseItemLabelsVisible(true);  
  102.             renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_LEFT));  
  103.             renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());  
  104.             renderer.setBaseItemLabelFont(new Font("Dialog"110));  //设置提示折点数据形状  
  105.             plot.setRenderer(renderer);  
  106.             // 创建文件输出流  
  107.             String imagePath = JfreeChartUtils.class.getResource("/").getPath() + "/freemarkerTemplate/images/" + imageName + ".jpg";  
  108.             File fos_jpg = new File(imagePath);  
  109.             if (!fos_jpg.exists()) {  
  110.                 fos_jpg.createNewFile();  
  111.             }  
  112.             // 输出到哪个输出流  
  113.             ChartUtilities.saveChartAsJPEG(fos_jpg, chart, // 统计图表对象  
  114.                     1100// 宽  
  115.                     400 // 高  
  116.             );  
  117.             returnImagePath = imagePath;  
  118.         } catch (IOException e) {  
  119.             e.printStackTrace();  
  120.         }  
  121.         return returnImagePath;  
  122.     }  
  123.   
  124.   
  125.     /** 
  126.      * 用户设备关联图,每一张图有三条线 
  127.      * 
  128.      * @param titleName: 
  129.      * @param relatioContentList 
  130.      * @return 图片的地址 
  131.      */  
  132.     public static String createChart(String imageName, String titleName, Map<String, Map<String, String>> relatioContentList) {  
  133.   
  134.         String imageFilePath = "";  
  135.         Calendar calendar = Calendar.getInstance();  
  136.         calendar.setTime(new Date());  
  137.         calendar.add(Calendar.MONTH, -1);  
  138.         //定义图标对象  
  139.         try {  
  140.             JFreeChart chart = ChartFactory.createLineChart(null,// 报表题目,字符串类型  
  141.                     "数量"// 横轴  
  142.                     "计数"// 纵轴  
  143.                     createDataset(relatioContentList), // 获得数据集  
  144.                     PlotOrientation.VERTICAL, // 图标方向垂直  
  145.                     true// 显示图例  
  146.                     false// 不用生成工具  
  147.                     false // 不用生成URL地址  
  148.             );  
  149.             chart.setTitle(calendar.get(Calendar.YEAR) + "年" + (calendar.get(Calendar.MONTH) + 1) + "月" + titleName + "统计 ");  
  150.             chart.setTitle(new TextTitle(chart.getTitle().getText(),new Font("宋体"113)));  
  151.             //整个大的框架属于chart  可以设置chart的背景颜色  
  152.             // 生成图形  
  153.             CategoryPlot plot = chart.getCategoryPlot();  
  154.             // 图像属性部分  
  155.             plot.setBackgroundPaint(Color.WHITE);  
  156.             plot.setDomainGridlinesVisible(true);  //设置背景网格线是否可见  
  157.             plot.setDomainGridlinePaint(Color.BLACK); //设置背景网格线颜色  
  158.             plot.setRangeGridlinePaint(Color.WHITE);  
  159.             plot.setNoDataMessage("没有数据");//没有数据时显示的文字说明。  
  160.             // 数据轴属性部分  
  161.             NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();  
  162.             rangeAxis.setLabelFont((new Font("宋体"112)));  
  163.             rangeAxis.setTickLabelFont((new Font("宋体"112)));  
  164.             rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());  
  165.             rangeAxis.setAutoRangeIncludesZero(true); //自动生成  
  166.             rangeAxis.setUpperMargin(0.20);  
  167.             rangeAxis.setLabelAngle(Math.PI / 2.0);  
  168.             rangeAxis.setAutoRange(false);  
  169.             // 数据渲染部分 主要是对折线做操作  
  170.             LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();  
  171.             renderer.setBaseItemLabelsVisible(true);  
  172.             renderer.setSeriesPaint(0, Color.CYAN);    //设置折线的颜色  
  173.             renderer.setBaseShapesFilled(true);  
  174.             renderer.setBaseItemLabelsVisible(true);  
  175.             renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_LEFT));  
  176.             renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());  
  177.             renderer.setBaseItemLabelFont(new Font("Dialog"110));  //设置提示折点数据形状  
  178.             plot.setRenderer(renderer);  
  179.             // 创建文件输出流  
  180.             imageFilePath = JfreeChartUtils.class.getResource("/").getPath() + "/freemarkerTemplate/images/" + imageName + ".jpg";  
  181.             File fos_jpg = new File(imageFilePath);  
  182.             if (!fos_jpg.exists()) {  
  183.                 fos_jpg.createNewFile();  
  184.             }  
  185.             // 输出到哪个输出流  
  186.             ChartUtilities.saveChartAsJPEG(fos_jpg, chart, // 统计图表对象  
  187.                     1100// 宽  
  188.                     400 // 高  
  189.             );  
  190.         } catch (IOException e) {  
  191.             e.printStackTrace();  
  192.         }  
  193.         return imageFilePath;  
  194.     }  
  195.   
  196.     private static DefaultCategoryDataset createDataset(Map<String, Map<String, String>> relatioContentList) {  
  197.         if(relationNameMap==null){  
  198.             relationNameMap=new HashMap<>();  
  199.            //此处省略  
  200.            
  201.         }  
  202.         DefaultCategoryDataset linedataset = new DefaultCategoryDataset();  
  203.         for (Map.Entry<String, Map<String, String>> relation : relatioContentList.entrySet()) {  
  204.             //得到hbase中一列数据就是一条线  
  205.             String relationName =relationNameMap.get(relation.getKey());  
  206.             Map<String, String> relationContent = relation.getValue();  
  207.   
  208.             List<Point> points = new ArrayList<>();//点  
  209.             for (Map.Entry<String, String> numCounts : relationContent.entrySet()) {  
  210.                 int x = Integer.parseInt(numCounts.getKey());  
  211.                 int y = Integer.parseInt(numCounts.getValue());  
  212.                 Point p = new Point(x, y);  
  213.                 points.add(p);  
  214.             }  
  215.             points.sort((p1, p2) -> p1.getX() - p2.getX());  
  216.             //添加数据值  
  217.             if (points != null && points.size() > 0) {  
  218.                 for (Point p : points) {  
  219.                     linedataset.addValue(p.getY(),  //值  
  220.                             relationName,//哪条数据线  
  221.                             p.getX() + ""); // 对应的横轴  
  222.                 }  
  223.             }  
  224.         }  
  225.         return linedataset;  
  226.     }  
  227.   
  228.     public static List<InfoPoint> getTop20Points(Map<String, String> values) {  
  229.         if (values == null || values.size() < 0) {  
  230.             return null;  
  231.         }  
  232.         List<InfoPoint> pointList = new ArrayList<>();  
  233.         for (Map.Entry<String, String> value : values.entrySet()) {  
  234.             String info = value.getKey();  
  235.             int num = Integer.parseInt(value.getValue());  
  236.             InfoPoint infoPoint = new InfoPoint(info, num);  
  237.             pointList.add(infoPoint);  
  238.         }  
  239.         pointList.sort((p1, p2) -> p2.getNum() - p1.getNum());  
  240.         if (pointList.size() > 20) {  
  241.             for (int i = 20; i < pointList.size(); i++) {  
  242.                 pointList.remove(i);  
  243.             }  
  244.         }  
  245.         return pointList;  
  246.     }  
  247.     //生成城市分布的饼图  
  248.     public static String creatLocationPieChart(String imageName, String typeInfo, Map<String, String> locationInfo) {  
  249.         if (locationInfo == null || locationInfo.size() <= 0) {  
  250.             return null;  
  251.         }  
  252.         String imageFilePath="";  
  253.         try {  
  254.             //设置饼图数据集  
  255.             DefaultPieDataset dataset = new DefaultPieDataset();  
  256.             List<InfoPoint> infoPoints=new ArrayList<>();  
  257.             for (Map.Entry<String, String> info : locationInfo.entrySet()) {  
  258.                 String city = info.getKey();  
  259.                 int num = Integer.parseInt(info.getValue());  
  260.                 InfoPoint point=new InfoPoint();  
  261.                 point.setInfo(city);  
  262.                 point.setNum(num);  
  263.                 infoPoints.add(point);  
  264.             }  
  265.             if(infoPoints!=null&&infoPoints.size()>8){  
  266.                 infoPoints=infoPoints.subList(0,8);  
  267.             }  
  268.             for(InfoPoint infoPoint:infoPoints){  
  269.                 dataset.setValue(infoPoint.getInfo(),infoPoint.getNum());  
  270.             }  
  271.             //通过工厂类生成JFreeChart对象  
  272.             JFreeChart chart = ChartFactory.createPieChart(typeInfo + "分布图", dataset, truetruefalse);  
  273.             chart.setTitle(new TextTitle(chart.getTitle().getText(),new Font("宋体"113)));  
  274.             //加个副标题  
  275.             PiePlot pieplot = (PiePlot) chart.getPlot();  
  276.             pieplot.setLabelFont(new Font("宋体"011));  
  277.   
  278.             //设置饼图是圆的(true),还是椭圆的(false);默认为true  
  279.             pieplot.setCircular(true);  
  280.             StandardPieSectionLabelGenerator standarPieIG = new StandardPieSectionLabelGenerator("{0}:({1},{2})", NumberFormat.getNumberInstance(), NumberFormat.getPercentInstance());  
  281.             pieplot.setLabelGenerator(standarPieIG);  
  282.             //没有数据的时候显示的内容  
  283.             pieplot.setNoDataMessage("无数据显示");  
  284.             pieplot.setLabelGap(0.02D);  
  285.             imageFilePath = JfreeChartUtils.class.getResource("/").getPath() + "/freemarkerTemplate/images/" + imageName + ".jpg";  
  286.             File fos_jpg = new File(imageFilePath);  
  287.             if (!fos_jpg.exists()) {  
  288.                 fos_jpg.createNewFile();  
  289.             }  
  290.             // 输出到哪个输出流  
  291.             ChartUtilities.saveChartAsJPEG(fos_jpg, chart, // 统计图表对象  
  292.                     800// 宽  
  293.                     800 // 高  
  294.             );  
  295.         }catch (Exception e){  
  296.             e.printStackTrace();  
  297.         }  
  298.         return imageFilePath;  
  299.     }  
  300. }  
  301.   
  302. //折线中的点  
  303. class Point {  
  304.     private int x;  
  305.     private int y;  
  306.   
  307.     public Point() {  
  308.     }  
  309.   
  310.     public Point(int x, int y) {  
  311.         this.x = x;  
  312.         this.y = y;  
  313.     }  
  314.   
  315.     public int getX() {  
  316.         return x;  
  317.     }  
  318.   
  319.     public void setX(int x) {  
  320.         this.x = x;  
  321.     }  
  322.   
  323.     public int getY() {  
  324.         return y;  
  325.     }  
  326.   
  327.     public void setY(int y) {  
  328.         this.y = y;  
  329. <span style="font-size:14px;">    }  
  330. }  
  331. </span>  
图片得到之后就是生成Word了,基本思路是先用Word做好模板,另存为xml,然后使用freemarker标签来替换,最后改成ftl文件就是模板,图片也只是替换那一大堆编码而已
  1. <w:pict>  
  2.              <w:binData w:name="wordml://03000003${imagePath_index}.png" xml:space="preserve">${imagePath}</w:binData>  
  3.                <v:shape id="_x0000_i1028" type="#_x0000_t75" style="width:440pt;height:440pt">  
  4.                    <v:imagedata src="wordml://03000003${imagePath_index}.png" o:title="6350.tmp"/>  
  5.                </v:shape>  
  6.            </w:pict>  

但这里确实花了我很多时间,主要的坑如下:

     1.freemarker标签的使用:尤其要注意是否为空的情况所以对于list还是map最好都加上这一句:

  1. <#if infoLocationList??&&infoLocationList?size gt 0>  
是否存在,至于具体的循环遍历网上有不再赘述

     2.Word模板另存为xml文件时一定要注意,如果是较高一点的版本有两种格式xml和2003xml这个在处理图片时有较大差别,我是使用2003xml版本(高版本的在处理图片时有错误),它在处理的时候需要将图片转成base64码具体代码如下:

  1. private String getImageStr(String imagePath) {  
  2.        String imgFile = imagePath;  
  3.        InputStream in = null;  
  4.        byte[] data = null;  
  5.        try {  
  6.            in = new FileInputStream(imgFile);  
  7.            data = new byte[in.available()];  
  8.            in.read(data);  
  9.            in.close();  
  10.        } catch (IOException e) {  
  11.            e.printStackTrace();  
  12.        }  
  13.        BASE64Encoder encoder = new BASE64Encoder();  
  14.        return encoder.encode(data);  
  15.    }  

  3.多张图片插入时会遇到图片重复记得改name和src,从List中取出数据并且实现插入多张图片:

  1. <w:p wsp:rsidR="00C46D75" wsp:rsidRDefault="00C46D75" wsp:rsidP="00AC571C"/>  
  2.         <#if infoLocationList??&&infoLocationList?size gt 0>  
  3.             <w:p wsp:rsidR="0051625F" wsp:rsidRDefault="007F46BC" wsp:rsidP="00AC571C">  
  4.           <w:r>  
  5.             <w:rPr>  
  6.               <w:rFonts w:hint="fareast"/>  
  7.               <wx:font wx:val="宋体"/>  
  8.             </w:rPr>  
  9.             <w:t>半年内手机号、身份证号、IP、GPS归属地信息分布</w:t>  
  10.           </w:r>  
  11.         </w:p>  
  12.             <#list infoLocationList as imagePath>  
  13.                 <w:p wsp:rsidR="007F46BC" wsp:rsidRDefault="00E75FA4" wsp:rsidP="00AC571C">  
  14.           <w:r>  
  15.             <w:tab/>  
  16.           </w:r>  
  17.           <w:r wsp:rsidR="00946545" wsp:rsidRPr="00946545">  
  18.             <w:rPr>  
  19.               <w:noProof/>  
  20.             </w:rPr>  
  21.             <w:pict>  
  22.               <w:binData w:name="wordml://03000003${imagePath_index}.png" xml:space="preserve">${imagePath}</w:binData>  
  23.                 <v:shape id="_x0000_i1028" type="#_x0000_t75" style="width:440pt;height:440pt">  
  24.                     <v:imagedata src="wordml://03000003${imagePath_index}.png" o:title="6350.tmp"/>  
  25.                 </v:shape>  
  26.             </w:pict>  
  27.           </w:r>  
  28.         </w:p>  
  29.             </#list>  
  30.         </#if>  
  4.如果模板太大转成的xml文件太大,看着是密密麻麻的一大片根本无法进一步处理,建议使用IDE进行代码格式化,想eclipse等等一般都可以,如果发现工具也无法格式化可以直接百度xml格式化,然后会有在线的工具

  5.有时候会遇到成功生成Word文档但是无法打开的情况,这时候可以根据错误提示用文本编辑工具来具体到哪一行去看看,如果遇到什么结束元素标签名称与开始标签名称不匹配多半是ftl中标签配对有问题(这个有时候即使没有改过也会出错,所以最好借助工具好好补齐标签)

 6.乱码问题,主要是在Linux服务器上会出现,具体方法网上也有。

FROM:http://blog.csdn.net/my_sunshine_y/article/details/52773528

猜你喜欢

转载自blog.csdn.net/envinfo2012/article/details/78213123