<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> <exclusions> <exclusion> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>ooxml-schemas</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>4.1.2</version> </dependency>
package com.springbooot.tree.dock.template;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.xwpf.usermodel.XWPFTableCell.XWPFVertAlign;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
/**
* poi操作word向word中写入复杂的表格(合并行、合并列)
*
* @author Christmas_G
*/
public class PaperTemplate {
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument();
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
//设置装订线
setBinding(document);
//设置页面大小和边距
setPageMargin(sectPr);
//设置保密★启用前
setConfidentiality(document);
//设置主标题
setMainHeading(document);
//设置副标题
setSubHeading(document);
//设置考试范围
setExamRange(document);
//设置个人信息
setPersonalInfo(document);
//设置誉分栏
setScoreColumn(document);
//设置注意事项
setAttention(document);
//设置空一行
setEmptyLine(document);
//设置大题及注释
setBigQuestionAndComment(document);
//设置大题评分区
//setBigQuestionScore(document);
//设置大题及注释评分区
// setBigQuestionAndCommentScore(document);
//设置页码
setPageNumber(document);
FileOutputStream out = new FileOutputStream("G:\\lyj\\bootmybatis\\src\\main\\resources\\templatesWord\\1.docx");
document.write(out);
out.close();
}
private static void setBinding(XWPFDocument document) {
//doc.getDocument().getBody().addNewSectPr();
CTSectPr sectPr = document.getDocument().getBody().getSectPr();
sectPr.addNewPgBorders();
CTPageBorders pgBorders = sectPr.getPgBorders();
CTBorder ctBorder = pgBorders.addNewBottom();
ctBorder.setVal(STBorder.DASH_DOT_STROKED);
ctBorder.setSz(BigInteger.valueOf(24));
ctBorder.setSpace(BigInteger.valueOf(24));
//Top Bottom Left Right
pgBorders.setLeft(ctBorder);
//以页面为基准
pgBorders.setOffsetFrom(STPageBorderOffset.PAGE);
//以文本为基准
//pgBorders.setOffsetFrom(STPageBorderOffset.TEXT);
}
private static void setPageNumber(XWPFDocument document) {
// 创建页眉和页脚策略
XWPFHeaderFooterPolicy headerFooterPolicy = document.createHeaderFooterPolicy();
// 创建页脚
XWPFFooter footer = headerFooterPolicy.createFooter(XWPFHeaderFooterPolicy.DEFAULT);
// 创建一个段落
XWPFParagraph paragraph = footer.createParagraph();
// 设置段落对齐方式
paragraph.setAlignment(ParagraphAlignment.CENTER);
// 创建一个run
XWPFRun run = paragraph.createRun();
// 插入页码域
run.setText("试卷第");
//run.addBreak(BreakType.TEXT_WRAPPING);
// 创建一个页码域
CTSimpleField field = paragraph.getCTP().addNewFldSimple();
field.setInstr("PAGE \\* MERGEFORMAT");
XWPFRun run1 = paragraph.createRun();
run1.setText("页,共");
// 创建一个页码域
CTSimpleField totalPagesField = paragraph.getCTP().addNewFldSimple();
totalPagesField.setInstr("NUMPAGES \\* MERGEFORMAT");
//run.addBreak(BreakType.TEXT_WRAPPING);
XWPFRun run2 = paragraph.createRun();
run2.setText("页");
}
private static void setBigQuestionAndCommentScore(XWPFDocument document) {
//设置大题及注释评分区
XWPFTable table = document.createTable(2, 3);
XWPFTableRow row = table.getRow(1);
row.setHeight(400);
mergeCellsVertically(table, 2, 0, 1);
//设置表单的宽度
XWPFTableCell cell = table.getRow(0).getCell(0);
cell.setText("评卷人");
XWPFTableCell cell2 = table.getRow(0).getCell(1);
cell2.setText("得分");
XWPFTableCell cell3 = table.getRow(0).getCell(2);
cell3.setText("一、填空题");
setCellBorder(cell3);
XWPFTableCell cell4 = table.getRow(1).getCell(2);
setCellBorder(cell4);
setTableCenter(table, 50, 175, 50, 175);
}
private static void setCellBorder(XWPFTableCell cell3) {
CTTcBorders ctTcBorders = cell3.getCTTc().addNewTcPr().addNewTcBorders();
ctTcBorders.addNewTop().setVal(STBorder.NONE);
ctTcBorders.addNewBottom().setVal(STBorder.NONE);
ctTcBorders.addNewRight().setVal(STBorder.NONE);
}
/**
* @Description: 跨列合并
*/
public static void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
if (cellIndex == fromCell) {
// The first merged cell is set with RESTART merge value
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
}
}
}
/**
* @Description: 跨行合并
* @see
*/
public static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
if (rowIndex == fromRow) {
// The first merged cell is set with RESTART merge value
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
}
}
}
//将表单内的字体设置居中
private static void setTableCenter(XWPFTable table, int top, int left, int bottom, int right) {
table.setCellMargins(top, left, bottom, right);
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
for (XWPFParagraph paragraph : cell.getParagraphs()) {
paragraph.setAlignment(ParagraphAlignment.CENTER);
for (XWPFRun run : paragraph.getRuns()) {
run.setFontSize(12);
run.setBold(true);
}
}
}
}
}
private static XWPFTable setBigQuestionScore(XWPFDocument document) {
XWPFTable table = document.createTable(2, 2);
document.createParagraph().createRun();
CTTblPr tableProps = table.getCTTbl().getTblPr();
CTTblWidth tableWidth = tableProps.addNewTblW();
tableWidth.setType(STTblWidth.DXA);
tableWidth.setW(BigInteger.valueOf(1800));
// 设置表格行高
for (int row = 0; row < 2; row++) {
XWPFTableRow tableRow = table.getRow(row);
CTRow ctRow = tableRow.getCtRow();
CTTrPr trPr = ctRow.addNewTrPr();
CTHeight trHeight = trPr.addNewTrHeight();
if (row == 0) {
trHeight.setVal(BigInteger.valueOf(400)); // 第一行高度设置
} else if (row == 1) {
trHeight.setVal(BigInteger.valueOf(560)); // 第二行高度设置
}
}
// 设置表格中的内容水平居中垂直居中
setTableCenter(table, 50, 200, 50, 200);
return table;
}
private static void setEmptyLine(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setSpacingBetween(2.5);
XWPFRun r = p.createRun();
r.setFontSize(25);
}
private static void setBigQuestionAndComment(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r = p.createRun();
r.setText("一、填空题");
r.setFontSize(10);
r.setFontFamily("宋体");
r.setBold(true);
}
private static void setScoreColumn(XWPFDocument document) {
XWPFTable table = document.createTable(2, 3); // 创建2行3列的表格
document.createParagraph().createRun();
// 设置表格样式
CTTblPr tableProps = table.getCTTbl().getTblPr();
CTJc ctJc = tableProps.addNewJc();
ctJc.setVal(STJc.CENTER);
CTTblWidth tableWidth = tableProps.addNewTblW();
tableWidth.setType(STTblWidth.DXA);
//tableWidth.setW(BigInteger.valueOf(2400));
// 设置表格行高
for (int row = 0; row < 2; row++) {
XWPFTableRow tableRow = table.getRow(row);
CTTrPr trPr = tableRow.getCtRow().addNewTrPr();
CTHeight trHeight = trPr.addNewTrHeight();
if (row == 0) {
trHeight.setVal(BigInteger.valueOf(400)); // 第一行高度设置
} else if (row == 1) {
trHeight.setVal(BigInteger.valueOf(560)); // 第二行高度设置
}
}
setTableCenter(table, 50, 200, 50, 200);
}
private static void setAttention(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r = p.createRun();
r.setText("注意事项:");
r.setFontSize(10);
r.setFontFamily("宋体");
XWPFParagraph p1 = document.createParagraph();
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r1 = p1.createRun();
r1.setText("1.答题前填写好自己的姓名、班级、考号等信息");
r1.setFontSize(10);
r1.setFontFamily("宋体");
XWPFParagraph p2 = document.createParagraph();
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r2 = p2.createRun();
r2.setText("2.请将答案正确填写在答题卡上");
r2.setFontSize(10);
r2.setFontFamily("宋体");
}
private static void setPersonalInfo(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setAlignment(ParagraphAlignment.CENTER);
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r = p.createRun();
r.setText("学校:___________姓名:___________班级:___________考号:___________");
r.setFontSize(10);
r.setFontFamily("Calibri");
}
private static void setExamRange(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setAlignment(ParagraphAlignment.CENTER);
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r = p.createRun();
r.setText("考试范围:xxx;考试1间:100分钟;命题人:xxx");
r.setFontFamily("宋体");
r.setFontSize(10);
}
private static void setSubHeading(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setSpacingBetween(1.5);
p.setAlignment(ParagraphAlignment.CENTER);
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r = p.createRun();
r.setText("试卷副标题");
r.setFontSize(18);
r.setFontFamily("黑体");
r.setBold(true);
}
private static void setMainHeading(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
p.setSpacingBefore(0);
p.setSpacingAfter(0);
p.setSpacingAfterLines(0);
p.setSpacingBeforeLines(0);
p.setIndentationFirstLine(0);
p.setSpacingBetween(1.5);
p.setAlignment(ParagraphAlignment.CENTER);
p.setVerticalAlignment(TextAlignment.CENTER);
//p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun r = p.createRun();
r.setText("2023-2024学年度高中数学期末考试卷");
r.setFontSize(15);
r.setFontFamily("宋体");
r.setBold(true);
}
private static void setPageMargin(CTSectPr sectPr) {
CTPageSz pgSz = sectPr.isSetPgSz() ? sectPr.getPgSz() : sectPr.addNewPgSz();
pgSz.setW(BigInteger.valueOf(595 * 20));
pgSz.setH(BigInteger.valueOf(842 * 20));
CTPageMar pageMar = sectPr.addNewPgMar();
pageMar.setLeft(BigInteger.valueOf(1995)); // 1 inch
pageMar.setRight(BigInteger.valueOf(1995)); // 1 inch
pageMar.setTop(BigInteger.valueOf(900)); // 1 inch
pageMar.setBottom(BigInteger.valueOf(900)); // 1 inch
}
private static void setConfidentiality(XWPFDocument document) {
XWPFParagraph p = document.createParagraph();
XWPFRun r = p.createRun();
r.setText("保密★启用前");
r.setFontSize(10);
r.setBold(true);
r.setFontFamily("黑体");
}
/**
* 向word中写表格
*
* @param element
* @return
* @throws Exception
* @author Christmas_G
*/
private static XWPFDocument setTable(Element element) throws Exception {
Document tableDoc = element.ownerDocument();
Elements trList = tableDoc.getElementsByTag("tr");
Elements tdList = trList.first().getElementsByTag("td");
XWPFDocument document = new XWPFDocument();
XWPFTable table = document.createTable(trList.size(), tdList.size());
boolean[][] colspanFlag = new boolean[trList.size()][tdList.size()];
boolean[][] rowspanFlag = new boolean[trList.size()][tdList.size()];
for (int row = 0; row < trList.size(); row++) {
Element tr = trList.get(row);
Elements tds = tr.getElementsByTag("td");
for (int col = 0; col < tds.size(); col++) {
Element td = tds.get(col);
String colspan = td.attr("colspan");
String rowspan = td.attr("rowspan");
String align = td.attr("align");
String widthStyle = td.attr("width");
String style = td.attr("style");
// 记录需要合并的列
if (!StringUtils.isEmpty(colspan)) {
int colCount = Integer.parseInt(colspan);
for (int i = 0; i < colCount - 1; i++) {
colspanFlag[row][col + i + 1] = true;
}
}
// 记录需要合并的行
if (!StringUtils.isEmpty(rowspan)) {
int rowCount = Integer.parseInt(rowspan);
for (int i = 0; i < rowCount - 1; i++) {
rowspanFlag[row + i + 1][col] = true;
}
}
// 处理合并
XWPFTableCell cell = table.getRow(row).getCell(col);
if (colspanFlag[row][col]) {
setColMerge(cell, STMerge.CONTINUE);
continue;
} else {
setColMerge(cell, STMerge.RESTART);
}
if (rowspanFlag[row][col]) {
setRowMerge(cell, STMerge.CONTINUE);
continue;
} else {
setRowMerge(cell, STMerge.RESTART);
}
// 设置列宽
if (!StringUtils.isAnyBlank(widthStyle) && !"0".equals(widthStyle)) {
int width = Integer.parseInt(widthStyle);
setWidth(cell, width);
}
XWPFParagraph paragraph = null;
int size = cell.getParagraphs().size();
if (size == 0) {
paragraph = cell.addParagraph();
} else {
paragraph = cell.getParagraphs().get(size - 1);
}
// 设置水平样式
if ("CENTER".equalsIgnoreCase(align)) {
paragraph.setAlignment(ParagraphAlignment.CENTER);
} else if ("LEFT".equalsIgnoreCase(align)) {
paragraph.setAlignment(ParagraphAlignment.LEFT);
}
// 设置垂直居中
cell.setVerticalAlignment(XWPFVertAlign.CENTER);
// 设置没有边框
if (!style.contains("border:")) {
setNotBorder(cell);
}
XWPFRun run = paragraph.createRun();
run.setText(td.text());
}
}
return document;
}
/**
* 设置行合并属性
*
* @param tableCell
* @param mergeVlaue
* @author Christmas_G
* @date 2019-05-31 14:08:02
*/
private static void setRowMerge(XWPFTableCell tableCell, STMerge.Enum mergeVlaue) {
CTTc ctTc = tableCell.getCTTc();
CTTcPr cpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
CTVMerge merge = cpr.isSetVMerge() ? cpr.getVMerge() : cpr.addNewVMerge();
merge.setVal(mergeVlaue);
}
/**
* 设置列合并属性
*
* @param tableCell
* @param mergeVlaue
* @author Christmas_G
* @date 2019-05-31 14:07:50
*/
private static void setColMerge(XWPFTableCell tableCell, STMerge.Enum mergeVlaue) {
CTTc ctTc = tableCell.getCTTc();
CTTcPr cpr = ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr();
CTHMerge merge = cpr.isSetHMerge() ? cpr.getHMerge() : cpr.addNewHMerge();
merge.setVal(mergeVlaue);
}
/**
* 补全表格
*
* @param tableHtml
* @return
* @author Christmas_G
* @date 2019-05-31 13:32:52
*/
private static Element supplementTable(String tableHtml) {
Document tableDoc = Jsoup.parse(tableHtml);
Elements trels = tableDoc.getElementsByTag("tr");
// 补全合并的列
supplementMergedColumns(trels);
// 补全合并的行
supplementMergedRows(trels);
return tableDoc.getElementsByTag("table").first();
}
/**
* 补全合并的列
*
* @param trels
* @author Christmas_G
* @date 2019-05-31 11:57:36
*/
private static void supplementMergedColumns(Elements trels) {
// 所有tr
Iterator<Element> trelIter = trels.iterator();
while (trelIter.hasNext()) {
Element trel = trelIter.next();
// 获取所有td
Elements tdels = trel.getElementsByTag("td");
Iterator<Element> tdelIter = tdels.iterator();
while (tdelIter.hasNext()) {
Element tdel = tdelIter.next();
// 删除样式
tdel.removeAttr("class");
// 取到合并的列数量
String colspanIndex = tdel.attr("colspan");
if (StringUtils.isEmpty(colspanIndex)) {
continue;
}
Integer colspanVal = Integer.parseInt(colspanIndex);
for (int i = 1; i < colspanVal; i++) {
trel.appendElement("td");
}
}
}
}
/**
* 补全合并的行(调用此方法前 需要调用 “补全合并的列”方法)
*
* @param trels
* @author Christmas_G
* @date 2019-05-31 11:57:47
*/
private static void supplementMergedRows(Elements trels) {
// 获取最大的列
int tdSize = 0;
Iterator<Element> iterator = trels.iterator();
while (iterator.hasNext()) {
Element element = iterator.next();
int size = element.getElementsByTag("td").size();
if (size > tdSize) {
tdSize = size;
}
}
for (int i = 0; i < trels.size(); i++) {
Element currTr = trels.get(i);
int currTrTds = currTr.getElementsByTag("td").size();
if (currTrTds == tdSize) {
continue;
}
int count = tdSize - currTrTds;
for (int j = 0; j < count; j++) {
currTr.appendElement("td");
}
}
}
/**
* 设置列宽
*
* @param cell
* @param width
* @author Christmas_G
* @date 2019-06-28 11:30:22
*/
private static void setWidth(XWPFTableCell cell, int width) {
CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
// 此处乘以20是我以最接近A4上创建表格的宽度手动设置的
// 目前没有找到将px转换为word里单位的方式
ctTblWidth.setW(BigInteger.valueOf(width).multiply(BigInteger.valueOf(20)));
ctTblWidth.setType(STTblWidth.DXA);
}
/**
* 设置表格为没有边框线
*
* @param cell
* @author Christmas_G
* @date 2019-06-28 11:33:48
*/
private static void setNotBorder(XWPFTableCell cell) {
CTTcBorders cTTcBorders = cell.getCTTc().getTcPr().addNewTcBorders();
CTBorder clBorder = cTTcBorders.addNewLeft();
clBorder.setVal(STBorder.Enum.forString("none"));
clBorder.setShadow(STOnOff.ON);
CTBorder crBorder = cTTcBorders.addNewRight();
crBorder.setVal(STBorder.Enum.forString("none"));
crBorder.setShadow(STOnOff.ON);
CTBorder cbBorder = cTTcBorders.addNewBottom();
cbBorder.setVal(STBorder.Enum.forString("none"));
cbBorder.setShadow(STOnOff.ON);
CTBorder ctBorder = cTTcBorders.addNewTop();
ctBorder.setVal(STBorder.Enum.forString("none"));
ctBorder.setShadow(STOnOff.ON);
}
}
效果
装订线暂时没有实现