前言
本文主要有以下几个知识点:
占位符填充
头像导出
表格填充
word转pdf
先来看下最后的效果图:
正文
模板配置
先来看下我们的模板:
首先我们需要先在word
模板里面设置占位符,这里有一个非常重要的点就是我们是根据${占位符}来替换的,其实word
文档本质上就是一个xml
文件,因此我们需要保证占位符不被切割,具体做法如下:
1.首先用解压工具打开模板
2.打开document.xml文件
3.可以看出文件并未格式化,我们先格式化代码
4.可以看到我们的占位符被切割了,我们需要干掉中间多余的。
5.点击开始后直接覆盖源文件就可以了,现在可以开始写代码了。
注意要保证我们的每个占位符不被切割,否则是无法进行替换的
模板填充
导入jar
包
org.apache.poi
poi
4.1.2
org.apache.poi
poi-scratchpad
4.1.2
org.apache.poi
poi-ooxml
4.1.2
首先看下我们两个表格的实体类(不一定要给表格建对象,根据个人需求添加即可)
// 家庭成员
@Builder(toBuilder = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EpRewandpun {
private String urewdate;
private String urewunit;
private String urewdesc;
}
// 奖惩情况
@Builder(toBuilder = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EpPmenber {
private String uconnection;
private String uname;
private String ubirthday;
private String uworkunit;
private String uploity;
private String ustatus;
}
接着看下我们的测试方法:
@SpringBootTest
class Tests {
@Test
void contextLoads() throws IOException {
String template = "C:UserslikunDesktop员工基本情况表.docx";
Map paramMap = new HashMap<>();
paramMap.put("${uname}", "乌龟");
paramMap.put("${usex}", "男");
paramMap.put("${ubirthdate}", "1998年10月22日");
paramMap.put("${unation}", "汉族");
paramMap.put("${unative}", "广东深圳");
paramMap.put("${uplace}", "广东汕头");
paramMap.put("${upolity}", "团员");
paramMap.put("${uworkdate}", "2020年3月16日");
paramMap.put("${uhealth}", "良好");
paramMap.put("${umajorpost}", "软件开发");
paramMap.put("${umajor}", "Java开发");
// 照片路径以及大小
Map phomap = new HashMap<>(8);
phomap.put("width", 100);
phomap.put("height", 130);
phomap.put("type", "png");
phomap.put("content", "E:\\Photo\\头像.jpg");
paramMap.put("${upho}", phomap);
//查询员工家庭信息
List menberlist = new ArrayList<>();
for (int i = 1; i < 3; i++) {
EpPmenber pmenber = new EpPmenber();
pmenber.setUname("小王");
pmenber.setUconnection("父亲");
pmenber.setUbirthday("1962年10月2日");
pmenber.setUploity("群众");
pmenber.setUworkunit("广东xxx公司");
pmenber.setUstatus("无");
menberlist.add(pmenber);
}
paramMap.put("menberlist", menberlist);
//查询员工奖励情况
List andpunlist = new ArrayList<>();
for (int i = 1; i < 3; i++) {
EpRewandpun rewandpun = new EpRewandpun();
rewandpun.setUrewdate("2020年5月1日");
rewandpun.setUrewunit("深圳xxx有限公司");
rewandpun.setUrewdesc("无");
andpunlist.add(rewandpun);
}
paramMap.put("andpunlist", andpunlist);
// 模板填充
XWPFDocument doc = WordUtil.generateWord(paramMap, template);
FileOutputStream fopts = new FileOutputStream("C:Users\\likun\\Desktop\\模板填充.docx");
doc.write(fopts);
fopts.close();
}
}
代码比较简单不再细说,先来看下我们的 generateWord
核心方法:
public class WordUtil {
/**
* 根据指定的参数值、模板,生成 word 文档
* 注意:其它模板需要根据情况进行调整
*
* @param param 变量集合
* @param template 模板路径
*/
public static XWPFDocument generateWord(Map param, String template) {
XWPFDocument doc = null;
try {
OPCPackage pack = POIXMLDocument.openPackage(template);
doc = new XWPFDocument(pack);
if (param != null && param.size() > 0) {
// 处理段落
List paragraphList = doc.getParagraphs();
processParagraphs(paragraphList, param, doc);
// 处理表格
Iterator it = doc.getTablesIterator();
//表格索引
int i = 0;
List menberlist = (List) param.get("menberlist");
List andpunlist = (List) param.get("andpunlist");
while (it.hasNext()) {
XWPFTable table = it.next();
int size = table.getRows().size() - 1;
XWPFTableRow row2 = table.getRow(size);
if (i == 1) {//家庭成员
if (menberlist.size() > 0) {
for (int j = 0; j < menberlist.size() - 1; j++) {
copy(table, row2, size + j);
}
}
} else if (i == 2) {//奖惩情况
if (andpunlist.size() > 0) {
for (int j = 0; j < andpunlist.size() - 1; j++) {
copy(table, row2, size + j);
}
}
}
_row++;
}
i++;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
}
看下我们的 copy
方法,用来拷贝行属性进行对表格进行复制
/**
* 拷贝赋值行
*
*/
public static void copy(XWPFTable table, XWPFTableRow sourceRow, int rowIndex) {
// 在表格指定位置新增一行
XWPFTableRow targetRow = table.insertNewTableRow(rowIndex);
// 复制行属性
targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
List cellList = sourceRow.getTableCells();
if (null == cellList) {
return;
}
// 复制列及其属性和内容
XWPFTableCell targetCell = null;
for (XWPFTableCell sourceCell : cellList) {
targetCell = targetRow.addNewTableCell();
// 列属性
targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
// 段落属性
if (sourceCell.getParagraphs() != null && sourceCell.getParagraphs().size() > 0) {
targetCell.getParagraphs().get(0).getCTP().setPPr(sourceCell.getParagraphs().get(0).getCTP().getPPr());
if (sourceCell.getParagraphs().get(0).getRuns() != null && sourceCell.getParagraphs().get(0).getRuns().size() > 0) {
XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
cellR.setText(sourceCell.getText());
cellR.setBold(sourceCell.getParagraphs().get(0).getRuns().get(0).isBold());
} else {
targetCell.setText(sourceCell.getText());
}
} else {
targetCell.setText(sourceCell.getText());
}
}
}
接下来是我们的 processParagraphs
方法,用来进行占位符的替换以及头像的插入。
/**
* 处理段落
*/
@SuppressWarnings({"unused", "rawtypes"})
public static void processParagraphs(List paragraphList, Map param, XWPFDocument doc) throws InvalidFormatException, IOException {
if (paragraphList != null && paragraphList.size() > 0) {
for (XWPFParagraph paragraph : paragraphList) {
List runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null) {
boolean isSetText = false;
for (Entry entry : param.entrySet()) {
String key = entry.getKey();
if (text.contains(key)) {
isSetText = true;
Object value;
if (entry.getValue() != null) {
value = entry.getValue();
} else {
value = "";
}
// 文本替换
if (value instanceof String) {
// 处理答案中的回车换行
if (((String) value).contains("n")) {
String[] lines = ((String) value).split("n");
if (lines.length > 0) {
text = text.replace(key, lines[0]);
for (int j = 1; j < lines.length; j++) {
run.addCarriageReturn();
run.setText(lines[j]);
}
}
} else {
text = text.replace(key, value.toString());
}
} else if (value instanceof Map) {
// 图片替换
text = text.replace(key, "");
Map pic = (Map) value;
int width = Integer.parseInt(pic.get("width").toString());
int height = Integer.parseInt(pic.get("height").toString());
int picType = getPictureType(pic.get("type").toString());
String byteArray = (String) pic.get("content");
CTInline inline = run.getCTR().addNewDrawing().addNewInline();
//插入图片
insertPicture(doc, byteArray, inline, width, height, paragraph);
}
}
}
if (isSetText) {
run.setText(text, 0);
}
}
}
}
}
}
接下来是 insertPicture
方法,用来插入图片,以及 getPictureType
方法获取图片类型。
/**
* 插入图片
*
*/
private static void insertPicture(XWPFDocument document, String filePath, CTInline inline, int width, int height, XWPFParagraph paragraph) throws Exception {
// 读取图片路径
InputStream inputStream = new FileInputStream(new File(filePath));;
document.addPictureData(inputStream, XWPFDocument.PICTURE_TYPE_PNG);
int id = document.getAllPictures().size() - 1;
final int emu = 9525;
width *= emu;
height *= emu;
String blipId = paragraph.getDocument().getRelationId(document.getAllPictures().get(id));
String picXml = getPicXml(blipId, width, height);
XmlToken xmlToken = null;
try {
xmlToken = XmlToken.Factory.parse(picXml);
} catch (XmlException xe) {
xe.printStackTrace();
}
inline.set(xmlToken);
inline.setDistT(0);
inline.setDistB(0);
inline.setDistL(0);
inline.setDistR(0);
CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx(width);
extent.setCy(height);
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
docPr.setId(id);
docPr.setName("IMG_" + id);
docPr.setDescr("IMG_" + id);
}
/**
* 根据图片类型,取得对应的图片类型代码
*
* @param picType
* @return int
*/
private static int getPictureType(String picType) {
int res = XWPFDocument.PICTURE_TYPE_PICT;
if (picType != null) {
if ("png".equalsIgnoreCase(picType)) {
res = XWPFDocument.PICTURE_TYPE_PNG;
} else if ("dib".equalsIgnoreCase(picType)) {
res = XWPFDocument.PICTURE_TYPE_DIB;
} else if ("emf".equalsIgnoreCase(picType)) {
res = XWPFDocument.PICTURE_TYPE_EMF;
} else if ("jpg".equalsIgnoreCase(picType) || "jpeg".equalsIgnoreCase(picType)) {
res = XWPFDocument.PICTURE_TYPE_JPEG;
} else if ("wmf".equalsIgnoreCase(picType)) {
res = XWPFDocument.PICTURE_TYPE_WMF;
}
}
return res;
}
最后填充就是这个样子:
word转pdf
最后在补充一个知识点,有时候会需要我们把 word
文档转成 pdf
,其实网上是有一个收费插件 `aspose-words,具体如何破解请上网搜查,首先导入包:
com.aspose.words
aspose-words-jdk16
16.4.0
接下来看下代码:
public class AsposeWordUtil {
private static final String WIN = "win";
/**
* word转pdf 需引入 aspose-words-16.4.0-jdk16.jar包 收费插件windows linux下均可用
*
* @param inPath
* 源文件路径
* @param outPath
* 输出文件路径
*/
public static void convertPdfToDocx(String inPath, String outPath) {
try {
FontSettings fontSettings = new FontSettings();
File file = new File(outPath);
FileOutputStream os = new FileOutputStream(file);
Document doc = new Document(inPath);
// 另外服务器需要上传中文字体到/usr/share/fonts目录(复制windowsC:WindowsFonts目录下的字体文件即可)
String cos = System.getProperty("os.name");
if (cos.toLowerCase().startsWith(WIN)) {
// windows环境
fontSettings.setFontsFolder("C:/Windows/Fonts", false);
} else {
// Linux环境
fontSettings.setFontsFolder("/usr/share/fonts", false);
}
doc.setFontSettings(fontSettings);
// 全面支持DOC, DOCX, OOXML, RTF HTML,OpenDocument, PDF,EPUB, XPS, SWF 相互转换
doc.save(os, SaveFormat.PDF);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:Linux服务器上需要上传中文字体,用法也很简单,只要传入word文档路径和要生成的pdf路径就可以了。
String wordPath = "C:Users\\likun\\Desktop\\人员基本情况.docx";
String pdfPath = "C:Users\\likun\\Desktop\\人员基本情况.pdf";
AsposeWordUtil.convertPdfToDocx(wordPath, pdfPath);
效果图:
总结
代码写到这里也就结束了,大伙可根据业务需求自行进行调整,如果有什么不对的地方请多多指教。