1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > POI生成word文档 包括标题 段落 表格 统计图(非图片格式)

POI生成word文档 包括标题 段落 表格 统计图(非图片格式)

时间:2019-06-16 13:48:18

相关推荐

POI生成word文档 包括标题 段落 表格 统计图(非图片格式)

Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。

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

需要实现的功能

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

总体思路

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

具体实现过程如下

创建word模板.文档中包含若干个章节,章节中包含章节标题、文本段落、表格标题、表格、统计图的占位符。占位符的内容依据自己的实际情况而定。加载模板,判断该章节是否存在,如果存在就替换具体数据。本示例数据都是模拟的,实际情况应该从数据库中查出。再次遍历替换后的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)@SpringBootTestpublic class PoiChartsTest {@Autowiredprivate PoiPropsConfig config;@Autowiredprivate PoiUtils poiUtils;//预编译正则表达式,加快正则匹配速度private Pattern pattern = pile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);@Testpublic 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 = "步步升超市作为零售业的典型代表,它已经在全国迅速发展起来。在上半年取得了不菲的成绩," +"创造销售额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.x.chart.CTChart;import org.openxmlformats.schemas.drawingml.x.chart.CTTitle;import org.openxmlformats.schemas.drawingml.x.chart.CTTx;import org.openxmlformats.schemas.drawingml.x.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{@Overridepublic void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph) {String sectionTitle ="水果一";poiUtils.setLevelTitle1(document,paragraph,sectionTitle);}@Overridepublic void replaceBody(XWPFParagraph paragraph) {String body ="我藏不住秘密,也藏不住忧伤,正如我藏不住爱你的喜悦,藏不住分离时的彷徨。" +"我就是这样坦然,你舍得伤,就伤。如果有一天,你要离开我,我不会留你,我知道你有" +"你的理由;如果有一天,你说还爱我,我会告诉你,其实我一直在等你;如果有一天,我们" +"擦肩而过,我会停住脚步,凝视你远去的背影,告诉自己那个人我曾经爱过。或许人一生可" +"以爱很多次,然而总有一个人,可以让我们笑得最灿烂,哭得最透彻,想得最深切。炊烟起" +"了,我在门口等你。夕阳下了,我在山边等你。叶子黄了,我在树下等你。月儿弯了,我在" +"十五等你。细雨来了,我在伞下等你。流水冻了,我在河畔等你。生命累了,我在天堂等你" +"。我们老了,我在来生等你。";poiUtils.setTextPro(paragraph,body);}@Overridepublic 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);}@Overridepublic 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{@Overridepublic void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph) {String sectionTitle ="水果二";poiUtils.setLevelTitle1(document,paragraph,sectionTitle);}@Overridepublic void replaceBody(XWPFParagraph paragraph) {String body ="无论是太阳拨开云雾,还是云雾遮挡太阳,都是彼此间的争斗。不要在晴空万里时," +"才说天空“蔚蓝如海”。我的前身本是大海中的一滴水,当我们的云层在天空中翻腾涌动时," +"这才是波涛汹涌的大海。即使卑微,也有自己的理想,也想留下瞬间的精彩。即便阳光把我由" +"一滴晶莹剔透的水转成一缕游丝般的水汽,也要在天空中展现出鲜艳曼妙的身影,留下瞬间的精彩" +",也要和其他兄弟姐妹组成波涛万里的云海,凝成雨珠,滴落大海,在轮回中重生!";poiUtils.setTextPro(paragraph,body);}@Overridepublic void insertTable(XWPFDocument document, XWPFParagraph paragraph) {super.insertTable(document, paragraph);}@Overridepublic 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);}@Overridepublic 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{@Overridepublic void replaceSectionTitle(XWPFDocument document, XWPFParagraph paragraph) {String sectionTitle ="水果三";poiUtils.setLevelTitle1(document,paragraph,sectionTitle);}@Overridepublic void replaceBody(XWPFParagraph paragraph) {String body="在一次次水与云的交替变化中,蓦然发现无论自己是天边的一片云,还是大海中的一滴水," +"都是那毫不起眼的卑微之物,谁会去解读一片云的心绪,聆听一滴水的声音?经历了风起风落," +"也就习惯了云卷云舒,渐渐的我懂得了顺其自然,心境归于平和,不在向往风起云涌的气象," +"云淡风轻才是自己的本色。";poiUtils.setTextPro(paragraph,body);}@Overridepublic 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);}@Overridepublic void replaceChartName(XWPFParagraph paragraph) {String chartName="香蕉销售统计图(折线图)";poiUtils.setTableOrChartTitle(paragraph,chartName);}}

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

application.yml

#文档生成相关类配置doc:section:section1: com.poi.docgenerate.Section1section2: com.poi.docgenerate.Section2section3: com.poi.docgenerate.Section3

PoiPropConfig.java

import org.springframework.boot.context.properties.ConfigurationProperties;import org.ponent;import java.util.Map;@ConfigurationProperties(prefix = "doc")@Componentpublic class PoiPropsConfig {//把章节名作为key,章节类的全路径名作为valueprivate Map<String,String> section;public void setSection(Map<String, String> section) {this.section = section;}public Map<String, String> getSection() {return section;}@Overridepublic 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 mon.base.Strings;import com.police.hunan.judge.entity.vo.DocGenerateVO;import org.apache.poi.xwpf.usermodel.*;import org.openxmlformats.schemas.wordprocessingml.x.main.*;import org.ponent;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;@Componentpublic 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 barctStyle.setUiPriority(indentNumber);CTOnOff onoffnull = CTOnOff.Factory.newInstance();ctStyle.setUnhideWhenUsed(onoffnull);// style shows up in the formats barctStyle.setQFormat(onoffnull);// style defines a heading of the given levelCTPPr ppr = CTPPr.Factory.newInstance();ppr.setOutlineLvl(indentNumber);ctStyle.setPPr(ppr);XWPFStyle style = new XWPFStyle(ctStyle);// is a null op if already definedXWPFStyles 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;}}

结果

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。