前端富文本编辑后转DOCX,网上查看了很多方法,转出的docx文件的document.xml都没有内容,不能被POI解析,于是通过html标签转换,实现简单样式和内容的转换。使用的时候需要根据前端标签去修改标签值
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* html转word工具类
*
*/
@Slf4j
public class HtmlConvertWordUtil {
/**
* html转word的main方法
*
* @param html 需要转换的html
*/
public static XWPFDocument htmlConvertWord(String html) {
XWPFDocument doc = new XWPFDocument();
Integer abstractNumSize = 0;
// 通过Jsoup格式化html
Document parse = Jsoup.parse(html);
// 获取html中所有的标签元素
Elements es = parse.body().getAllElements();
// 父标签值
Set<String> parentTagNameSet = Arrays.stream(new String[]{"div", "p", "h1", "h2", "h3", "h4", "h5", "table", "ul", "ol", "hr"}).collect(Collectors.toSet());
// 筛选出所有的父级标签 限于p、h1、h2、h3、table
List<Element> tag1 = es.stream().filter(x -> parentTagNameSet.contains(x.tagName())).collect(Collectors.toList());
// 循环每个父标签 ,每个父标签是一个段落,将父标签下的内容以及子标签对应的内容放到父标签段落中
for (Element e : tag1) {
//创建段落)
abstractNumSize = createXWPFParagraph(doc, e, abstractNumSize);
}
return doc;
}
/**
* 构建段落
*
* @param docxDocument
* @param e
*/
public static Integer createXWPFParagraph(XWPFDocument docxDocument, Element e, Integer abstractNumSize) {
// 创建段落
XWPFParagraph paragraph = docxDocument.createParagraph();
// 存放样式
List<String> allStyles = new ArrayList<>();
// 创建内容并设置样式
abstractNumSize = createXWPFRun(docxDocument, paragraph, e, allStyles, abstractNumSize);
return abstractNumSize;
}
/**
* 创建段落内容
*
* @param docxDocument 目标文档
* @param paragraph 段落
* @param e html中的元素
* @param allStyles 样式
* @param abstractNumSize 排序编号编码(最好从0开始)
*/
public static Integer createXWPFRun(XWPFDocument docxDocument, XWPFParagraph paragraph, Element e, List<String> allStyles, Integer abstractNumSize) {
// 父标签 style中的样式
List<String> parentStyle = new ArrayList<>(Arrays.asList(e.attr("style") == null ? new String[0] : e.attr("style").split(";")));
allStyles.addAll(parentStyle);
// 父标签样式 只针对于h1、h2、h3、p
if (e.tagName().contains("div") || e.tagName().contains("h") || "p".equals(e.tagName())) {
allStyles.add(e.tagName() + ":");
}
// 父标签下的所有的子标签
List<Node> nodes = e.childNodes();
if (CollUtil.isNotEmpty(nodes)) {
// 单独处理表格
if ("table".equals(e.tagName())) {
// 在word中创建表格
XWPFTable table = docxDocument.createTable();
// 设置表格宽度
CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
width.setType(STTblWidth.DXA);
width.setW(BigInteger.valueOf(9072));
nodes = nodes.stream().filter(x -> x instanceof Element).collect(Collectors.toList());
// 获取tbody
Element tableBody = (Element) nodes.get(0);
// 在word中创建表格的时候 会默认创建一行
table.removeRow(0);
// 遍历tr 行
tableBody.childNodes().stream().filter(x -> x instanceof Element).map(x -> (Element) x).forEach(x -> {
// 创建行
XWPFTableRow row = table.createRow();
// 列的下标
AtomicInteger i = new AtomicInteger();
// 遍历td 列
x.childNodes().stream().filter(c -> c instanceof Element).map(c -> (Element) c).forEach(c -> {
i.getAndIncrement();
// 这个创建列 多行只要创建了第一行的列 下面所有的行都会有对应创建的列 所有如果多行列是一样的 只需要创建一次就可以
XWPFTableCell cell = row.getCell(i.intValue() - 1);
if (cell == null) {
cell = row.createCell();
}
// 单元格内容垂直居中
CTTcPr tcpr = cell.getCTTc().addNewTcPr();
CTVerticalJc va = tcpr.addNewVAlign();
va.setVal(STVerticalJc.CENTER);
// 暂存当前标签的样式
List<String> tempStyles = new ArrayList<>();
// 获取样式
tempStyles = cellStyle(c, tempStyles);
if (tempStyles.size() != 0) {
allStyles.addAll(tempStyles);
XWPFRun run = cell.addParagraph().createRun();
run.setText(c.text());
setFontStyle(allStyles, run, paragraph, docxDocument, c);
allStyles.removeAll(allStyles);
} else {
cell.setText(c.text());
}
});
});
} else if (StringUtils.equals("ol", e.tagName())) {
// 有序列表
abstractNumSize = createNumberList(docxDocument, e, allStyles, true, abstractNumSize);
} else if (StringUtils.equals("ul", e.tagName())) {
// 无序列表
abstractNumSize = createNumberList(docxDocument, e, allStyles, false, abstractNumSize);
}else {
for (Node node : nodes) {
// 创建段落内容
XWPFRun run = paragraph.createRun();
// 当子标签 为TextNode 的时候 说明这个子标签仅仅是文本 没有被标签修饰
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
run.setText(textNode.text().replaceAll(" ", ""));
// 设置样式 已父标签样式为基准
setFontStyle(allStyles, run, paragraph, docxDocument, null);
// 当子标签 为Element的时候 说明这个子标签不单单是文本,还包括标签 这时候需要根据对应标签的样式 修饰当前文本
} else if (node instanceof Element) {
Element children = (Element) node;
// 存放子标签中style样式
List<String> childrenStyle = new ArrayList<>(Arrays.asList(children.attr("style") == null ? new String[0] : children.attr("style").split(";")));
// 子标签名称
String tagName = children.tagName();
// 添加标签对应的样式
childrenStyle.add(addTagStyle(tagName, children));
// 将父子标签样式汇总
allStyles.addAll(childrenStyle);
// 查看子标签下是否还有子标签
List<Node> grandsons = children.childNodes().stream().filter(x -> x instanceof Element).collect(Collectors.toList());
if (grandsons != null && grandsons.size() != 0) {
// 如果子标签下还有字标签 样式需要包括当前子标签的样式
createXWPFRun(docxDocument, paragraph, children, allStyles, abstractNumSize);
}
// 查看当前标签 有没有包括内容 如果不加这个直接text()方法 他会获取当前标签的子标签的内容 造成内容重复的问题
List<Node> childrenNodes = children.childNodes().stream().filter(x -> x instanceof TextNode).collect(Collectors.toList());
if (childrenNodes != null && childrenNodes.size() != 0) {
// 子标签内容
String text = children.text();
// 这里设置内容需要排除超链接a标签 a标签需要特殊设置内容
List<String> aStyle = allStyles.stream().filter(x -> x.indexOf("a:") >= 0).collect(Collectors.toList());
if (aStyle == null || aStyle.size() == 0) {
run.setText(text.replaceAll(" ", ""));
}
setFontStyle(allStyles, run, paragraph, docxDocument, children);
}
// 去除子标签样式 当前标签的内容 仅限于当前标签 下个标签不能使用
allStyles.removeAll(childrenStyle);
}
}
}
} else {
// 横线 hr标签
if ("hr".equals(e.tagName())) {
// 创建段落内容
XWPFRun run = paragraph.createRun();
run.setText("_________________________________________________________________________"); // 用足够多的下划线字符填充
run.setUnderline(UnderlinePatterns.SINGLE);
// 可根据需要调整对齐方式
paragraph.setAlignment(ParagraphAlignment.CENTER);
}
}
return abstractNumSize;
}
/**
* 添加图片
* @param run
* @param pictureUrl
* @param fileName
* @return
*/
public static XWPFRun addPicture(XWPFRun run, String pictureUrl,String fileName) {
if (pictureUrl == null) {
return run;
}
URL url = null;
InputStream inputStream = null;
try {
pictureUrl = URLDecoder.decode(pictureUrl,"UTF-8");
url = new URL(pictureUrl);
inputStream = url.openConnection().getInputStream();
// 获取 图片类型 并添加图片
run.addPicture(inputStream, getPictureType(pictureUrl),fileName, Units.toEMU(400), Units.toEMU(256));
} catch (MalformedURLException e) {
throw new RuntimeException("图片url解析异常,url=" + pictureUrl);
} catch (IOException e) {
throw new RuntimeException("获取图片异常,url=" + pictureUrl);
} catch (InvalidFormatException e) {
throw new RuntimeException("添加图片异常,url=" + pictureUrl);
}
return run;
}
/**
* 根据图片类型,取得对应的图片类型代码
* @param picType
* @return int
*/
private static int getPictureType(String picType){
int res = XWPFDocument.PICTURE_TYPE_PICT;
if(picType != null){
if(picType.equalsIgnoreCase("png")){
res = XWPFDocument.PICTURE_TYPE_PNG;
}else if(picType.equalsIgnoreCase("dib")){
res = XWPFDocument.PICTURE_TYPE_DIB;
}else if(picType.equalsIgnoreCase("emf")){
res = XWPFDocument.PICTURE_TYPE_EMF;
}else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
res = XWPFDocument.PICTURE_TYPE_JPEG;
}else if(picType.equalsIgnoreCase("wmf")){
res = XWPFDocument.PICTURE_TYPE_WMF;
}
}
return res;
}
/**
* 循环取出表格中的字体样式
*
* @param c cell单元格
* @param tempStyles 样式
* @return
*/
private static List<String> cellStyle(Element c, List<String> tempStyles) {
List<Element> collect = c.childNodes().stream().filter(s -> s instanceof Element).map(s -> (Element) s).collect(Collectors.toList());
for (Element element : collect) {
tempStyles.add(addTagStyle(element.tagName(), element));
List<Node> childs = element.childNodes().stream().filter(s -> s instanceof Element).collect(Collectors.toList());
if (childs != null && childs.size() != 0) {
cellStyle(element, tempStyles);
}
}
return tempStyles;
}
/**
* 添加标签样式
*
* @param tagName 标签
* @param children 元素
* @return
*/
public static String addTagStyle(String tagName, Element children) {
String style = "";
switch (tagName) {
// 字体相关
case "font":
// 字体
if (StringUtils.isNotBlank(children.attr("font-family"))) {
style = "font-family:" + children.attr("font-family");
}
// 大小
if (StringUtils.isNotBlank(children.attr("font-size"))) {
style = "font-size:" + children.attr("font-size");
}
// 颜色
if (StringUtils.isNotBlank(children.attr("color"))) {
style = "color:" + children.attr("color");
}
break;
// 删除线
case "s":
style = "s:";
break;
// br标签
case "br":
style = "br:";
break;
// u标签 下划线
case "u":
style = "u:";
break;
// i标签 斜体
case "em":
style = "em:";
break;
// b标签 加粗
case "strong":
style = "strong:";
break;
// a 标签 超链接
case "a":
if (StringUtils.isNotBlank(children.attr("href"))) {
style = "a:" + children.attr("href");
}
break;
case "sup":
style = "sup:";
break;
case "sub":
style = "sub:";
break;
}
return style;
}
/**
* 设置内容样式
*
* @param styles 样式
* @param run 需要赋予样式的对象 XWPFRun
* @param paragraph 段落
* @param docxDocument 目标文档
*/
public static void setFontStyle(List<String> styles, XWPFRun run, XWPFParagraph paragraph, XWPFDocument docxDocument, Element children) {
// 自定义样式排序 父标签样式在前 子标签样式在后 子标签样式可以覆盖父标签样式
Collections.sort(styles, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1.contains("h") || "p".equals(o1)||o1.contains("div")) {
return -1;
} else if ("p".equals(o2) || o2.contains("h")||o2.contains("div")) {
return 1;
} else {
return 0;
}
}
});
for (String styleValue : styles) {
if (StringUtils.isBlank(styleValue)) {
continue;
}
// 获取style中的样式
String style = styleValue.substring(0, styleValue.indexOf(":")).replaceAll(" ", "");
// style样式对应的值
String value = styleValue.substring(styleValue.indexOf(":") + 1).replaceAll(" ", "");
switch (style) {
/*--------------------------------标签对应的样式 例如p 、h1、h2、h3*/
// 正文
case "p":
//对齐方式
//paragraph.setAlignment(ParagraphAlignment.BOTH);
//首行缩进:567==1厘米
//paragraph.setIndentationFirstLine(567);
break;
case "div":
// 对齐方式
paragraph.setAlignment(ParagraphAlignment.BOTH);
// 首行缩进:567==1厘米
paragraph.setIndentationFirstLine(567);
break;
// h1标题
case "h1":
addCustomHeadingStyle(docxDocument, "标题 1", 1);
paragraph.setStyle("标题 1");
run.setBold(true);
run.setColor("000000");
run.setFontFamily("宋体");
run.setFontSize(20);
break;
// h2 标题
case "h2":
addCustomHeadingStyle(docxDocument, "标题 2", 2);
paragraph.setStyle("标题 2");
run.setBold(true);
run.setColor("000000");
run.setFontFamily("宋体");
run.setFontSize(18);
break;
// h3标题
case "h3":
addCustomHeadingStyle(docxDocument, "标题 3", 3);
paragraph.setStyle("标题 3");
run.setBold(true);
run.setColor("000000");
run.setFontFamily("宋体");
run.setFontSize(16);
break;
case "h4":
addCustomHeadingStyle(docxDocument, "标题 4", 4);
paragraph.setStyle("标题 4");
run.setBold(true);
run.setColor("000000");
run.setFontFamily("宋体");
run.setFontSize(14);
break;
case "h5":
addCustomHeadingStyle(docxDocument, "标题 5", 5);
paragraph.setStyle("标题 5");
run.setBold(true);
run.setColor("000000");
run.setFontFamily("宋体");
run.setFontSize(12);
break;
// 行高 行间距
case "line-height":
double tempDouble = Double.parseDouble(value);
int height = (int) Math.round(tempDouble);
run.setTextPosition(height);
break;
/* 左内边距 因为单位是em 暂不处理此标签
case "padding-left":
CTSectPr sectPr = docxDocument.getDocument().getBody().addNewSectPr();
CTPageMar pageMar = sectPr.addNewPgMar();
pageMar.setLeft(BigInteger.valueOf(720L));
break;*/
// 首行缩进
case "text-indent":
int twips = 0;
if (StringUtils.endsWith(value, "pt")) {
String indent = value.replaceAll("pt", "");
Double pt = Double.parseDouble(indent) * 20;// pt=20twips
twips = pt.intValue();
} else if (StringUtils.endsWith(value, "px")) {
String indent = value.replaceAll("px", "");
Double px = Double.parseDouble(indent) * 15;// px=15twips
twips = px.intValue();
} else if (StringUtils.endsWith(value, "em")) {
String indent = value.replaceAll("em", "");
Double em = Double.parseDouble(indent);
int fontSize = run.getFontSize() > 0 ? run.getFontSize() : 12;
twips = fontSize * em.intValue() * 15;
}
paragraph.setIndentationFirstLine(twips);
break;
case "font-family":
CTFonts ctFonts = run.getCTR().addNewRPr().addNewRFonts();
// 设置中文字体
ctFonts.setEastAsia(value);
// 设置英文数字字体
ctFonts.setAscii(value);
break;
// 大小
case "font-size":
if (StringUtils.isNotBlank(value)) {
int size = Integer.parseInt(value.replaceAll("px", ""));
size = (size * 72) / 96;
run.setFontSize(size);
}
// run.setFontSize(fontSizeConvert(value));
break;
// 颜色
case "color":
run.setColor(rgbToColorValue(value));
break;
// 删除线
case "s":
run.setStrikeThrough(true);
break;
// br 换行
case "br":
run.addCarriageReturn();
break;
// u 下划线
case "u":
run.setUnderline(UnderlinePatterns.SINGLE);
break;
// em 斜体
case "em":
run.setItalic(true);
break;
// strong 加粗
case "strong":
run.setBold(true);
break;
// background-color 背景色
case "background-color":
// 高亮(不使用)
// run.getCTR().addNewRPr().addNewHighlight().setVal(getBackground(value));
// 背景色
CTShd shd = run.getCTR().addNewRPr().addNewShd();
shd.setFill("auto"); // 设置填充模式
shd.setColor(rgbToColorValue(value)); // 自定义颜色,这里是紫色(RGB: 255, 0, 255)
shd.setVal(STShd.SOLID); // 设置阴影样式,例如20%透明度
break;
// a 超连接
case "a":
// 创建一个超链接运行并设置文本
XWPFHyperlinkRun hyperlinkRun = paragraph.createHyperlinkRun(value);
hyperlinkRun.setText((children.text()));
// 设置链接颜色和下划线(可选)
hyperlinkRun.setColor("0000FF");
hyperlinkRun.setUnderline(UnderlinePatterns.SINGLE);
break;
// 文本对齐方式
case "text-align":
if (StringUtils.equals("center", value)) {
paragraph.setAlignment(ParagraphAlignment.CENTER);
} else if (StringUtils.equals("left", value)) {
paragraph.setAlignment(ParagraphAlignment.LEFT);
} else if (StringUtils.equals("right", value)) {
paragraph.setAlignment(ParagraphAlignment.RIGHT);
} else if (StringUtils.equals("justify", value)) {
paragraph.setAlignment(ParagraphAlignment.BOTH);
}
break;
// 上标
case "sup":
run.setSubscript(VerticalAlign.SUPERSCRIPT);
break;
// 下标
case "sub":
run.setSubscript(VerticalAlign.SUBSCRIPT);
break;
}
}
}
/**
* 增加自定义标题样式。这里用的是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);
}
/**
* 字体像素大小转换为word字体大小
*
* @param level
* @return
*/
public static Integer fontSizeConvert(String level) {
Integer fontSize = null;
if (StringUtils.isBlank(level)) {
return fontSize;
}
switch (level) {
// 对应像素大小 10px
case "1":
fontSize = 7;
break;
// 对应像素大小 13px
case "2":
fontSize = 8;
break;
// 对应像素大小 16px
case "3":
fontSize = 9;
break;
// 对应像素大小 18px
case "4":
fontSize = 10;
break;
// 对应像素大小 24px
case "5":
fontSize = 14;
break;
// 对应像素大小 32px
case "6":
fontSize = 18;
break;
// 对应像素大小 48px
case "7":
fontSize = 28;
break;
case "8":
fontSize = 36;
break;
case "9":
fontSize = 48;
break;
case "10":
fontSize = 72;
break;
default:
fontSize = 5;
}
return fontSize;
}
/**
* 17 种标准色是 aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, white, yellow。
*
* @param color
* @return
* @date 2020年4月7日 下午7:16:39
*/
public static STHighlightColor.Enum getBackground(String color) {
color = color.replaceAll(" ", "");
if ("yellow".equals(color) || "rgb(255,255,0)".equals(color) || "#FFFF00".equals(color)) {
//1-黄色
return STHighlightColor.YELLOW;
} else if ("lime".equals(color) || "rgb(0,255,0)".equals(color) || "#00FF00".equals(color)) {
//2-绿色
return STHighlightColor.GREEN;
} else if ("aqua".equals(color) || "rgb(0,255,255)".equals(color) || "#00FFFF".equals(color)) {
//3-青色
return STHighlightColor.CYAN;
} else if ("fuchsia".equals(color) || "rgb(255,0,255)".equals(color) || "#FF00FF".equals(color)) {
//4-粉红色
return STHighlightColor.MAGENTA;
} else if ("blue".equals(color) || "rgb(0,0,255)".equals(color) || "#0000FF".equals(color)) {
//5-蓝色
return STHighlightColor.BLUE;
} else if ("red".equals(color) || "rgb(255,0,0)".equals(color) || "#FF0000".equals(color)) {
//6-红色
return STHighlightColor.RED;
} else if ("navy".equals(color) || "rgb(0,0,128)".equals(color) || "#000080".equals(color)) {
//7-深蓝色
return STHighlightColor.DARK_BLUE;
} else if ("teal".equals(color) || "rgb(0,128,128)".equals(color) || "#008080".equals(color)) {
//8-深青色
return STHighlightColor.DARK_CYAN;
} else if ("green".equals(color) || "rgb(0,128,0)".equals(color) || "#008000".equals(color)) {
//9-深绿色
return STHighlightColor.DARK_GREEN;
} else if ("purple".equals(color) || "rgb(128,0,128)".equals(color) || "#800080".equals(color)) {
//10-深粉红色,紫色
return STHighlightColor.DARK_MAGENTA;
} else if ("maroon".equals(color) || "rgb(128,0,0)".equals(color) || "#800000".equals(color)) {
//11-深红色
return STHighlightColor.DARK_RED;
} else if ("olive".equals(color) || "rgb(128,128,0)".equals(color) || "#808000".equals(color)) {
//12-深黄色
return STHighlightColor.DARK_YELLOW;
} else if ("gray".equals(color) || "rgb(128,128,128)".equals(color) || "#808080".equals(color)) {
//13-深灰色
return STHighlightColor.DARK_GRAY;
} else if ("silver".equals(color) || "rgb(192,192,192)".equals(color) || "#C0C0C0".equals(color)) {
//14-浅灰色
return STHighlightColor.LIGHT_GRAY;
} else if ("black".equals(color) || "rgb(0,0,0)".equals(color) || "#000000".equals(color)) {
//15-黑色
return STHighlightColor.BLACK;
} else {
//无色
return STHighlightColor.NONE;
}
}
/**
* rgb(245, 219, 77) 转十六进制 #F5DB4D 并去除#
* @param rgb rgb(245, 219, 77)
* @return F5DB4D
*/
public static String rgbToColorValue(String rgb) {
String[] rgbValues = rgb.substring(rgb.indexOf('(') + 1, rgb.lastIndexOf(')')).split(",");
// 转十六进制
String hexColor = String.format("#%02X%02X%02X", Integer.parseInt(rgbValues[0]), Integer.parseInt(rgbValues[1]), Integer.parseInt(rgbValues[2]));
// 在POI中去掉'#'符号
return hexColor.substring(1);
}
/**
* 创建列表(有序,无序)
*/
public static Integer createNumberList(XWPFDocument docxDocument, Element e, List<String> allStyles, boolean isOl, Integer abstractNumSize) {
List<Node> nodes = e.childNodes();
// 初始化文档的编号定义部分
XWPFNumbering numbering = docxDocument.getNumbering();
if (ObjectUtil.isNull(numbering)) {
numbering = docxDocument.createNumbering();
}
while (ObjectUtil.isNotNull(numbering.getAbstractNum(BigInteger.valueOf(abstractNumSize)))) {
abstractNumSize++;
}
// 创建有序列表编号定义
CTAbstractNum ctAbstractNum = CTAbstractNum.Factory.newInstance();
XWPFAbstractNum abstractNum = new XWPFAbstractNum(ctAbstractNum);
abstractNum.getAbstractNum().setAbstractNumId(BigInteger.valueOf(abstractNumSize));
// 样式
CTLvl ctLvl = ctAbstractNum.addNewLvl();
if (isOl) {
// 层数
ctLvl.setIlvl(BigInteger.ZERO);
// 设置为十进制格式
ctLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
// // 设置起始数字(对于有序列表)
ctLvl.addNewStart().setVal(BigInteger.valueOf(1));
ctLvl.addNewLvlText().setVal("%1.");
} else {
// 设置实心圆点
CTNumFmt ctNumFmt = CTNumFmt.Factory.newInstance();
ctNumFmt.setVal(STNumberFormat.BULLET);
ctLvl.setNumFmt(ctNumFmt);
CTLevelText ctLevelText = ctLvl.addNewLvlText();
ctLevelText.setVal("\u2022"); // 设置实心圆点作为项目符号
}
CTJc ctJc = ctLvl.addNewLvlJc();
ctJc.setVal(STJc.LEFT);
// 添加有序列表定义到文档的编号系统
numbering.addAbstractNum(abstractNum);
BigInteger numId = numbering.addNum(BigInteger.valueOf(abstractNumSize));
// 创建有序列表项
for (Node node : nodes) {
// 创建段落并关联到有序列表
XWPFParagraph ulParagraph = docxDocument.createParagraph();
ulParagraph.setNumID(numId);
// 创建文本运行并添加内容
Element children = (Element) node;
abstractNumSize = createXWPFRun(docxDocument, ulParagraph, children, allStyles, abstractNumSize);
}
return abstractNumSize++;
}
}