pom层
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>3.3.6</version>
</dependency>
<!--word操作工具类-->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.6.0-beta1</version>
</dependency>
application-dev.yml
# 模板导出相关 本地环境
word:
temDir: E:/test # 临时文件夹
titleUrl: word/content.docx # 富文本模板地址
模板:
controller层
可以自定义<html>
在Enum层 和Util配置好对应的格式
public class ExcelController {
@Value("${word.temDir}")
private String exportWordTemDir;
@Value("${word.titleUrl}")
private String exportWordTitleUrl;
@Autowired
private AdminApproveService adminApproveService;
@Autowired
private AdminTitleService adminTitleService;
/**
* 批量导出正文内容
*
* @param request
* @param response
* @return
* @throws IOException
*/
@ApiOperation("批量导出标题正文")
@PostMapping("/conteneWrod")
public CommonResult conteneWrod(@RequestBody Integer[] ids, HttpServletRequest request, HttpServletResponse response) throws Exception {
InputStream in = null;
XWPFDocument doc = null;
in = Thread.currentThread().getContextClassLoader().getResourceAsStream(exportWordTitleUrl);
OPCPackage srcPackage = OPCPackage.open(in);
doc = new XWPFDocument(srcPackage);
List<ApproveVo> titleApprovalList = adminApproveService.listByIds(ids);
List<Map<String, Object>> mapList = new ArrayList<>();
for (ApproveVo approveVo : titleApprovalList) {
List<TitleVo> titlesList = adminTitleService.queryApprovalById(approveVo.getId());
for (TitleVo titleVo : titlesList) {
Map<String, Object> param = new HashMap<>();
String format = DateUtils.format(approveVo.getReleaseTime());
String content = "<h2>" + titleVo.getTitleName() + "</h2>" + "<h7>" + format + "审批表标题正文内容" + "</h7>" + titleVo.getContent() + "<br>" + "<br>" + "<br>" + "<br>";
String contents = RichTextToDocxutil.txt2String(content);
param.put("content", contents);
mapList.add(param);
}
}
XWPFDocumentUtil.wordInsertRitchText(doc, mapList);
// 将doc输出到指定目录
ExcelUtils.exportWordList(doc, exportWordTemDir, "标题表.docx", request, response);
return CommonResult.successMsg("导出成功");
}
}
Util层
RichTextToDocxutil工具类:
package com.jshop.common.utils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author corey
* @version 1.0
* @date 2020/5/3 5:51 下午
* @Desc 将富文本txt导出成word
*/
public class RichTextToDocxutil {
/**
* 导出富本框到docx
*/
public static void outRichTextToDocx(String contents ,String outFilePath) {
String content = txt2String(contents);
InputStream inputStream=null;
OutputStream out = null;
try {
// 输入富文本内容,返回字节数组
byte[] result = HtmlToWord.resolveHtml(content);
//输出文件
out = new FileOutputStream(outFilePath);
out.write(result);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取html文件的内容
*
* @param content 读取富文本
* @return 返回文件内容
*/
public static String txt2String(String content) {
StringBuilder result = new StringBuilder();
try {
// 构造一个BufferedReader类来读取富文本
result.append(System.lineSeparator()+content);
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
}
}
XWPFDocumentUtil工具类:
package com.jshop.common.utils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import java.util.List;
import java.util.Map;
/**
* @author corey
* @version 1.0
* @date 2020/5/5 2:04 下午
*/
public class XWPFDocumentUtil {
/**
* 往doc的标记位置插入富文本内容 注意:目前支持富文本里面带url的图片,不支持base64编码的图片
*
* @param doc 需要插入内容的Word
* @param ritchtextMap 标记位置对应的富文本内容
* @param
*/
public static void wordInsertRitchText(XWPFDocument doc, List<Map<String, Object>> ritchtextMap) {
try {
int i = 0;
long beginTime = System.currentTimeMillis();
// 如果需要替换多份富文本,通过Map来操作,key:要替换的标记,value:要替换的富文本内容
for (Map<String, Object> mapList : ritchtextMap) {
for (Map.Entry<String, Object> entry : mapList.entrySet()) {
i++;
for (XWPFParagraph paragraph : doc.getParagraphs()) {
if (entry.getKey().equals(paragraph.getText().trim())) {
// 在标记处插入指定富文本内容
HtmlUtils.resolveHtml(entry.getValue().toString(), doc, paragraph);
if (i == ritchtextMap.size()) {
//当导出最后一个富文本时 删除需要替换的标记
doc.removeBodyElement(doc.getPosOfParagraph(paragraph));
}
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
HtmlUtils工具类:
package com.jshop.common.utils;
import com.jshop.common.constant.CommonConStant;
import com.jshop.common.enums.ElementEnum;
import com.jshop.common.enums.TitleFontEnum;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
public class HtmlUtils {
/**
* 给document添加指定元素
*
* @param document
*/
public static void addElement(Document document) {
if (ObjectUtils.isEmpty(document)) {
throw new NullPointerException("不允许为空的对象添加元素");
}
Elements elements = document.getAllElements();
for (Element e : elements) {
String attrName = ElementEnum.getValueByCode(e.tag().getName());
if (!StringUtils.isEmpty(attrName)) {
e.attr(CommonConStant.COMMONATTR, attrName);
}
}
}
/**
* 将富文本内容写入到Word
* 因富文本样式种类繁多,不能一一枚举,目前实现了H1、H2、H3、段落、图片、表格枚举
*
* @param ritchText 富文本内容
* @param doc 需要写入富文本内容的Word 写入图片和表格需要用到
* @param paragraph
* @param
*/
public static void resolveHtml(String ritchText, XWPFDocument doc, XWPFParagraph paragraph) {
Document document = Jsoup.parseBodyFragment(ritchText, "UTF-8");
try {
// 添加固定元素
HtmlUtils.addElement(document);
Elements elements = document.select("[" + CommonConStant.COMMONATTR + "]");
for (Element em : elements) {
XmlCursor xmlCursor = paragraph.getCTP().newCursor();
switch (em.attr(CommonConStant.COMMONATTR)) {
case "title":
break;
case "subtitle":
break;
case "imgurl":
String src = em.attr("src");
URL url = new URL(src);
URLConnection uc = url.openConnection();
InputStream inputStream = uc.getInputStream();
XWPFParagraph imgurlparagraph = doc.insertNewParagraph(xmlCursor);
ParagraphStyleUtil.setImageCenter(imgurlparagraph);
imgurlparagraph.createRun().addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, "图片.jpeg", Units.toEMU(150), Units.toEMU(150));
closeStream(inputStream);
File file = new File("picture.jpg");
boolean exists = file.exists();
if (exists) {
file.delete();
}
break;
case "imgbase64":
break;
case "table":
XWPFTable xwpfTable = doc.insertNewTbl(xmlCursor);
addTable(xwpfTable, em);
// 设置表格居中
ParagraphStyleUtil.setTableLocation(xwpfTable, "center");
// 设置内容居中
ParagraphStyleUtil.setCellLocation(xwpfTable, "CENTER", "center");
break;
case "h1":
XWPFParagraph h1paragraph = doc.insertNewParagraph(xmlCursor);
XWPFRun xwpfRun_1 = h1paragraph.createRun();
xwpfRun_1.setText(em.text());
//居中
ParagraphStyleUtil.setImageCenter(h1paragraph);
// 设置字体
ParagraphStyleUtil.setTitle(xwpfRun_1, TitleFontEnum.H1.getTitle());
break;
case "h2":
XWPFParagraph h2paragraph = doc.insertNewParagraph(xmlCursor);
XWPFRun xwpfRun_2 = h2paragraph.createRun();
xwpfRun_2.setText(em.text());
//居中
ParagraphStyleUtil.setImageCenter(h2paragraph);
// 设置字体
ParagraphStyleUtil.setTitle(xwpfRun_2, TitleFontEnum.H2.getTitle());
break;
case "h3":
XWPFParagraph h3paragraph = doc.insertNewParagraph(xmlCursor);
XWPFRun xwpfRun_3 = h3paragraph.createRun();
xwpfRun_3.setText(em.text());
// 设置字体
ParagraphStyleUtil.setTitle(xwpfRun_3, TitleFontEnum.H3.getTitle());
break;
case "paragraph":
XWPFParagraph paragraphd = doc.insertNewParagraph(xmlCursor);
// 设置段落缩进 4个空格
paragraphd.createRun().setText(" " + em.text());
break;
case "br":
XWPFParagraph br = doc.insertNewParagraph(xmlCursor);
XWPFRun run = br.createRun();
run.addBreak(BreakType.TEXT_WRAPPING);
case "h7":
XWPFParagraph h7paragraph = doc.insertNewParagraph(xmlCursor);
XWPFRun xwpfRun_7 = h7paragraph.createRun();
xwpfRun_7.setText(em.text());
//居左
ParagraphStyleUtil.AlignmentRight(h7paragraph);
// 设置字体
ParagraphStyleUtil.setTitle(xwpfRun_7, TitleFontEnum.H7.getTitle());
default:
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭输入流
*
* @param closeables
*/
public static void closeStream(Closeable... closeables) {
for (Closeable c : closeables) {
if (c != null) {
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将富文本的表格转换为Word里面的表格
*/
private static void addTable(XWPFTable xwpfTable, Element table) {
Elements trs = table.getElementsByTag("tr");
// XWPFTableRow 第0行特殊处理
int rownum = 0;
for (Element tr : trs) {
addTableTr(xwpfTable, tr, rownum);
rownum++;
}
}
/**
* 将元素里面的tr 提取到 xwpfTabel
*/
private static void addTableTr(XWPFTable xwpfTable, Element tr, int rownum) {
Elements tds = tr.getElementsByTag("th").isEmpty() ? tr.getElementsByTag("td") : tr.getElementsByTag("th");
XWPFTableRow row_1 = null;
for (int i = 0, j = tds.size(); i < j; i++) {
if (0 == rownum) {
// XWPFTableRow 第0行特殊处理,
XWPFTableRow row_0 = xwpfTable.getRow(0);
if (i == 0) {
row_0.getCell(0).setText(tds.get(i).text());
} else {
row_0.addNewTableCell().setText(tds.get(i).text());
}
} else {
if (i == 0) {
// 换行需要创建一个新行
row_1 = xwpfTable.createRow();
row_1.getCell(i).setText(tds.get(i).text());
} else {
row_1.getCell(i).setText(tds.get(i).text());
}
}
}
}
}
ParagraphStyleUtil工具类
package com.jshop.common.utils;
import com.jshop.common.enums.TitleFontEnum;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.util.List;
/**
* 设置文本样式工具,因为Word样式种类繁多,不能一一枚举
* @author corey
* @version 1.0
* @date 2020/5/5 9:36 下午
*/
public class ParagraphStyleUtil {
/**
* 段落缩进
* @param paragraph
*/
public static void setIndentationFirstLine(XWPFParagraph paragraph){
paragraph.setFirstLineIndent(400);
}
/**
* 设置标题 根据富文本的tag来判断
* @param run
* @param title
*/
public static void setTitle(XWPFRun run, String title){
// 加粗
run.setBold(true);
run.setFontSize(TitleFontEnum.getFontByTitle(title));
}
/**
* 设置单元格水平位置和垂直位置
*
* @param xwpfTable
* @param verticalLoction 单元格中内容垂直上TOP,下BOTTOM,居中CENTER,BOTH两端对齐
* @param horizontalLocation 单元格中内容水平居中center,left居左,right居右,both两端对齐
*/
public static void setCellLocation(XWPFTable xwpfTable, String verticalLoction, String horizontalLocation) {
List<XWPFTableRow> rows = xwpfTable.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
CTTc cttc = cell.getCTTc();
CTP ctp = cttc.getPList().get(0);
CTPPr ctppr = ctp.getPPr();
if (ctppr == null) {
ctppr = ctp.addNewPPr();
}
CTJc ctjc = ctppr.getJc();
if (ctjc == null) {
ctjc = ctppr.addNewJc();
}
ctjc.setVal(STJc.Enum.forString(horizontalLocation)); //水平居中
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.valueOf(verticalLoction));//垂直居中
}
}
}
/**
* 设置表格位置
*
* @param xwpfTable
* @param location 整个表格居中center,left居左,right居右,both两端对齐
*/
public static void setTableLocation(XWPFTable xwpfTable, String location) {
CTTbl cttbl = xwpfTable.getCTTbl();
CTTblPr tblpr = cttbl.getTblPr() == null ? cttbl.addNewTblPr() : cttbl.getTblPr();
CTJc cTJc = tblpr.addNewJc();
cTJc.setVal(STJc.Enum.forString(location));
}
/**
* 设置图片居中
* @param xwpfParagraph
*/
public static void setImageCenter(XWPFParagraph xwpfParagraph){
//居中
xwpfParagraph.setAlignment(ParagraphAlignment.CENTER);
}
/**
*居左
*/
public static void AlignmentLeft(XWPFParagraph xwpfParagraph){
//左
xwpfParagraph.setAlignment(ParagraphAlignment.LEFT);
}
/**
*居右
*/
public static void AlignmentRight(XWPFParagraph xwpfParagraph){
//右
xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
}
}
HtmlToWordUtil工具类:
package com.jshop.common.utils;
import com.jshop.common.constant.CommonConStant;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.*;
import org.docx4j.wml.PPrBase.Ind;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.math.BigInteger;
import java.util.List;
/**
* @program: htmltoword
* @description: html 转 docx
* @author: corey
* @create: 2020-04-29 14:10
**/
public class HtmlToWord {
private static ObjectFactory factory;
private static WordprocessingMLPackage wordMLPackage;
/**
* 将一段富文本字符串转为一个字节数组
* @param data
* @return
*/
public static byte[] resolveHtml(String data) {
Document document = Jsoup.parseBodyFragment(data, "UTF-8");
ByteArrayOutputStream out = null;
try {
wordMLPackage = WordprocessingMLPackage.createPackage();
factory = Context.getWmlObjectFactory();
Relationship relationship = createFooterPart();
createFooterReference(relationship);
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
alterStyleSheet();
// 添加固定元素
HtmlUtils.addElement(document);
Elements elements = document.select("["+ CommonConStant.COMMONATTR+"]");
for (Element em : elements) {
switch (em.attr(CommonConStant.COMMONATTR)) {
case "title":
documentPart.addStyledParagraphOfText("Title", em.text());
break;
case "subtitle":
documentPart.addStyledParagraphOfText("Subtitle", em.text());
break;
case "imgurl":
String imgSrc = em.attr("src");
File file = new File(imgSrc);
byte[] bytes = convertImageToByteArray(file);
addImageToPackage(wordMLPackage, bytes);
break;
case "imgbase64":
break;
case "table":
Tbl table = addTable(em);
documentPart.addObject(table);
break;
case "h1":
documentPart.addStyledParagraphOfText("Heading1", em.text());
break;
case "h2":
documentPart.addStyledParagraphOfText("Heading2", em.text());
break;
case "h3":
documentPart.addStyledParagraphOfText("Heading3", em.text());
break;
case "paragraph":
P p = addParapraph(em.text());
//设置首行缩进
setFirstLine(p,"400");
documentPart.getContent().add(p);
break;
default:
documentPart.addParagraphOfText(em.text());
break;
}
}
addPageBreak(documentPart);
out = new ByteArrayOutputStream();
wordMLPackage.save(out);
return out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* @Title: addParapraph
* @Description: (文本转段落)
* @param @param text
* @param @return 设定文件
* @return P 返回类型
* @throws
*/
private static P addParapraph(String text) {
factory= Context.getWmlObjectFactory();
P paragraph = factory.createP();
Text t = factory.createText();
t.setValue(text);
R run = factory.createR();
run.getContent().add(t);
paragraph.getContent().add(run);
RPr runProperties = factory.createRPr();
run.setRPr(runProperties);
return paragraph;
}
/**
* @Title: setFirstLine
* @param @param p
* @param @param str 设定文件
* @return void 返回类型
* @throws
*/
private static void setFirstLine(P p , String str) {
PPr ppr = getPPr(p);
Ind ind = ppr.getInd();
if (ind == null) {
ind = new Ind();
ppr.setInd(ind);
}
ind.setFirstLine(new BigInteger(str));
};
private static PPr getPPr(P p) {
PPr ppr = p.getPPr();
if (ppr == null) {
ppr = new PPr();
p.setPPr(ppr);
}
return ppr;
}
/**
* table @param @return 设定文件 @return Tbl 返回类型 @throws
*/
private static Tbl addTable(Element table) {
factory = Context.getWmlObjectFactory();
Tbl tbl = factory.createTbl();
addBorders(tbl);
Elements trs = table.getElementsByTag("tr");
for (Element tr : trs) {
Tr fTr = addTableTr(tr);
tbl.getContent().add(fTr);
}
return tbl;
}
/**
* tr @param @return 设定文件 @return Tr 返回类型 @throws
*/
private static Tr addTableTr(Element tr) {
Elements tds = tr.getElementsByTag("th").isEmpty() ? tr.getElementsByTag("td") : tr.getElementsByTag("th");
Tr ftr = factory.createTr();
for (int i = 0, j = tds.size(); i < j; i++) {
Tc ftd = factory.createTc();
setCellWidth(ftd, 1000);
ftd.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(tds.get(i).text()));
ftr.getContent().add(ftd);
}
return ftr;
}
/**
* 本方法创建一个单元格属性集对象和一个表格宽度对象. 将给定的宽度设置到宽度对象然后将其添加到 属性集对象. 最后将属性集对象设置到单元格中.
*/
private static void setCellWidth(Tc tableCell, int width) {
TcPr tableCellProperties = new TcPr();
TblWidth tableWidth = new TblWidth();
tableWidth.setW(BigInteger.valueOf(width));
tableCellProperties.setTcW(tableWidth);
tableCell.setTcPr(tableCellProperties);
}
/**
* 本方法为表格添加边框
*/
private static void addBorders(Tbl table) {
table.setTblPr(new TblPr());
CTBorder border = new CTBorder();
border.setColor("auto");
border.setSz(new BigInteger("4"));
border.setSpace(new BigInteger("0"));
border.setVal(STBorder.SINGLE);
TblBorders borders = new TblBorders();
borders.setBottom(border);
borders.setLeft(border);
borders.setRight(border);
borders.setTop(border);
borders.setInsideH(border);
borders.setInsideV(border);
table.getTblPr().setTblBorders(borders);
}
/**
* 将图片从文件对象转换成字节数组.
*
* @param file
* 将要转换的文件
* @return 包含图片字节数据的字节数组
* @throws FileNotFoundException
* @throws IOException
*/
private static byte[] convertImageToByteArray(File file) throws FileNotFoundException, IOException {
InputStream is = new FileInputStream(file);
long length = file.length();
// 不能使用long类型创建数组, 需要用int类型.
if (length > Integer.MAX_VALUE) {
System.out.println("File too large!!");
}
byte[] bytes = new byte[(int) length];
int offset = 0;
int numRead = 0;
while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// 确认所有的字节都没读取
if (offset < bytes.length) {
System.out.println("Could not completely read file " + file.getName());
}
is.close();
return bytes;
}
/**
* Docx4j拥有一个由字节数组创建图片部件的工具方法, 随后将其添加到给定的包中. 为了能将图片添加 到一个段落中, 我们需要将图片转换成内联对象.
* 这也有一个方法, 方法需要文件名提示, 替换文本, 两个id标识符和一个是嵌入还是链接到的指示作为参数. 一个id用于文档中绘图对象不可见的属性,
* 另一个id用于图片本身不可见的绘制属性. 最后我们将内联 对象添加到段落中并将段落添加到包的主文档部件.
*
* @param wordMLPackage
* 要添加图片的包
* @param bytes
* 图片对应的字节数组
* @throws Exception
* 不幸的createImageInline方法抛出一个异常(没有更多具体的异常类型)
*/
private static void addImageToPackage(WordprocessingMLPackage wordMLPackage, byte[] bytes) throws Exception {
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
int docPrId = 1;
int cNvPrId = 2;
Inline inline = imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);
P paragraph = addInlineImageToParagraph(inline);
wordMLPackage.getMainDocumentPart().addObject(paragraph);
}
/**
* 创建一个对象工厂并用它创建一个段落和一个可运行块R. 然后将可运行块添加到段落中. 接下来创建一个图画并将其添加到可运行块R中. 最后我们将内联
* 对象添加到图画中并返回段落对象.
*
* @param inline
* 包含图片的内联对象.
* @return 包含图片的段落
*/
private static P addInlineImageToParagraph(Inline inline) {
// 添加内联对象到一个段落中
ObjectFactory factory = new ObjectFactory();
P paragraph = factory.createP();
R run = factory.createR();
paragraph.getContent().add(run);
Drawing drawing = factory.createDrawing();
run.getContent().add(drawing);
drawing.getAnchorOrInline().add(inline);
return paragraph;
}
/**
* This method alters the default style sheet that is part of each document.
*
* To do this, we first retrieve the style sheet from the package and then get
* the Styles object from it. From this object, we get the list of actual styles
* and iterate over them. We check against all styles we want to alter and apply
* the alterations if applicable.
*
* @param
*/
public static void alterStyleSheet() {
StyleDefinitionsPart styleDefinitionsPart = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart();
Styles styles = null;
try {
styles = styleDefinitionsPart.getContents();
} catch (Docx4JException e) {
e.printStackTrace();
}
List<Style> stylesList = styles.getStyle();
for (Style style : stylesList) {
if (style.getStyleId().equals("Normal")) {
alterNormalStyle(style);
} else if (style.getStyleId().equals("Heading1")) {
alterHeading1Style(style);
} else if (style.getStyleId().equals("Heading2")) {
alterHeading2Style(style);
} else if (style.getStyleId().equals("Title") || style.getStyleId().equals("Subtitle")) {
getRunPropertiesAndRemoveThemeInfo(style);
}
}
}
/**
* First we create a run properties object as we want to remove nearly all of
* the existing styling. Then we change the font and font size and set the run
* properties on the given style. As in previous examples, the font size is
* defined to be in half-point size.
*/
private static void alterNormalStyle(Style style) {
// we want to change (or remove) almost all the run properties of the
// normal style, so we create a new one.
RPr rpr = new RPr();
changeFontToArial(rpr);
changeFontSize(rpr, 20);
style.setRPr(rpr);
}
/**
* For this style, we get the existing run properties from the style and remove
* the theme font information from them. Then we also remove the bold styling,
* change the font size (half-points) and add an underline.
*/
private static void alterHeading1Style(Style style) {
RPr rpr = getRunPropertiesAndRemoveThemeInfo(style);
removeBoldStyle(rpr);
changeFontSize(rpr, 28);
/* addUnderline(rpr); */
}
private static void alterHeading2Style(Style style) {
RPr rpr = getRunPropertiesAndRemoveThemeInfo(style);
removeBoldStyle(rpr);
changeFontSize(rpr, 24);
/* addUnderline(rpr); */
}
private static RPr getRunPropertiesAndRemoveThemeInfo(Style style) {
// We only want to change some settings, so we get the existing run
// properties from the style.
RPr rpr = style.getRPr();
removeThemeFontInformation(rpr);
return rpr;
}
/**
* Change the font of the given run properties to Arial.
*
* A run font specifies the fonts which shall be used to display the contents of
* the run. Of the four possible types of content, we change the styling of two
* of them: ASCII and High ANSI. Finally we add the run font to the run
* properties.
*
* @param runProperties
*/
private static void changeFontToArial(RPr runProperties) {
RFonts runFont = new RFonts();
runFont.setAscii("Arial");
runFont.setHAnsi("Arial");
runProperties.setRFonts(runFont);
}
/**
* Change the font size of the given run properties to the given value.
*
* @param runProperties
* @param fontSize
* Twice the size needed, as it is specified as half-point value
*/
private static void changeFontSize(RPr runProperties, int fontSize) {
HpsMeasure size = new HpsMeasure();
size.setVal(BigInteger.valueOf(fontSize));
runProperties.setSz(size);
}
/**
* Removes the theme font information from the run properties. If this is not
* removed then the styles based on the normal style won't inherit the Arial
* font from the normal style.
*
* @param runProperties
*/
private static void removeThemeFontInformation(RPr runProperties) {
runProperties.getRFonts().setAsciiTheme(null);
runProperties.getRFonts().setHAnsiTheme(null);
}
/**
* Removes the Bold styling from the run properties.
*
* @param runProperties
*/
private static void removeBoldStyle(RPr runProperties) {
runProperties.getB().setVal(false);
}
/**
* As in the previous example, this method creates a footer part and adds it to
* the main document and then returns the corresponding relationship.
*
* @return
* @throws InvalidFormatException
*/
private static Relationship createFooterPart() throws InvalidFormatException {
FooterPart footerPart = new FooterPart();
footerPart.setPackage(wordMLPackage);
footerPart.setJaxbElement(createFooterWithPageNr());
return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);
}
/**
* As in the previous example, we create a footer and a paragraph object. But
* this time, instead of adding text to a run, we add a field. And just as with
* the table of content, we have to add a begin and end character around the
* actual field with the page number. Finally we add the paragraph to the
* content of the footer and then return it.
*
* @return
*/
public static Ftr createFooterWithPageNr() {
Ftr ftr = factory.createFtr();
P paragraph = factory.createP();
addFieldBegin(paragraph);
addPageNumberField(paragraph);
addFieldEnd(paragraph);
ftr.getContent().add(paragraph);
return ftr;
}
/**
* Creating the page number field is nearly the same as creating the field in
* the TOC example. The only difference is in the value. We use the PAGE
* command, which prints the number of the current page, together with the
* MERGEFORMAT switch, which indicates that the current formatting should be
* preserved when the field is updated.
*
* @param paragraph
*/
private static void addPageNumberField(P paragraph) {
R run = factory.createR();
Text txt = new Text();
txt.setSpace("preserve");
txt.setValue(" PAGE \\* MERGEFORMAT ");
run.getContent().add(factory.createRInstrText(txt));
paragraph.getContent().add(run);
}
/**
* Every fields needs to be delimited by complex field characters. This method
* adds the delimiter that precedes the actual field to the given paragraph.
*
* @param paragraph
*/
private static void addFieldBegin(P paragraph) {
R run = factory.createR();
FldChar fldchar = factory.createFldChar();
fldchar.setFldCharType(STFldCharType.BEGIN);
run.getContent().add(fldchar);
paragraph.getContent().add(run);
}
/**
* Every fields needs to be delimited by complex field characters. This method
* adds the delimiter that follows the actual field to the given paragraph.
*
* @param paragraph
*/
private static void addFieldEnd(P paragraph) {
FldChar fldcharend = factory.createFldChar();
fldcharend.setFldCharType(STFldCharType.END);
R run3 = factory.createR();
run3.getContent().add(fldcharend);
paragraph.getContent().add(run3);
}
/**
* This method fetches the document final section properties, and adds a newly
* created footer reference to them.
*
* @param relationship
*/
public static void createFooterReference(Relationship relationship) {
List<SectionWrapper> sections = wordMLPackage.getDocumentModel().getSections();
SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
// There is always a section wrapper, but it might not contain a sectPr
if (sectPr == null) {
sectPr = factory.createSectPr();
wordMLPackage.getMainDocumentPart().addObject(sectPr);
sections.get(sections.size() - 1).setSectPr(sectPr);
}
FooterReference footerReference = factory.createFooterReference();
footerReference.setId(relationship.getId());
footerReference.setType(HdrFtrRef.DEFAULT);
sectPr.getEGHdrFtrReferences().add(footerReference);
}
/**
* Adds a page break to the document.
*
* @param documentPart
*/
private static void addPageBreak(MainDocumentPart documentPart) {
Br breakObj = new Br();
breakObj.setType(STBrType.PAGE);
P paragraph = factory.createP();
paragraph.getContent().add(breakObj);
try {
documentPart.getContents().getBody().getContent().add(paragraph);
} catch (Docx4JException e) {
e.printStackTrace();
}
}
}
ExcelUtils工具类:
```java
/**
* 导出word
* <p>第一步生成替换后的word文件,只支持docx</p>
* <p>第二步下载生成的文件</p>
* <p>第三步删除生成的临时文件</p>
* 模版变量中变量格式:{{foo}}
*
* @param doc word模板地址
* @param temDir 生成临时文件存放地址
* @param fileName 文件名
* @param params 替换的参数
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
public static void exportWordList(XWPFDocument doc,String temDir, String fileName, HttpServletRequest request, HttpServletResponse response) {
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 {
String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
fileName = URLEncoder.encode(fileName, "UTF-8");
} else {
fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
}
String tmpPath = temDir + fileName;
FileOutputStream fos = new FileOutputStream(tmpPath);
doc.write(fos);
// 设置强制下载不打开
response.setContentType("application/force-download");
// 设置文件名
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
OutputStream out = response.getOutputStream();
doc.write(out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
ConStant层
CommonConStant类:
package com.jshop.common.constant;
/**
* 公共常量
*/
public class CommonConStant {
// 固定元素节点
public static final String COMMONATTR = "data-class";
}
Enums层
ElementEnum 类:
package com.jshop.common.enums;
/**
* html 元素枚举映射类
*/
public enum ElementEnum {
H1("h1","h1","一级标题"),
H2("h2","h2","二级标题"),
H3("h3","h3","三级标题"),
H7("h7","h7","小标题"),
P("p", "paragraph", "段落"),
STRONG("strong","","加粗"),
I("i","","斜体"),
U("u", "", "字体下划线"),
IMG("img", "imgurl", "base64图片"),
TABLE("table","table","表格"),
BR("br","br","换行")
;
private String code;
private String value;
private String desc;
public String getCode() {
return code;
}
public String getValue() {
return value;
}
public String getDesc() {
return desc;
}
ElementEnum(String code, String value, String desc) {
this.code = code;
this.value = value;
this.desc = desc;
}
public static String getValueByCode(String code) {
for (ElementEnum e : ElementEnum.values()) {
if (e.getCode().equalsIgnoreCase(code)) {
return e.getValue();
}
}
return null;
}
}
TitleFontEnum类:
package com.jshop.common.enums;
/**
* @desc 设置标题字体大小
* @author corey
* @version 1.0
* @date 2020/5/5 9:48 下午
*/
public enum TitleFontEnum {
H1("h1", 24),
H2("h2", 22),
H3("h3", 12),
H7("h7",12)
;
private String title;
private Integer font;
public String getTitle() {
return title;
}
public Integer getFont() {
return font;
}
TitleFontEnum(String title, Integer font) {
this.title = title;
this.font = font;
}
public static Integer getFontByTitle(String title){
for (TitleFontEnum e : TitleFontEnum.values()) {
if (title.equals(e.getTitle())) {
return e.getFont();
}
}
return null;
}
}
效果图: