精华内容
下载资源
问答
  • echarts 生成统计图时,自动保存图片到服务器,并使用freemarker生成带图片和循环表格的word
  • 用java实现word统计报表和图形统计的导出
  • .net生成word报表

    2016-08-08 14:00:33
    在系统里,直接引用该文件,可以进行word报表的制作,往里面添加 文字、统计图表等。这条、引用的COM组件版本 C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\14.0.0.0__71e9bce111e9429c\Microsoft....
  • 统计图片在word文档的生成 方法有两种,一种是生成图片在客户端放入word文档,另一种是直接将图片的64编码放入word文档当中
  • NULL 博文链接:https://chaoyi.iteye.com/blog/2173710
  • POI和JFreeChart框架实现生成Excel文件(生成 统计图
  • 在工作中经常用到会有一些生成统计报告、请假等word的功能需求,小编之前做了一些报表的生成,使用过freemarker和poi,但是使用freemarker生成word有一些麻烦的点: 需要先将模板word转化为xml,而且在模板word中写...

    先给大家一个效果图:

    左侧是word模板,右侧是生成后的word文档。

     

    在工作中经常用到会有一些生成统计报告、请假等word的功能需求,小编之前做了一些报表的生成,使用过freemarker和poi,但是使用freemarker生成word有一些麻烦的点:

    1. 需要先将模板word转化为xml,而且在模板word中写好的占位符${obj}也会在转化为xml后被拆分开,还需要人工处理一次
    2. 在表格循环的时候,需要使用freemarker的<#list list as item>标签进行遍历
    3. 统计图表(饼图、折线图、柱状图等),图片适配度地,操作麻烦,图表需要人工修改大量xml中的标签已达到动态修改图表数据的效果,而word转为xml时,图片是已base64进行存储,在模板替换中我们需要将图片的base64码替换为占位符

    这就意味着使用freemarker需要我们预先编写好word模板再转化为xml,再对xml进行freemaker的标签、占位符等的处理后才能进行word生成,而且图表支持度很低。

    这里小编使用easypoi+jfree的形式进行word生成,使用jfree生成统计图表(饼图、折线图、柱状图等)图片,使用easypoi进行占位符替换、表格循环、图片插入已达到根据word模板生成word的效果。通过这个方式生成word只需要预先编写好word模板就可以进行word生成。

    但这个解决方案有一种缺点就是只支持07以后的word,也就是后缀为.docx的word文档。

    下面我就以Spring Boot项目为例举一个例子:

    开发工具:IntelliJ IDEA

    JDK:1.8

    以及项目目录结构:

    1、添加依赖:

            <dependency>
    			<groupId>cn.afterturn</groupId>
    			<artifactId>easypoi-base</artifactId>
    			<version>4.1.0</version>
    		</dependency>
    		<dependency>
    			<groupId>org.jfree</groupId>
    			<artifactId>jcommon</artifactId>
    			<version>1.0.24</version>
    		</dependency>
    		<dependency>
    			<groupId>org.jfree</groupId>
    			<artifactId>jfreechart</artifactId>
    			<version>1.5.0</version>
    		</dependency>

    2、编写jfreeutil工具类

    作为例子,我只封装了将图片转为字节数组和根据具有生成饼状图Image实体的两个方法,各位自行参考

    import cn.afterturn.easypoi.entity.ImageEntity;
    import lombok.extern.slf4j.Slf4j;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartUtils;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.StandardChartTheme;
    import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
    import org.jfree.chart.plot.PiePlot;
    import org.jfree.data.general.DefaultPieDataset;
    import org.springframework.util.Assert;
    import java.awt.*;
    import java.io.*;
    import java.util.Map;
    
    /**
     * @author 何昌杰
     *
     * 参考API博客 https://blog.csdn.net/dagecao/article/details/86536680
     */
    @Slf4j
    public class JfreeUtil {
    
        private static String tempImgPath="D:\\tempJfree.jpeg";
    
        /**
         * 将图片转化为字节数组
         * @return 字节数组
         */
        private static byte[] imgToByte(){
            File file = new File(tempImgPath);
            byte[] buffer = null;
            try {
                FileInputStream fis = new FileInputStream(file);
                ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
                byte[] b = new byte[1000];
                int n;
                while ((n = fis.read(b)) != -1) {
                    bos.write(b, 0, n);
                }
                fis.close();
                bos.close();
                buffer = bos.toByteArray();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
            //删除临时文件
            file.delete();
            return buffer;
        }
    
        public static ImageEntity pieChart(String title, Map<String, Integer> datas, int width, int height) {
    
            //创建主题样式
            StandardChartTheme standardChartTheme = new StandardChartTheme("CN");
            //设置标题字体
            standardChartTheme.setExtraLargeFont(new Font("宋体", Font.BOLD, 20));
            //设置图例的字体
            standardChartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 15));
            //设置轴向的字体
            standardChartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 15));
            //设置主题样式
            ChartFactory.setChartTheme(standardChartTheme);
    
            //根据jfree生成一个本地饼状图
            DefaultPieDataset pds = new DefaultPieDataset();
            datas.forEach(pds::setValue);
            //图标标题、数据集合、是否显示图例标识、是否显示tooltips、是否支持超链接
            JFreeChart chart = ChartFactory.createPieChart(title, pds, true, false, false);
            //设置抗锯齿
            chart.setTextAntiAlias(false);
            PiePlot plot = (PiePlot) chart.getPlot();
            plot.setNoDataMessage("暂无数据");
            //忽略无值的分类
            plot.setIgnoreNullValues(true);
            plot.setBackgroundAlpha(0f);
            //设置标签阴影颜色
            plot.setShadowPaint(new Color(255,255,255));
            //设置标签生成器(默认{0})
            plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}({1})/{2}"));
            try {
                ChartUtils.saveChartAsJPEG(new File(tempImgPath), chart, width, height);
            } catch (IOException e1) {
                log.error("生成饼状图失败!");
            }
            ImageEntity imageEntity = new ImageEntity(imgToByte(), width, height);
            Assert.notNull(imageEntity.getData(),"生成饼状图对象失败!");
            return imageEntity;
        }
    }

    关于jfree的一些API接口,和图形图表属性设置各位可参考这篇博客

    注:@Slf4j注解需要添加lombok的依赖

    3、编写wordutil工具类

    import cn.afterturn.easypoi.word.WordExportUtil;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;
    import org.springframework.util.Assert;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.util.Map;
    
    /**
     * @author 何昌杰
     */
    public class WordUtil {
        /**
         * 导出word
         * <p>第一步生成替换后的word文件,只支持docx</p>
         * <p>第二步下载生成的文件</p>
         * <p>第三步删除生成的临时文件</p>
         * 模版变量中变量格式:{{foo}}
         *
         * @param templatePath word模板地址
         * @param temDir       生成临时文件存放地址
         * @param fileName     文件名
         * @param params       替换的参数
         */
        public static void exportWord(String templatePath, String temDir, String fileName, Map<String, Object> params) {
            Assert.notNull(templatePath, "模板路径不能为空");
            Assert.notNull(temDir, "临时文件路径不能为空");
            Assert.notNull(fileName, "导出文件名不能为空");
            Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式");
            if (!temDir.endsWith("/")) {
                temDir = temDir + File.separator;
            }
            File dir = new File(temDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            try {
                XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
                String tmpPath = temDir + fileName;
                FileOutputStream fos = new FileOutputStream(tmpPath);
                doc.write(fos);
                fos.flush();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    4、编写word模板

    关于word中的更多表达式各位可参考easypoi文档的3节内容。

    5、编写测试类

    import java.util.ArrayList;
    import java.util.HashMap;
    import cn.afterturn.easypoi.entity.ImageEntity;
    import com.springclouddemo.freemarkerdemo.utils.JfreeUtil;
    import com.springclouddemo.freemarkerdemo.utils.WordUtil;
    
    /**
     * @author 何昌杰
     */
    public class WordDemo1 {
    
        public static void main(String[] args) {
            HashMap<String, Object> map = new HashMap<>(4);
    
            //模拟饼状图数据
            HashMap<String, Integer> datas = new HashMap<>(3);
            datas.put("一号",10);
            datas.put("二号",20);
            datas.put("三号",40);
            ImageEntity imageEntity = JfreeUtil.pieChart("测试",datas, 500, 300);
             map.put("picture", imageEntity);
    
            //模拟其它普通数据
            map.put("username", "张三");
            map.put("date", "2019-10-10");
            map.put("desc", "测试");
            map.put("boo", true);
    
            //模拟表格数据
            ArrayList<HashMap<String, String>> list = new ArrayList<>(2);
            HashMap<String, String> temp = new HashMap<>(3);
            temp.put("sn","1");
            temp.put("name","第一个人");
            temp.put("age","23");
            list.add(temp);
            temp = new HashMap<>(3);
            temp.put("sn","2");
            temp.put("name","第二个人");
            temp.put("age","24");
            list.add(temp);
            map.put("personlist",list);
            //word模板相对路径、word生成路径、word生成的文件名称、数据源
            WordUtil.exportWord("template/demo1.docx", "D:/", "生成文件.docx", map);
        }
    }

    6、运行测试

    生成后的word文档

    展开全文
  • 导出word可以生成图表,饼图和柱状和nopi的文本替换对Nopi版本有要求,我用来做的一个产品的年报
  • Java数据生成统计图表程序

    热门讨论 2008-05-13 13:37:00
    Java数据生成统计图表程序,根据输入的温数据数据生成统计折线,采用的java数据结构,java swing和java图形.
  • echarts 生成统计图时,自动保存图片到服务器,并使用freemarker生成word。 1.项目工程截图如下: 需要echarts包和生成word的freemarker-2.3.8.jar包 2.index.jsp页面中的代码: String path = ...

    echarts 生成统计图时,自动保存图片到服务器,并使用freemarker生成word。


    1.项目工程截图如下:


    需要echarts包和生成word的freemarker-2.3.8.jar包

    2.index.jsp页面中的代码:

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <base href="<%=basePath%>">
        
        <title>ECharts 生成报表 并保存图片</title>
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    	<!--
    	<link rel="stylesheet" type="text/css" href="styles.css">
    	-->
      </head>
      
      <body>
        <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
        <div id="main" style="height:400px"></div>
        <!-- ECharts单文件引入 -->
        <script src="<%=basePath %>js/echarts/build/dist/echarts.js"></script>
        <script type="text/javascript">
            // 路径配置
            require.config({
                paths: {
                    echarts: '<%=basePath %>js/echarts/build/dist'
                }
            });
            
            // 使用
            require(
                [
                    'echarts',
                    'echarts/chart/bar' // 使用柱状图就加载bar模块,按需加载
                ],
                function (ec) {
                    // 基于准备好的dom,初始化echarts图表
                    var myChart = ec.init(document.getElementById('main')); 
                    
                    var option = {
                        tooltip: {
                            show: true
                        },
                        legend: {
                            data:['销量']
                        },
                        xAxis : [
                            {
                                type : 'category',
                                data : ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
                            }
                        ],
                        yAxis : [
                            {
                                type : 'value'
                            }
                        ],
                        series : [
                            {
                                "name":"销量",
                                "type":"bar",
                                "data":[5, 20, 40, 10, 10, 20]
                            }
                        ]
                    };
            
                    // 为echarts对象加载数据 
                    myChart.setOption(option); 
                    setTimeout(exportImage, 2000);
                    function exportImage(){
    	                var data = "a="+encodeURIComponent(myChart.getDataURL("png"));
    				    var xmlhttp;
    				    if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
    				        xmlhttp = new XMLHttpRequest();
    				    } else { // code for IE6, IE5
    				        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    				    }
    				    xmlhttp.open("POST","<%=path%>/servlet/saveImage",true);
    				    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    				    xmlhttp.onreadystatechange = function() {
    				        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    				            alert("保存成功");
    				        }
    				    }
    				    xmlhttp.send(data);
    			    }
    			    
                }
            );
        </script>
    </body>
    </html>
    
    3.web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
    	xmlns="http://java.sun.com/xml/ns/javaee" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <display-name></display-name>	
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      
      <servlet>
        <servlet-name>saveImage</servlet-name>
        <servlet-class>com.servlet.SaveImage</servlet-class>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>saveImage</servlet-name>
        <url-pattern>/servlet/saveImage</url-pattern>
      </servlet-mapping>
    </web-app>
    
    4.SaveImage.java

    package com.servlet;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.word.WordUtil;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    public class SaveImage extends HttpServlet {
    	private static final long serialVersionUID = -1915463532411657451L;
    	 
        public void init() throws ServletException {  
           
        } 
        protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        	
        }
     
        protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        	String a = request.getParameter("a");
            try {
                String[] url = a.split(",");
                String u = url[1];
                // Base64解码
                byte[] b = new BASE64Decoder().decodeBuffer(u);
                WordUtil wordUtil = new WordUtil();
                String fileStr = wordUtil.saveFile(); 
                // 生成图片
                OutputStream out = new FileOutputStream(new File(fileStr+"\\test.png"));
                out.write(b);
                out.flush();
                out.close();
                
                //数据模拟,如果是真实编写,可以建service层,dao层进行数据的获取
                Map<String, Object> dataMap = new HashMap<String, Object>();
                dataMap = getData();
                //生产word
                wordUtil.createWord("2.ftl", fileStr+"\\test.doc", dataMap);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        public Map<String, Object> getData() {
        	Map<String, Object> dataMap = new HashMap<String, Object>();
        	WordUtil wordUtil = new WordUtil();
        	String fileStr = wordUtil.saveFile();
        	
        	dataMap.put("image", getImageStr(fileStr+"\\test.png"));
    		List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
    		for (int i = 0; i < 2; i++) {
    			Map<String,Object> map = new HashMap<String,Object>();
    			map.put("xuhao", i);
    			map.put("neirong", "内容"+i);
    			list.add(map);
    			
    		}
    		dataMap.put("list", list);
    		dataMap.put("info", "测试");
    		return dataMap;
        }
        public String getImageStr(String imgFile) {
    		InputStream in = null;
    		byte[] data = null;
    		try {
    			in = new FileInputStream(imgFile);
    			data = new byte[in.available()];
    			in.read(data);
    			in.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		BASE64Encoder encoder = new BASE64Encoder();
    		return encoder.encode(data);
    	}
    }
    
    5.WordUtil.java

    package com.word;
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.util.Map;
    
    import sun.misc.BASE64Encoder;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateException;
    
    public class WordUtil {
    	private Configuration configuration = null;
    
    	public WordUtil() {
    		configuration = new Configuration();
    		configuration.setDefaultEncoding("utf-8");
    
    	}
    
    	public void createWord(String templetName, String filePathName, Map<String, Object> dataMap) {
    		configuration.setClassForTemplateLoading(this.getClass(), "/com/word"); // FTL文件所存在的位置
    		Template t = null;
    		try {
    			// 获取模版文件
    			t = configuration.getTemplate(templetName);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		// 生成文件的路径和名称
    		File outFile = new File(filePathName);
    		Writer out = null;
    		try {
    			out = new BufferedWriter(new OutputStreamWriter(
    					new FileOutputStream(outFile)));
    		} catch (FileNotFoundException e1) {
    			e1.printStackTrace();
    		}
    
    		try {
    			t.process(dataMap, out);
    		} catch (TemplateException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public String getImageStr(String imgFile) {
    		InputStream in = null;
    		byte[] data = null;
    		try {
    			in = new FileInputStream(imgFile);
    			data = new byte[in.available()];
    			in.read(data);
    			in.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		BASE64Encoder encoder = new BASE64Encoder();
    		return encoder.encode(data);
    	}
    	public String saveFile() {
    		String nowpath = System.getProperty("user.dir");
    		String path = nowpath.replace("bin", "webapps");
    		path += "\\"+"TestWeb"+"\\"+"word";
    		File tmp = new File(path);
    		if (!tmp.exists()) {
    			tmp.mkdirs();
    		}
    		return path;
    	}
    }
    
    6.模版的制作:

    1).新建word文档,内容如下图:


    2).把word文档另存为Word 2003 XML 文档,打开内容,如下图:


    在<w:tr>之前<#list list as list>,在</w:tr>之后加</#list>,如果不循环,不需要加。接在把图片生产的一大串字符改成${image},试情况改此文件的编码。

    3).把修改好的xml后缀改为ftl。模版做好,放在指定位置即可。跑完程序后,生产如下word:


    大功告成!有什么问题,大家一起讨论!


    爱生活,爱分享,爱康宝!

    展开全文
  • 最近在项目中做了一个生成并导出word报表的功能,在这里分享给大家。 经过查看ESPC原有的生成报表代码和网上查阅的一些方法,解决方案的思路如下: **1.**利用pychartdir库生成图表,保存图片,转成字节数据 **2.**...

    最近在项目中做了一个生成并导出word报表的功能,在这里分享给大家。

    经过查看ESPC原有的生成报表代码和网上查阅的一些方法,解决方案的思路如下:

    **1.**利用pychartdir库生成图表,保存图片,转成字节数据

    **2.**然后使用office办公软件编写所需要的模板word,另存为xml文件

    **3.**利用jinja2库渲染修改好的模板,然后写入.doc文件即可

    那下面我将我实现的过程记录给大家分享一下。

    一、利用pychartdir库生成图表,保存图片,转成字节数据

    1.利用pychartdir库生成一个条形图

    首先需要导入pychartdir库

    我们以生成一个条形图为例子:

    结果为一张图片:

    2.我们在word中如果只是引用路径,那么生成的word就会出现找不到图片,此时,我们应该使用下面这个函数将图片转化为字节数据:

    此时我们就拿到了我们想要的数据

    我们可以将所需要画图的封装成一个工具类,只留取数据接口比如:

    二.然后使用office办公软件编写所需要的模板word,另存为xml文件

    1.使用office软件编写一个所需要的word模板,编写好之后选择另存,类型选择为xml文件。

    2,使用sublime或者其他文本编辑打开xml文件,在模板相应位置替换成渲染的数据模型,具体语法和Django模板的语法基本一致,如:

    三.利用jinja2库渲染修改好的模板,然后写入.doc文件即可

    1.导入jinja2模块和相应模块

    2.加载我们刚刚编辑好的word模板

    3.打开和渲染模板

    其中w_id和w_pname属性是word图片的属性,只要每一张图片id name唯一即可

    全部代码如下:

    运行代码,即可生成我们想要的word报表

    参考资料及其链接:

    Jinja2 安装:
    pip install Jinja2

    pychartdir 安装:
    Python中要使用pychartdir的绘图的话需要安装pychartdir模块,其安装方法不同于其他python模块的安装。

    1.先下载pychartdir,可从官网http://www.advsofteng.com/download.html下载对应的zip包

    2.解压后的doc目录下有一个pychartdir.chm帮助文档

    3.帮助文档中Installation中有描述安装的方法:

    在python的安装目录下的Lib\site-packages目录下新建chartdirector目录

    将解压后的ChartDirector\lib目录下的所有文件复制到前面创建chartdirector目录下

    在site-packages目录下新建一个chartdirector.pth文件,记事本打开,里面添加chartdirector,保存退出

    4.打开python IDE,输入from pychartdir import *,如果导入成功则说明安装成功,可正常使用pychartdir

    pychartdir API及其demo

    展开全文
  • 一个非常实用的ASP.NET生成统计图的方法,包含了详细的源程序、数据库以及一个Word格式的说明,经过完善测试的,您根据自己的需求,稍加修改,即可应用。
  • ASP.NET调用Office组件生成统计图
  • Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写...POI生成excel是比较常用的技术之一,但是用来生成word相对来说比较少,今天演示一下利用P...
        Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。
    

    POI生成excel是比较常用的技术之一,但是用来生成word相对来说比较少,今天演示一下利用POI生成word文档的整个流程,,当然方法有很多种,这是我感觉比较方便的一种。
    需要实现的功能

    • 在文档中动态生成章节标题、文本段落、表格标题、表格、统计图等等。
    • 给每个章节编号
    • 删除没有替换的占位符

    总体思路
    这种方法的本质其实是动态替换,动态替换的意思是在模板中写入很多某种特定格式的占位符(关键词)以及图的样例,如果前端需要生成这个章节的内容,那么,把这个关键词传到后端,再在模板中寻找关键词,有则替换成具体内容,无则不替换。最后把没有替换掉的占位符删除,从而达到动态替换的效果。

    具体实现过程如下

    1. 创建word模板.文档中包含若干个章节,章节中包含章节标题、文本段落、表格标题、表格、统计图的占位符。占位符的内容依据自己的实际情况而定。
    2. 加载模板,判断该章节是否存在,如果存在就替换具体数据。本示例数据都是模拟的,实际情况应该从数据库中查出。
    3. 再次遍历替换后的word文档,把没有替换掉的占位符删除。

    示例

    1. 创建模板

    如下图所示:
    在这里插入图片描述

    2.替换数据

    2.1 测试类

    import org.apache.poi.xwpf.usermodel.*;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.util.ResourceUtils;
    
    import java.io.*;
    import java.util.Iterator;
    import java.util.List;
    import java.util.regex.Pattern;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class PoiChartsTest {
        @Autowired
        private  PoiPropsConfig config;
    
        @Autowired
        private PoiUtils poiUtils;
    
        //预编译正则表达式,加快正则匹配速度
        private  Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
    
        @Test
        public void test() throws Exception {
            XWPFDocument document = null;
            InputStream inputStream=null;
            try {
                File file = ResourceUtils.getFile("D://poi.docx");
                inputStream = new FileInputStream(file);
                document = new XWPFDocument(inputStream);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();
    
            //处理文字
            while (itPara.hasNext()) {
                XWPFParagraph paragraph = itPara.next();
                String paraText = paragraph.getText();
                //如果没有匹配到指定格式的关键词占位符(如${title}格式的)则不进行后续处理
                if (!pattern.matcher(paraText).find()) {
                    continue;
                }
                //提取出文档模板占位符中的章节标题
                String keyInParaText = paraText.split("\\$\\{")[1].split("\\}")[0];
                //如果占位符是大标题
                if ("title".equalsIgnoreCase(keyInParaText)) {
                    insertTitle(paragraph);
                    continue;
                }
                //如果占位符代表文本总描述
                if ("totalDesc".equalsIgnoreCase(keyInParaText)) {
                    insertText(paragraph);
                    continue;
                }
                //如果占位符代表章节标题
                if (keyInParaText.contains("section") && keyInParaText.contains("Title")) {
                	//获取章节类名
                    String name = keyInParaText.substring(0, 8);
                    //获取章节类的路径
                    String classPath = config.getSection().get(name);
                    //通过类路径获取类对象
                    BaseSection base = (BaseSection) Class.forName(classPath).newInstance();
                    base.replaceSectionTitle(document, paragraph);
                    continue;
                }
                //如果占位符代表章节文本描述
                if (keyInParaText.contains("body")) {
                    String name = keyInParaText.substring(0, 8);
                    BaseSection base = (BaseSection) Class.forName(config.getSection().get(name)).newInstance();
                    base.replaceBody(paragraph);
                    continue;
                }
                //如果占位符代表表名
                if (keyInParaText.contains("tableName")) {
                    String name = keyInParaText.substring(0, 8);
                    BaseSection base = (BaseSection) Class.forName(config.getSection().get(name)).newInstance();
                    base.replaceTableName(paragraph);
                    continue;
                }
                //如果占位符代表表
                if (keyInParaText.endsWith("table")) {
                    String name = keyInParaText.substring(0, 8);
                    BaseSection base = (BaseSection) Class.forName(config.getSection().get(name)).newInstance();
                    base.insertTable(document, paragraph);
                    continue;
                }
                //如果占位符代表统计图
                if (keyInParaText.endsWith("chart")) {
                    String name = keyInParaText.substring(0, 8);
                    paragraph.removeRun(0);
                    BaseSection base = (BaseSection) Class.forName(config.getSection().get(name)).newInstance();
                    base.replaceChart(document, keyInParaText);
                    continue;
                }
                //如果占位符代表图名
                if (keyInParaText.contains("chartName")) {
                    String name = keyInParaText.substring(0, 8);
                    BaseSection base = (BaseSection) Class.forName(config.getSection().get(name)).newInstance();
                    base.replaceChartName(paragraph);
                    continue;
                }
            }
    
            //再遍历一次文档,把没有替换的占位符段落删除
            List<IBodyElement> elements = document.getBodyElements();
            int indexTable = 0;
            for (int k = 0; k < elements.size(); k++) {
                IBodyElement bodyElement = elements.get(k);
                //所有段落,如果有${}格式的段落便删除该段落
                if (bodyElement.getElementType().equals(BodyElementType.PARAGRAPH)) {
                    XWPFParagraph p = (XWPFParagraph) bodyElement;
                    String paraText = p.getText();
                    boolean flag = false;
                    if (pattern.matcher(paraText).find()) {
                        flag = document.removeBodyElement(k);
                        if (flag) {
                            k--;
                        }
                    }
                }
                //如果是表格,那么给表格的前一个段落(即表名加上编号,如表1)
                if (bodyElement.getElementType().equals(BodyElementType.TABLE)) {
                    indexTable++;
                    XWPFParagraph tableTitleParagraph = (XWPFParagraph) elements.get(k - 1);
                    StringBuilder tableTitleText = new StringBuilder(tableTitleParagraph.getParagraphText());
                    tableTitleText.insert(0, "表" + indexTable + " ");
                    poiUtils.setTableOrChartTitle(tableTitleParagraph, tableTitleText.toString());
                }
            }
    
            //给章节与小节添加序号
            poiUtils.init(document);
    
            //导出word文档
            FileOutputStream docxFos = new FileOutputStream("D://test1.docx");
            document.write(docxFos);
            docxFos.flush();
            docxFos.close();
            inputStream.close();
        }
    
        //插入大标题
        public void insertTitle(XWPFParagraph paragraph) {
            String title = "步步升超市报告";
            List<XWPFRun> runs = paragraph.getRuns();
            int runSize = runs.size();
            /**Paragrap中每删除一个run,其所有的run对象就会动态变化,即不能同时遍历和删除*/
            int haveRemoved = 0;
            for (int runIndex = 0; runIndex < runSize; runIndex++) {
                paragraph.removeRun(runIndex - haveRemoved);
                haveRemoved++;
            }
            /**3.插入新的Run即将新的文本插入段落*/
            XWPFRun createRun = paragraph.insertNewRun(0);
            createRun.setText(title);
            XWPFRun separtor = paragraph.insertNewRun(1);
            /**两段之间添加换行*/
            separtor.setText("\r");
            //设置字体大小
            createRun.setFontSize(22);
            //是否加粗
            createRun.setBold(true);
            //设置字体
            createRun.setFontFamily("宋体");
            //设置居中
            paragraph.setAlignment(ParagraphAlignment.CENTER);
        }
    
        //插入文本描述
        private void insertText(XWPFParagraph paragraph) {
            String text = "步步升超市作为零售业的典型代表,它已经在全国迅速发展起来。在2018年上半年取得了不菲的成绩," +
                    "创造销售额230亿,本着方便于民,服务于民的宗旨,我们会继续努力。以下是详细信息报告:"
            poiUtils.setTextPro(paragraph, text);
        }
    }
    

    2.2 章节基类
    因为要实现动态生成,所以要用到反射和多态。

    import org.apache.poi.ooxml.POIXMLDocumentPart;
    import org.apache.poi.xwpf.usermodel.*;
    import org.apache.xmlbeans.XmlCursor;
    import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
    import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle;
    import org.openxmlformats.schemas.drawingml.x2006.chart.CTTx;
    import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
    
    
    import java.util.ArrayList;
    import java.util.List;
    
    public abstract class BaseSection {
        PoiUtils poiUtils = new PoiUtils();
    
        //替换章节标题
        public abstract void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph);
    
        //替换章节内容
        public abstract void replaceBody(XWPFParagraph paragraph);
    
        //替换章节表名
        public  void replaceTableName(XWPFParagraph paragraph){
            String tableName="水果销售数据";
            poiUtils.setTableOrChartTitle(paragraph,tableName);
        }
    
        //生成表
        public void insertTable(XWPFDocument document, XWPFParagraph paragraph) {
            /*1.将段落原有文本(原有所有的Run)全部删除*/
            poiUtils.deleteRun(paragraph);
            /*生成表格插入提示游标*/
            XmlCursor cursor = paragraph.getCTP().newCursor();
            // 在指定游标位置插入表格
            XWPFTable table = document.insertNewTbl(cursor);
            //设置表格居中
            table.setTableAlignment(TableRowAlign.CENTER);
            //模拟表格数据
            List<String[]> list = new ArrayList<>();
            list.add(new String[]{"苹果", "100", "6", "600", "10.7"});
            list.add(new String[]{"香蕉", "200", "5", "1000", "17.9"});
            list.add(new String[]{"桃子", "300", "4", "1200", "21.4"});
            list.add(new String[]{"葡萄", "400", "3", "1200", "21.4"});
            list.add(new String[]{"西瓜", "500", "2", "1000", "17.9"});
            list.add(new String[]{"车厘子", "600", "1", "600", "10.7"});
            //根据数据生成表格
            inserInfo(table, list);
            //设置表格中所有单元格水平居中对齐
            poiUtils.setTableCenter(table);
        }
    
        //替换统计图数据
        public  void replaceChart(XWPFDocument document,String key){
            //模拟统计图数据
            //系列
            String[] series={"销售量(kg)","销售额(元)","净盈利额(元)"};
            //x轴
            String[] categories={"星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
            List<Number[]> values = new ArrayList<>();
            //一周的销售量
            Number[] value1 = {60,80,74,52,66,88,90};
            //一周的销售额
            Number[] value2 = {450.2,652.1,554,384.6,486.5,688.9,711.1};
            //一周的净盈利额
            Number[] value3 = {200.2,326.4,266,159.5,222.2,355.5,369.5};
    
            values.add(value1);
            values.add(value2);
            values.add(value3);
    
            try {
                XWPFChart xChart = null;
                CTChart ctChart = null;
                for (POIXMLDocumentPart part : document.getCharts()) {
                    if (part instanceof XWPFChart) {
                        xChart = (XWPFChart) part;
                        ctChart = xChart.getCTChart();
                        String chartTitle=getTitle(ctChart);
                        if (key.equalsIgnoreCase(chartTitle) ) {
                            generateChart(xChart, series,categories,values);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取模板中表格的标题
         * @param chart
         * @return
         */
        public String getTitle(CTChart chart) {
            CTTitle title = chart.getTitle();
            if (title != null) {
                CTTx tx = title.getTx();
                CTTextBody tb = tx.getRich();
                return tb.getPArray(0).getRArray(0).getT();
            }
            return "";
        }
    
        /**
         *
         * @param chart 模板中统计图对象
         * @param series 系列
         * @param categories x轴
         * @param values 具体的值
         */
        public abstract void generateChart(XWPFChart chart,String[]series,String[]categories,List<Number[]> values);
    
        // 替换图名称
        public abstract void replaceChartName(XWPFParagraph paragraph);
    
        /**
         * 把信息插入表格,因为每个章节的表格生成都一样,避免麻烦,所以统一用这个数据
         * @param table
         * @param list
         */
        private void inserInfo(XWPFTable table, List<String[]> list) {
            //设置表格宽度
            table.setWidth(10000);
            XWPFTableRow row = table.getRow(0);
            row.getCell(0).setText("指标");
            row.addNewTableCell().setText("销售数量");
            row.addNewTableCell().setText("单价");
            row.addNewTableCell().setText("销售总额");
            row.addNewTableCell().setText("销售额占比");
    
            for (int i = 0; i < list.size(); i++) {
                row = table.createRow();
                String[] obj = list.get(i);
                for (int j = 0; j < obj.length; j++) {
                    row.getCell(j).setText(obj[j]);
                }
            }
        }
    }
    

    2.3 章节1

    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.ss.util.CellReference;
    import org.apache.poi.xddf.usermodel.chart.*;
    import org.apache.poi.xwpf.usermodel.XWPFChart;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;
    import org.apache.poi.xwpf.usermodel.XWPFParagraph;
    
    import java.util.List;
    
    public class Section1 extends BaseSection{
    
        @Override
        public  void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph) {
            String sectionTitle ="水果一";
            poiUtils.setLevelTitle1(document,paragraph,sectionTitle);
        }
    
        @Override
        public void replaceBody(XWPFParagraph paragraph) {
            String body ="我藏不住秘密,也藏不住忧伤,正如我藏不住爱你的喜悦,藏不住分离时的彷徨。" +
                    "我就是这样坦然,你舍得伤,就伤。如果有一天,你要离开我,我不会留你,我知道你有" +
                    "你的理由;如果有一天,你说还爱我,我会告诉你,其实我一直在等你;如果有一天,我们" +
                    "擦肩而过,我会停住脚步,凝视你远去的背影,告诉自己那个人我曾经爱过。或许人一生可" +
                    "以爱很多次,然而总有一个人,可以让我们笑得最灿烂,哭得最透彻,想得最深切。炊烟起" +
                    "了,我在门口等你。夕阳下了,我在山边等你。叶子黄了,我在树下等你。月儿弯了,我在" +
                    "十五等你。细雨来了,我在伞下等你。流水冻了,我在河畔等你。生命累了,我在天堂等你" +
                    "。我们老了,我在来生等你。";
            poiUtils.setTextPro(paragraph,body);
        }
    
    
        @Override
        public void generateChart(XWPFChart chart, String[] series, String[] categories, List<Number[]> values) {
            String chartTitle="香蕉销售数据周统计图";
            final List<XDDFChartData> data = chart.getChartSeries();
            final XDDFBarChartData bar = (XDDFBarChartData) data.get(0);
    
            final int numOfPoints = categories.length;
    
            final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
    
            final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
            for (int i = 0; i < values.size(); i++) {
                final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, i + 1, i + 1));
                Number[] value = values.get(i);
                final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(value, valuesDataRange, i + 1);
                XDDFChartData.Series ser;
                if (i < 3) {
                    ser = bar.getSeries().get(i);
                    ser.replaceData(categoriesData, valuesData);
                } else {
                    ser = bar.addSeries(categoriesData, valuesData);
                }
                CellReference cellReference = chart.setSheetTitle(series[i], 1);
                ser.setTitle(series[i], cellReference);
            }
    
            chart.plot(bar);
            chart.setTitleText(chartTitle);
            chart.setTitleOverlay(false);
        }
    
        @Override
        public void replaceChartName(XWPFParagraph paragraph) {
            String chartName="香蕉销售统计图(柱状图)";
            poiUtils.setTableOrChartTitle(paragraph,chartName);
        }
    }
    

    2.4 章节2

    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.xddf.usermodel.chart.*;
    import org.apache.poi.xwpf.usermodel.XWPFChart;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;
    import org.apache.poi.xwpf.usermodel.XWPFParagraph;
    
    import java.util.List;
    
    public class Section2 extends BaseSection{
    
        @Override
        public void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph) {
            String sectionTitle ="水果二";
            poiUtils.setLevelTitle1(document,paragraph,sectionTitle);
        }
    
        @Override
        public void replaceBody(XWPFParagraph paragraph) {
            String body ="无论是太阳拨开云雾,还是云雾遮挡太阳,都是彼此间的争斗。不要在晴空万里时," +
                    "才说天空“蔚蓝如海”。我的前身本是大海中的一滴水,当我们的云层在天空中翻腾涌动时," +
                    "这才是波涛汹涌的大海。即使卑微,也有自己的理想,也想留下瞬间的精彩。即便阳光把我由" +
                    "一滴晶莹剔透的水转成一缕游丝般的水汽,也要在天空中展现出鲜艳曼妙的身影,留下瞬间的精彩" +
                    ",也要和其他兄弟姐妹组成波涛万里的云海,凝成雨珠,滴落大海,在轮回中重生!";
            poiUtils.setTextPro(paragraph,body);
        }
    
    
        @Override
        public void insertTable(XWPFDocument document, XWPFParagraph paragraph) {
            super.insertTable(document, paragraph);
        }
    
        @Override
        public void generateChart(XWPFChart chart, String[] series, String[] categories, List<Number[]> values) {
            String chartTitle="香蕉销售数据周统计图";
            final List<XDDFChartData> data = chart.getChartSeries();
            XDDFPieChartData pie = (XDDFPieChartData) data.get(0);
            Number[]values1 = values.get(2);
            final int numOfPoints = categories.length;
            final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
            final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
    
            final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
            final XDDFNumericalDataSource<?> valuesData = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange, 1);
    
            XDDFChartData.Series series1 = pie.getSeries().get(0);
            series1.replaceData(categoriesData, valuesData);
            series1.setTitle(series[0], chart.setSheetTitle(series[0], 0));
    
            chart.plot(pie);
            chart.setTitleText(chartTitle);
            chart.setTitleOverlay(false);
        }
    
        @Override
        public void replaceChartName(XWPFParagraph paragraph) {
            String chartName="香蕉销售统计图(饼图)";
            poiUtils.setTableOrChartTitle(paragraph,chartName);
        }
    }
    

    2.5 章节3

    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.ss.util.CellReference;
    import org.apache.poi.xddf.usermodel.chart.*;
    import org.apache.poi.xwpf.usermodel.XWPFChart;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;
    import org.apache.poi.xwpf.usermodel.XWPFParagraph;
    
    import java.util.List;
    
    public class Section3 extends BaseSection{
    
        @Override
        public void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph) {
            String sectionTitle ="水果三";
            poiUtils.setLevelTitle1(document,paragraph,sectionTitle);
        }
    
        @Override
        public void replaceBody(XWPFParagraph paragraph) {
        String body="在一次次水与云的交替变化中,蓦然发现无论自己是天边的一片云,还是大海中的一滴水," +
                "都是那毫不起眼的卑微之物,谁会去解读一片云的心绪,聆听一滴水的声音?经历了风起风落," +
                "也就习惯了云卷云舒,渐渐的我懂得了顺其自然,心境归于平和,不在向往风起云涌的气象," +
                "云淡风轻才是自己的本色。";
            poiUtils.setTextPro(paragraph,body);
        }
    
        @Override
        public void generateChart(XWPFChart chart, String[] series, String[] categories, List<Number[]> values) {
            String chartTitle="香蕉销售数据周统计图";
            final List<XDDFChartData> data = chart.getChartSeries();
            final XDDFLineChartData line = (XDDFLineChartData) data.get(0);
    
            final int numOfPoints = categories.length;
    
            final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
    
            final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
            for (int i = 0; i < values.size(); i++) {
                final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, i + 1, i + 1));
                Number[] value = values.get(i);
                final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(value, valuesDataRange, i + 1);
                XDDFChartData.Series ser;
                if (i < 3) {
                    ser = line.getSeries().get(i);
                    ser.replaceData(categoriesData, valuesData);
                } else {
                    ser = line.addSeries(categoriesData, valuesData);
                }
                CellReference cellReference = chart.setSheetTitle(series[i], 1);
                ser.setTitle(series[i], cellReference);
            }
    
            chart.plot(line);
            chart.setTitleText(chartTitle);
            chart.setTitleOverlay(false);
        }
    
        @Override
        public void replaceChartName(XWPFParagraph paragraph) {
            String chartName="香蕉销售统计图(折线图)";
            poiUtils.setTableOrChartTitle(paragraph,chartName);
        }
    }
    

    2.6 配置章节类和章节类路径

    • application.yml
    #文档生成相关类配置
    doc:
      section:
        section1: com.poi.docgenerate.Section1
        section2: com.poi.docgenerate.Section2
        section3: com.poi.docgenerate.Section3
    
    • PoiPropConfig.java
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    
    import java.util.Map;
    
    @ConfigurationProperties(prefix = "doc")
    @Component
    public class PoiPropsConfig {
    
        //把章节名作为key,章节类的全路径名作为value
        private Map<String,String> section;
    
        public void setSection(Map<String, String> section) {
            this.section = section;
        }
    
        public Map<String, String> getSection() {
            return section;
        }
        
        @Override
        public String toString() {
            for (Map.Entry<String,String> entry:section.entrySet()){
                String key = entry.getKey();
                String value = entry.getValue();
                System.out.println(key+":"+value);
            }
           return null;
        }
    }
    

    2.7 工具类

    import com.google.common.base.Strings;
    import com.police.hunan.judge.entity.vo.DocGenerateVO;
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
    import org.springframework.stereotype.Component;
    
    import java.beans.BeanInfo;
    import java.beans.IntrospectionException;
    import java.beans.Introspector;
    import java.beans.PropertyDescriptor;
    import java.math.BigInteger;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Component
    public class PoiUtils {
    
        private Map<String, Map<String, Object>> orderMap = new HashMap<String, Map<String, Object>>();
    
        /*把*/
        public static void copy(Object obj, Object dest) {
            // 获取属性
            BeanInfo sourceBean = null;
            try {
                sourceBean = Introspector.getBeanInfo(obj.getClass(), Object.class);
            } catch (IntrospectionException e) {
                e.printStackTrace();
            }
            PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();
    
            BeanInfo destBean = null;
            try {
                destBean = Introspector.getBeanInfo(dest.getClass(), Object.class);
            } catch (IntrospectionException e) {
                e.printStackTrace();
            }
            PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();
            try {
                for (int i = 0; i < sourceProperty.length; i++) {
                    Object value = sourceProperty[i].getReadMethod().invoke(obj);
                    if (value != null) {
    
                        for (int j = 0; j < destProperty.length; j++) {
                            if (sourceProperty[i].getName().equals(destProperty[j].getName()) && sourceProperty[i].getPropertyType() == destProperty[j].getPropertyType()) {
                                // 调用source的getter方法和dest的setter方法
                                destProperty[j].getWriteMethod().invoke(dest, value);
                                break;
                            }
                        }
                    } else {
                        continue;
                    }
                }
            } catch (Exception e) {
                try {
                    throw new Exception("属性复制失败:" + e.getMessage());
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
        
        /**
         * 设置一级标题内容及样式
         *
         * @param paragraph
         * @param text
         */
        public  void setLevelTitle1(XWPFDocument document, XWPFParagraph paragraph, String text) {
            /**1.将段落原有文本(原有所有的Run)全部删除*/
            deleteRun(paragraph);
            /**3.插入新的Run即将新的文本插入段落*/
            XWPFRun createRun = paragraph.insertNewRun(0);
            createRun.setText(text);
            XWPFRun separtor = paragraph.insertNewRun(1);
            /**两段之间添加换行*/
            separtor.setText("\r");
            createRun.setFontSize(16);
            createRun.setFontFamily("黑体");
            paragraph.setIndentationFirstLine(600);
            paragraph.setSpacingAfter(20);
            paragraph.setSpacingBefore(20);
            addCustomHeadingStyle(document, "标题1", 1);
            paragraph.setStyle("标题1");
        }
    
        /**
         * 设置二级标题内容及样式
         *
         * @param paragraph
         * @param text
         */
        public  void setLevelTitle2(XWPFDocument document, XWPFParagraph paragraph, String text) {
            deleteRun(paragraph);
            /**3.插入新的Run即将新的文本插入段落*/
            XWPFRun createRun = paragraph.insertNewRun(0);
            createRun.setText(text);
            XWPFRun separtor = paragraph.insertNewRun(1);
            /**两段之间添加换行*/
            separtor.setText("\r");
            createRun.setFontSize(16);
            createRun.setBold(true);
            createRun.setFontFamily("楷体_GB2312");
            paragraph.setIndentationFirstLine(600);
            paragraph.setSpacingAfter(10);
            paragraph.setSpacingBefore(10);
            addCustomHeadingStyle(document, "标题2", 2);
            paragraph.setStyle("标题2");
        }
    
        /**
         * 设置正文文本内容及样式
         *
         * @param paragraph
         * @param text
         */
        public  void setTextPro(XWPFParagraph paragraph, String text) {
            deleteRun(paragraph);
            /**3.插入新的Run即将新的文本插入段落*/
            XWPFRun createRun = paragraph.insertNewRun(0);
            createRun.setText(text);
            XWPFRun separtor = paragraph.insertNewRun(1);
            /**两段之间添加换行*/
            separtor.addBreak();
            createRun.addTab();
            createRun.setFontFamily("仿宋_GB2312");
            createRun.setFontSize(16);
    
            paragraph.setFirstLineIndent(20);
            paragraph.setAlignment(ParagraphAlignment.BOTH);
            paragraph.setIndentationFirstLine(600);
            paragraph.setSpacingAfter(10);
            paragraph.setSpacingBefore(10);
        }
    
        /**
         * 向段落添加文本
         *
         * @param paragraph
         * @param text
         */
        public  void addTextPro(XWPFParagraph paragraph, String text) {
            /**3.插入新的Run即将新的文本插入段落*/
            XWPFRun createRun = paragraph.createRun();
            createRun.setText(text);
            XWPFRun separtor = paragraph.createRun();
            /**两段之间添加换行*/
            separtor.addBreak();
            createRun.addTab();
            createRun.setFontFamily("仿宋_GB2312");
            createRun.setFontSize(16);
            paragraph.setFirstLineIndent(20);
            paragraph.setAlignment(ParagraphAlignment.BOTH);
            paragraph.setIndentationFirstLine(600);
            paragraph.setSpacingAfter(10);
            paragraph.setSpacingBefore(10);
    
            paragraph.addRun(createRun);
            paragraph.addRun(separtor);
        }
    
        /**
         * 设置表格标题内容及样式
         *
         * @param paragraph
         * @param text
         */
        public  void setTableOrChartTitle(XWPFParagraph paragraph, String text) {
            /**1.将段落原有文本(原有所有的Run)全部删除*/
           deleteRun(paragraph);
            XWPFRun createRun = paragraph.insertNewRun(0);
            createRun.setText(text);
            XWPFRun separtor = paragraph.insertNewRun(1);
            /**两段之间添加换行*/
            separtor.setText("\r");
            createRun.setFontFamily("楷体");
            createRun.setFontSize(16);
            createRun.setBold(true);
            paragraph.setSpacingAfter(10);
            paragraph.setSpacingBefore(10);
            paragraph.setAlignment(ParagraphAlignment.CENTER);
        }
    
        public void deleteRun(XWPFParagraph paragraph) {
            /*1.将段落原有文本(原有所有的Run)全部删除*/
            List<XWPFRun> runs = paragraph.getRuns();
            int runSize = runs.size();
            /*Paragrap中每删除一个run,其所有的run对象就会动态变化,即不能同时遍历和删除*/
            int haveRemoved = 0;
            for (int runIndex = 0; runIndex < runSize; runIndex++) {
                paragraph.removeRun(runIndex - haveRemoved);
                haveRemoved++;
            }
        }
    
        /**
         * 合并行
         *
         * @param table
         * @param col     需要合并的列
         * @param fromRow 开始行
         * @param toRow   结束行
         */
        public static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
            for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
                CTVMerge vmerge = CTVMerge.Factory.newInstance();
                if (rowIndex == fromRow) {
                    vmerge.setVal(STMerge.RESTART);
                } else {
                    vmerge.setVal(STMerge.CONTINUE);
                }
                XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
                CTTcPr tcPr = cell.getCTTc().getTcPr();
                if (tcPr != null) {
                    tcPr.setVMerge(vmerge);
                } else {
                    tcPr = CTTcPr.Factory.newInstance();
                    tcPr.setVMerge(vmerge);
                    cell.getCTTc().setTcPr(tcPr);
                }
            }
        }
    
        /**
         * 设置表格内容居中
         *
         * @param table
         */
        public void setTableCenter(XWPFTable table) {
            List<XWPFTableRow> rows = table.getRows();
            for (XWPFTableRow row : rows) {
                row.setHeight(400);
                List<XWPFTableCell> cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    CTTc cttc = cell.getCTTc();
                    CTTcPr ctPr = cttc.addNewTcPr();
                    ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                    cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
                }
            }
        }
    
        public void init(XWPFDocument document) {
            //获取段落
            List<XWPFParagraph> paras = document.getParagraphs();
    
            for (int i = 0; i < paras.size(); i++) {
                XWPFParagraph para = paras.get(i);
    //              System.out.println(para.getCTP());//得到xml格式
    //              System.out.println(para.getStyleID());//段落级别
    //              System.out.println(para.getParagraphText());//段落内容
    
                String titleLvl = getTitleLvl(document, para);//获取段落级别
                if ("a5".equals(titleLvl) || "HTML".equals(titleLvl) || "".equals(titleLvl) || null == titleLvl) {
                    titleLvl = "8";
                }
    
                if (null != titleLvl && !"".equals(titleLvl) && !"8".equals(titleLvl)) {
                    String t = titleLvl;
                    String orderCode = getOrderCode(titleLvl);//获取编号
                    String text = para.getParagraphText();
                    text = orderCode + " " + text;
                    List<XWPFRun> runs = para.getRuns();
                    int runSize = runs.size();
                    /**Paragrap中每删除一个run,其所有的run对象就会动态变化,即不能同时遍历和删除*/
                    int haveRemoved = 0;
                    for (int runIndex = 0; runIndex < runSize; runIndex++) {
                        para.removeRun(runIndex - haveRemoved);
                        haveRemoved++;
                    }
                    if ("1".equals(titleLvl)) {
                        setLevelTitle1(document, para, text);
                    }
                    if ("2".equals(titleLvl)) {
                        setLevelTitle2(document, para, text);
                    }
                }
            }
        }
    
        /**
         * Word中的大纲级别,可以通过getPPr().getOutlineLvl()直接提取,但需要注意,Word中段落级别,通过如下三种方式定义:
         * 1、直接对段落进行定义;
         * 2、对段落的样式进行定义;
         * 3、对段落样式的基础样式进行定义。
         * 因此,在通过“getPPr().getOutlineLvl()”提取时,需要依次在如上三处读取。
         *
         * @param doc
         * @param para
         * @return
         */
        private String getTitleLvl(XWPFDocument doc, XWPFParagraph para) {
            String titleLvl = "";
    
            //判断该段落是否设置了大纲级别
            CTP ctp = para.getCTP();
            if (ctp != null) {
                CTPPr pPr = ctp.getPPr();
                if (pPr != null) {
                    if (pPr.getOutlineLvl() != null) {
                        return String.valueOf(pPr.getOutlineLvl().getVal());
                    }
                }
            }
    
    
            //判断该段落的样式是否设置了大纲级别
            if (para.getStyle() != null) {
                if (doc.getStyles().getStyle(para.getStyle()).getCTStyle().getPPr().getOutlineLvl() != null) {
                    return String.valueOf(doc.getStyles().getStyle(para.getStyle()).getCTStyle().getPPr().getOutlineLvl().getVal());
                }
    
    
                //判断该段落的样式的基础样式是否设置了大纲级别
                if (doc.getStyles().getStyle(doc.getStyles().getStyle(para.getStyle()).getCTStyle().getBasedOn().getVal())
                        .getCTStyle().getPPr().getOutlineLvl() != null) {
                    String styleName = doc.getStyles().getStyle(para.getStyle()).getCTStyle().getBasedOn().getVal();
                    return String.valueOf(doc.getStyles().getStyle(styleName).getCTStyle().getPPr().getOutlineLvl().getVal());
                }
            }
            if (para.getStyleID() != null) {
                return para.getStyleID();
            }
            return titleLvl;
        }
    
        /**
         * 增加自定义标题样式。这里用的是stackoverflow的源码
         *
         * @param docxDocument 目标文档
         * @param strStyleId   样式名称
         * @param headingLevel 样式级别
         */
        private static void addCustomHeadingStyle(XWPFDocument docxDocument, String strStyleId, int headingLevel) {
    
    
            CTStyle ctStyle = CTStyle.Factory.newInstance();
            ctStyle.setStyleId(strStyleId);
    
            CTString styleName = CTString.Factory.newInstance();
            styleName.setVal(strStyleId);
            ctStyle.setName(styleName);
    
            CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
            indentNumber.setVal(BigInteger.valueOf(headingLevel));
    
            // lower number > style is more prominent in the formats bar
            ctStyle.setUiPriority(indentNumber);
    
            CTOnOff onoffnull = CTOnOff.Factory.newInstance();
            ctStyle.setUnhideWhenUsed(onoffnull);
    
            // style shows up in the formats bar
            ctStyle.setQFormat(onoffnull);
    
            // style defines a heading of the given level
            CTPPr ppr = CTPPr.Factory.newInstance();
            ppr.setOutlineLvl(indentNumber);
            ctStyle.setPPr(ppr);
    
            XWPFStyle style = new XWPFStyle(ctStyle);
    
            // is a null op if already defined
            XWPFStyles styles = docxDocument.createStyles();
    
            style.setType(STStyleType.PARAGRAPH);
            styles.addStyle(style);
    
        }
    
        /**
         * 获取标题编号
         *
         * @param titleLvl
         * @return
         */
        private String getOrderCode(String titleLvl) {
            String order = "";
    
            if ("0".equals(titleLvl) || Integer.parseInt(titleLvl) == 8) {//文档标题||正文
                return "";
            } else if (Integer.parseInt(titleLvl) > 0 && Integer.parseInt(titleLvl) < 8) {//段落标题
    
                //设置最高级别标题
                Map<String, Object> maxTitleMap = orderMap.get("maxTitleLvlMap");
                if (null == maxTitleMap) {//没有,表示第一次进来
                    //最高级别标题赋值
                    maxTitleMap = new HashMap<String, Object>();
                    maxTitleMap.put("lvl", titleLvl);
                    orderMap.put("maxTitleLvlMap", maxTitleMap);
                } else {
                    String maxTitleLvl = maxTitleMap.get("lvl") + "";//最上层标题级别(0,1,2,3)
                    if (Integer.parseInt(titleLvl) < Integer.parseInt(maxTitleLvl)) {//当前标题级别更高
                        maxTitleMap.put("lvl", titleLvl);//设置最高级别标题
                        orderMap.put("maxTitleLvlMap", maxTitleMap);
                    }
                }
    
                //查父节点标题
                int parentTitleLvl = Integer.parseInt(titleLvl) - 1;//父节点标题级别
                Map<String, Object> cMap = orderMap.get(titleLvl);//当前节点信息
                Map<String, Object> pMap = orderMap.get(parentTitleLvl + "");//父节点信息
    
                if (0 == parentTitleLvl) {//父节点为文档标题,表明当前节点为1级标题
                    int count = 0;
                    //最上层标题,没有父节点信息
                    if (null == cMap) {//没有当前节点信息
                        cMap = new HashMap<String, Object>();
                    } else {
                        count = Integer.parseInt(String.valueOf(cMap.get("cCount")));//当前序个数
                    }
                    count++;
                    order = count + "";
                    cMap.put("cOrder", order);//当前序
                    cMap.put("cCount", count);//当前序个数
                    orderMap.put(titleLvl, cMap);
    
                } else {//父节点为非文档标题
                    int count = 0;
                    //如果没有相邻的父节点信息,当前标题级别自动升级
                    if (null == pMap) {
                        return getOrderCode(String.valueOf(parentTitleLvl));
                    } else {
                        String pOrder = String.valueOf(pMap.get("cOrder"));//父节点序
                        if (null == cMap) {//没有当前节点信息
                            cMap = new HashMap<String, Object>();
                        } else {
                            count = Integer.parseInt(String.valueOf(cMap.get("cCount")));//当前序个数
                        }
                        count++;
                        order = pOrder + "." + count;//当前序编号
                        cMap.put("cOrder", order);//当前序
                        cMap.put("cCount", count);//当前序个数
                        orderMap.put(titleLvl, cMap);
                    }
                }
    
                //字节点标题计数清零
                int childTitleLvl = Integer.parseInt(titleLvl) + 1;//子节点标题级别
                Map<String, Object> cdMap = orderMap.get(childTitleLvl + "");//
                if (null != cdMap) {
                    cdMap.put("cCount", 0);//子节点序个数
                    orderMap.get(childTitleLvl + "").put("cCount", 0);
                }
            }
            return order;
        }
    }
    
    

    结果

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 怎样用电脑制作条形统计图和柱形统计图用电脑制作条形统计图和柱形统计图,可通过EXCEL表格实现。方法步骤如下:1、打开EXCEL表格,输入需要制作图表的相关数据,然后选中相关数据,点击插入选项卡中“柱形图”,并...
  • 1、准备 需要用到python-docx,jieba和wordcloud模块,先install pip3 install jieba pip install wordcloud 2、开始代码 ...(2)读取word文档的文本内容 此模块主要使用到了python-docx,官方文档地址:
  • 这是一部分自己做的代码,但是不知道怎么设置统计图的样式 public void setChartDate(String filepath,String pos,List[]> list){ ActiveXComponent word=null; Dispatch doc=null; Dispatch graph=null; ...
  • 可以先看下这篇文章:通过FreeMarker生成word文档及导出PDF文件 0、在线预览:利用微软在线预览链接展示office相关文档内容 当我们需要将文档展示到页面的时候,往往会想到将office相关文件转换成pdf,然后再通过...
  • 完美解决方案:Java生成复杂Word文档

    热门讨论 2011-07-18 19:31:06
    客户要求用程序生成标准的word文档,要能打印,而且不能变形,以前用过很多解决方案,都在客户严格要求下牺牲的无比惨烈。  POI读word文档还行,写文档实在不敢恭维,复杂的样式很难控制不提,想象一下一个20多页,...
  • word2007中生成统计图的两种方式

    千次阅读 2012-11-21 08:24:13
    有两种方式可以生成word中的统计图 1、添加chart,然后选择某个excel中的数据源 2、直接从excel里复制生成好的统计图   两种方式生成的图都一样, 但是docx跟xlsx文件的关系不一样   第一张情况,xlsx文件...
  • 我现在的问题是能导出文字和表格 不知道怎么导出统计图,求大神帮忙解答我的这个问题
  • 写了一个月 4月20-5月20,终于完成 …一个日报 大多es查的数据,有一点mysql查的数据 先占个坑,回头有空记录一下
  • Java实现Word画折线(非图片)

    千次阅读 2020-03-01 13:16:21
    最近需要实现Java在Word中画折线功能,不能简单的生成折线再导入。实现方法就是利用Apache poi 工具。实现的效果如下,把鼠标放上去可以显示值。 比较简单,有一些坑,发出来记录一下。 模板 要实现上述效果...
  • POI报表Word导出

    2017-02-06 14:31:07
    POI报表Word导出
  • 一、制作.ftl后缀的word模板文件 1、新建一个word文档模板 &amp;amp;amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;amp;amp;nbsp; &amp;amp;amp;amp;amp;amp;nbsp;&...
  • 项目中用到生成word报表,报表中有表格的合并 、页眉、表格中会有报表图片。然后查找了网上的资料,利用echar生成柱状,然后已base64串的方式发给后台,在后台解析成字节数组 ,利用poi生成word文档中。
  • python数据预处理生成词频统计图、词云 数据来源请看本人博客下简单的数据处理(一) 一、对转换后的结果进行预处理 1、我们需要去除每一句里的不可用字符, 例如"",.!@#$%^&*(){}+=-…以及数字[0-9]等这些不可用...
  • 如何在EXCEL里做条形统计图

    千次阅读 2021-06-24 01:26:36
    如何在EXCEL里做条形统计图?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!如何在EXCEL里做条形统计图?步骤1) 执行“插入—图表”命令,开启“图表...
  • java后端实现生成折线统计图表

    千次阅读 2020-02-16 12:09:09
    需求:java通过poi往word里插入统计图,例如用水用电趋势图,企业纳税折线图等。 方案一:通过jfeechart实现,代码如下: import java.awt.Color; import java.awt.Font; import java.io.File; import java.io....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,938
精华内容 16,375
关键字:

word生成统计图