功能說明: 將試卷導出word,並可以列印,裝訂,效果圖: ![](https://img2023.cnblogs.com/blog/2699913/202307/2699913-20230718094848710-642780924.png) 下麵是實現代碼: ``` package com.xx ...
功能說明:
將試卷導出word,並可以列印,裝訂,效果圖:
下麵是實現代碼:
package com.xxxxx.business.course.utils;
import com.alibaba.fastjson.JSONObject;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import com.xxxxx.business.course.domain.*;
import com.xxxxx.business.utils.UtilTools;
import com.xxxxx.business.utils.domain.JudgeRule;
import com.xxxxx.business.utils.impl.BaseImpl;
import com.xxxxx.common.core.domain.R;
import com.xxxxx.common.core.utils.DateUtils;
import com.xxxxx.common.core.utils.StringUtils;
import com.xxxxx.common.core.utils.file.FileTypeUtils;
import com.xxxxx.common.core.utils.file.ImageUtils;
import com.xxxxx.common.core.utils.reflect.ReflectUtils;
import com.xxxxx.common.core.utils.sign.Base64;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
/**
* 試卷歸檔幫助類
*/
public class VerifyPaperFileUtils extends BaseImpl {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
public static VerifyPaperFileUtils getInstance() {
return new VerifyPaperFileUtils();
}
private VerifyPaperFileUtils() {
}
private Integer pageNum = 0;
private XWPFDocument docx;
private CTSectPr ctSectPr;
private QuestionImportRule importRule = QuestionImportRule.getDefaultRule("");
private VerifyPaper verifyPaper;
private PaperTemplate paperTemplate;
private boolean includeAnswer = false;
private String imgRegex = "img";
private String[] quSortNoArr = new String[]{"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十"};
private String rgb_black = "000000";
/**
* 外部調用方法,導出word
*
* @param response
* @param verifyPaper
* @throws IOException
*/
public R<?> exportWord(HttpServletResponse response, VerifyPaper verifyPaper, PaperTemplate paperTemplate) throws Exception {
this.verifyPaper = verifyPaper;
if (paperTemplate == null) paperTemplate = VerifyPaperFileUtils.getDefaultPaperTemplate();
this.paperTemplate = paperTemplate;
R<?> r = this.init();
if (!r.isSuccess()) throw new Exception(r.getMsg());
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setCharacterEncoding("utf-8");
return this.exportWord(response);
}
/**
* 驗證相關數據
*
* @return
*/
private R<?> init() {
if (this.verifyPaper == null) {
return R.fail("未選擇試卷");
}
if (this.verifyPaper.getVerifyPaperQtList() == null || verifyPaper.getVerifyPaperQtList().size() == 0) {
return R.fail("未設置課程題型");
}
if (this.verifyPaper.getVerifyPaperQtList().stream().filter(s -> s.getVerifyPaperQuList() == null || s.getVerifyPaperQuList().size() == 0).count() > 0)
return R.fail("有題型未設置試題");
return validTemplate();
}
private R<?> validTemplate() {
if (this.paperTemplate == null) {
return R.fail("未設置模板");
}
return R.ok();
}
/**
* 導出word,私有方法
*
* @param response
* @throws IOException
*/
private R<?> exportWord(HttpServletResponse response) throws IOException, InvalidFormatException {
try {
R<?> r = writeWord();
if (!r.isSuccess()) return r;
docx.write(response.getOutputStream());
return R.ok();
} catch (Exception e) {
logger.error("導出Word異常,{}", e.getMessage());
throw e;
} finally {
IOUtils.closeQuietly(docx);
}
}
/**
* 試題數據寫入文檔
*/
private R<?> writeWord() throws IOException, InvalidFormatException {
R<XWPFDocument> r = createDocxTemplate();
if (!r.isSuccess()) return R.fail(r.getMsg());
docx = r.getData();
writePaperInfo();
writeWordQts();
return R.ok();
}
/**
* 製作模板
* 模板各種設置
*
* @return
*/
private R<XWPFDocument> createDocxTemplate() {
XWPFDocument document = new NiceXWPFDocument();
R<?> r = writeWordPageSetting(document);
if (!r.isSuccess()) return R.fail(r.getMsg());
//整體設置
writeWordPageSetting(document);
//頁眉
writeWordHeader(document);
//頁腳
writeWordFooter(document);
return R.ok(document);
}
/**
* 頁面設置
*
* @return
*/
private R<?> writeWordPageSetting(XWPFDocument docx) {
if (StringUtils.isEmpty(paperTemplate.getPtPageSize())) return R.fail("未設置模板尺寸");
ctSectPr = docx.getDocument().getBody().getSectPr();
if (ctSectPr == null) ctSectPr = docx.getDocument().getBody().addNewSectPr();
CTPageSz ctPageSz = ctSectPr.isSetPgSz() ? ctSectPr.getPgSz() : ctSectPr.addNewPgSz();
switch (paperTemplate.getPtPageSize().toUpperCase()) {
case "A3":
ctPageSz.setW(getTwipFromCM(29.7)); //設置寬度(這裡是一個ulong類型,單位:緹 , 1釐米=567緹)
ctPageSz.setH(getTwipFromCM(42)); //設置高度(這裡是一個ulong類型)
break;
case "A4":
ctPageSz.setW(getTwipFromCM(21));
ctPageSz.setH(getTwipFromCM(29.7));
break;
case "A5":
ctPageSz.setW(getTwipFromCM(14.8));
ctPageSz.setH(getTwipFromCM(21));
break;
case "B4":
ctPageSz.setW(getTwipFromCM(25.7));
ctPageSz.setH(getTwipFromCM(36));
break;
case "B5":
ctPageSz.setW(getTwipFromCM(18.2));
ctPageSz.setH(getTwipFromCM(25.7));
break;
case "16開":
ctPageSz.setW(getTwipFromCM(18.4));
ctPageSz.setH(getTwipFromCM(26));
break;
case "32開":
ctPageSz.setW(getTwipFromCM(13));
ctPageSz.setH(getTwipFromCM(18.4));
break;
case "大32開":
ctPageSz.setW(getTwipFromCM(14));
ctPageSz.setH(getTwipFromCM(20.3));
break;
default:
return R.fail(StringUtils.format("不支持的尺寸:{}", paperTemplate.getPtPageSize()));
}
if (paperTemplate.getPtDirection().intValue() == 1) // 橫向
{
BigInteger tempW = ctPageSz.getW();
ctPageSz.setW(ctPageSz.getH());
ctPageSz.setH(tempW);
ctPageSz.setOrient(STPageOrientation.LANDSCAPE);
}
//設置邊距
CTPageMar pageMar = ctSectPr.getPgMar();
if (pageMar == null) pageMar = ctSectPr.addNewPgMar();
pageMar.setBottom(getTwipFromCM(paperTemplate.getPtMarginBottom().doubleValue()));
pageMar.setTop(getTwipFromCM(paperTemplate.getPtMarginTop().doubleValue()));
pageMar.setRight(getTwipFromCM(paperTemplate.getPtMarginRight().doubleValue()));
pageMar.setLeft(getTwipFromCM(paperTemplate.getPtMarginLeft().doubleValue()));
pageMar.setHeader(getTwipFromCM(paperTemplate.getPtHeaderMargin().doubleValue()));//頁眉到頂端的距離
pageMar.setFooter(getTwipFromCM(paperTemplate.getPtFooterMargin().doubleValue()));//頁腳到低端的距離
if (paperTemplate.getPtMarginBottom().doubleValue() > 2) {
pageMar.setFooter(getTwipFromCM(1));
} else {
double leftH = paperTemplate.getPtMarginBottom().doubleValue() - 1;
if (leftH > 0) {
pageMar.setFooter(getTwipFromCM(leftH));
}
}
//處理分欄
if (paperTemplate.getPtColumNum().intValue() > 1) {
if (!ctSectPr.isSetCols()) ctSectPr.addNewCols();
if (!UtilTools.compareBigIntegerValue(BigInteger.valueOf(paperTemplate.getPtColumNum()), ctSectPr.getCols().getNum())) {
CTColumns ctColumns = ctSectPr.getCols();
ctColumns.setNum(BigInteger.valueOf(paperTemplate.getPtColumNum().longValue()));
ctSectPr.setCols(ctColumns);
}
}
return R.ok();
}
/**
* 設置頭部
*/
private void writeWordHeader(XWPFDocument docx) {
if (StringUtils.isEmpty(paperTemplate.getPtSealLineType())) {
paperTemplate.setPtSealLineType("once");
}
XWPFHeader header = null;
//STHdrFtr.Enum headerType = StringUtils.equalsIgnoreCase(paperTemplate.getPtSealLineType(), "once") ? STHdrFtr.DEFAULT : STHdrFtr.Enum.forString(paperTemplate.getPtSealLineType());
if (!StringUtils.equalsIgnoreCase(paperTemplate.getPtSealLineType(), "once")) {
XWPFHeaderFooterPolicy headerFooterPolicy = docx.getHeaderFooterPolicy();
if (headerFooterPolicy == null) headerFooterPolicy = docx.createHeaderFooterPolicy();
String sealType = "";
sealType = paperTemplate.getPtSealLineType().toLowerCase();
switch (sealType) {
case "default":
//每頁
header = headerFooterPolicy.createHeader(STHdrFtr.DEFAULT);
break;
case "odd":
//奇數頁
header = headerFooterPolicy.createHeader(STHdrFtr.DEFAULT);
docx.setEvenAndOddHeadings(true);
break;
case "even":
//偶數頁
header = headerFooterPolicy.createHeader(STHdrFtr.EVEN);
docx.setEvenAndOddHeadings(true);
break;
}
}
if (paperTemplate.getPtPageHeadShow() && StringUtils.isNotEmpty(paperTemplate.getPtPageHeadText())) {
if (header == null) {
XWPFHeaderFooterPolicy headerFooterPolicy = docx.getHeaderFooterPolicy();
if (headerFooterPolicy == null) headerFooterPolicy = docx.createHeaderFooterPolicy();
header = headerFooterPolicy.createHeader(STHdrFtr.DEFAULT);
}
XWPFParagraph p = header.createParagraph();
if (StringUtils.isNotEmpty(paperTemplate.getPtPageHeadAlign()))
p.setAlignment(Enum.valueOf(ParagraphAlignment.class, paperTemplate.getPtPageHeadAlign()));
XWPFRun run = p.createRun();
run.setText(paperTemplate.getPtPageHeadText());
run.setFontSize(paperTemplate.getPtPageHeadSize());
}
//在頁眉裡面添加裝訂線
//製作左邊裝訂線
writeWordBroadside(docx, header);
}
/**
* 設置側邊欄
* 換新頁面設置一個
*/
private void writeWordBroadside(XWPFDocument docx, XWPFHeader header) {
if (paperTemplate.getPtSealLineShow() == null || !paperTemplate.getPtSealLineShow()) return;
ctSectPr = docx.getDocument().getBody().getSectPr();
if (ctSectPr == null) ctSectPr = docx.getDocument().getBody().addNewSectPr();
//創建一個
CTPageMar pageMar = ctSectPr.isSetPgMar() ? ctSectPr.getPgMar() : ctSectPr.addNewPgMar();
//裝訂線寬度
BigInteger slideBarWidth = pageMar.getLeft();
if (pageMar.getGutter() != null) slideBarWidth.add(pageMar.getGutter());
if (paperTemplate.getPtSealLineWidth().doubleValue() > 2.5) {
slideBarWidth = slideBarWidth.subtract(getTwipFromCM(0.5));
}
BigInteger cellWidth = slideBarWidth.divide(BigInteger.valueOf(2));
//BigInteger top = BigInteger.valueOf(1);
BigInteger slideBarHeight = ctSectPr.getPgSz().getH().subtract(BigInteger.valueOf(2));
// 創建一個側邊欄
XWPFTable sidebarTable =
StringUtils.equalsIgnoreCase(paperTemplate.getPtSealLineType(), "once")
? docx.createTable(1, 2)
: header.createTable(1, 2);
CTTblPr ctTblPr = sidebarTable.getCTTbl().getTblPr();
if (ctTblPr == null) ctTblPr = sidebarTable.getCTTbl().addNewTblPr();
//設置寬度
ctTblPr.getTblW().setType(STTblWidth.DXA);
ctTblPr.getTblW().setW(slideBarWidth);
// BigInteger x = BigInteger.ZERO.subtract(slideBarWidth);
//居左
CTJc jcTable = ctTblPr.isSetJc() ? ctTblPr.getJc() : ctTblPr.addNewJc();
jcTable.setVal(STJc.LEFT);
sidebarTable.setTableAlignment(TableRowAlign.LEFT);
//定位
CTTblPPr ctTblPPr = ctTblPr.isSetTblpPr() ? ctTblPr.getTblpPr() : ctTblPr.addNewTblpPr();
ctTblPPr.setVertAnchor(STVAnchor.PAGE);
ctTblPPr.setHorzAnchor(STHAnchor.PAGE);
ctTblPPr.setTblpXSpec(STXAlign.LEFT);
ctTblPPr.setTblpYSpec(STYAlign.TOP);
ctTblPPr.setTblpX(BigInteger.ZERO);
ctTblPPr.setTblpY(BigInteger.ZERO);
//去除邊框
setTableBorder(sidebarTable, false);
for (XWPFTableRow row : sidebarTable.getRows()) {
row.setHeight(slideBarHeight.intValue());
for (XWPFTableCell cell : row.getTableCells()) {
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
CTTcPr ctTcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();
if (!ctTcPr.isSetTcW()) ctTcPr.addNewTcW();
ctTcPr.getTcW().setW(cellWidth);
//文字方向
CTTextDirection cellDirection = ctTcPr.isSetTextDirection() ? ctTcPr.getTextDirection() : ctTcPr.addNewTextDirection();
cellDirection.setVal(STTextDirection.BT_LR);
//垂直居中
CTVerticalJc ctVerticalJc = ctTcPr.isSetVAlign() ? ctTcPr.getVAlign() : ctTcPr.addNewVAlign();
ctVerticalJc.setVal(STVerticalJc.Enum.forString("center"));
//左對齊
CTOnOff noWap = ctTcPr.isSetNoWrap() ? ctTcPr.getNoWrap() : ctTcPr.addNewNoWrap();
noWap.setVal(STOnOff.Enum.forString("on"));
}
}
//學生信息
XWPFTableCell cellInfo = sidebarTable.getRow(0).getCell(0);
//String blankStr = " ";
String blankStr = "_____________";
XWPFParagraph paragraph = cellInfo.getParagraphs().get(0);
paragraph.setVerticalAlignment(TextAlignment.CENTER);
paragraph.setAlignment(ParagraphAlignment.CENTER);
//paragraph.setFontAlignment(2);
if (paperTemplate.getPtSealLineStudentCode()) {
addTextToSidebar(paragraph, " 學號:", false);
addTextToSidebar(paragraph, blankStr, false);
}
if (paperTemplate.getPtSealLineStudentName()) {
addTextToSidebar(paragraph, " 姓名:", false);
addTextToSidebar(paragraph, blankStr, false);
}
if (paperTemplate.getPtSealLineStudentCollege()) {
addTextToSidebar(paragraph, " 學院:", false);
addTextToSidebar(paragraph, blankStr, false);
}
if (paperTemplate.getPtSealLineStudentGrade()) {
addTextToSidebar(paragraph, " 年級:", false);
addTextToSidebar(paragraph, blankStr, false);
}
if (paperTemplate.getPtSealLineStudentDepartment()) {
addTextToSidebar(paragraph, " 系:", false);
addTextToSidebar(paragraph, blankStr, false);
}
if (paperTemplate.getPtSealLineStudentSpecialty()) {
addTextToSidebar(paragraph, " 專業:", false);
addTextToSidebar(paragraph, blankStr, false);
}
if (paperTemplate.getPtSealLineStudentClass()) {
addTextToSidebar(paragraph, " 班級:", false);
addTextToSidebar(paragraph, blankStr, false);
}
//裝訂線
XWPFTableCell cellLine = sidebarTable.getRow(0).getCell(1);
paragraph = cellLine.getParagraphs().get(0);
paragraph.setVerticalAlignment(TextAlignment.CENTER);
paragraph.setAlignment(ParagraphAlignment.CENTER);
// paragraph.setAlignment(ParagraphAlignment.CENTER);
// paragraph.setFontAlignment(2);
//String lineStr = "-----------------------------------------------";
String lineStr = "-------------------------------------------";
addTextToSidebar(paragraph, lineStr, false);
addTextToSidebar(paragraph, " 密 - 封 - 線 ", false);
addTextToSidebar(paragraph, lineStr, false);
}
private void addTextToSidebar(XWPFParagraph paragraph, String text, boolean isUnderline) {
XWPFRun studentRun = paragraph.createRun();
studentRun.setText(text);
studentRun.setFontSize(paperTemplate.getPtSealLineSize());
studentRun.setFontFamily(paperTemplate.getPtSealLineFamily());
if (isUnderline) {
studentRun.setUnderline(UnderlinePatterns.SINGLE);
}
}
private void addTextToSidebar(XWPFParagraph paragraph, String text, boolean isUnderline, boolean isBold) {
XWPFRun studentRun = paragraph.createRun();
studentRun.setText(text);
studentRun.setFontSize(paperTemplate.getPtSealLineSize());
studentRun.setFontFamily(paperTemplate.getPtSealLineFamily());
if (isBold) studentRun.setBold(true);
if (isUnderline) {
studentRun.setUnderline(UnderlinePatterns.SINGLE);
}
}
/**
* 設置頁腳
*/
private void writeWordFooter(XWPFDocument docx) {
boolean hasFoot = paperTemplate.getPtPageFootShow() && StringUtils.isNotEmpty(paperTemplate.getPtPageFootText());
boolean hasPageNum = paperTemplate.getPtPageNumShow();
if (hasFoot || hasPageNum) {
XWPFHeaderFooterPolicy headerFooterPolicy = docx.getHeaderFooterPolicy();
if (headerFooterPolicy == null) headerFooterPolicy = docx.createHeaderFooterPolicy();
XWPFFooter footer = headerFooterPolicy.createFooter(XWPFHeaderFooterPolicy.DEFAULT);
XWPFParagraph p = footer.getParagraphs() != null && footer.getParagraphs().size() > 0 ? footer.getParagraphs().get(0) : footer.createParagraph();
if (hasFoot) {
if (StringUtils.isNotEmpty(paperTemplate.getPtPageFootAlign()))
p.setAlignment(Enum.valueOf(ParagraphAlignment.class, paperTemplate.getPtPageFootAlign()));
XWPFRun run = p.createRun();
run.setText(paperTemplate.getPtPageFootText());
run.setFontSize(paperTemplate.getPtPageFootSize());
if (hasPageNum) {
p.setAlignment(ParagraphAlignment.LEFT);
}
} else {
p.setAlignment(ParagraphAlignment.CENTER);
}
if (hasPageNum) {
//設置分頁
if (hasFoot) {
CTTabStop tabStop = p.getCTP().getPPr().addNewTabs().addNewTab();
tabStop.setVal(STTabJc.RIGHT);
//設置位置?
int twipsPerInch = 1440;
tabStop.setPos(BigInteger.valueOf(6 * twipsPerInch));
}
XWPFRun run = p.createRun();
run.setFontSize(paperTemplate.getPtPageNumSize());
run.setText("第");
if (hasFoot) run.addTab();
//當前頁 begin
//p.getCTP().addNewFldSimple().setInstr("PAGE \\* MERGEFORMAT");
run = p.createRun();
run.setShadow(false);
run.setFontSize(paperTemplate.getPtPageNumSize());
CTFldChar fldChar = run.getCTR().addNewFldChar();
fldChar.setFldCharType(STFldCharType.Enum.forString("begin"));
run.setFontSize(paperTemplate.getPtPageNumSize());
CTText ctText = run.getCTR().addNewInstrText();
ctText.setStringValue("PAGE \\* MERGEFORMAT");
ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve"));
fldChar = run.getCTR().addNewFldChar();
fldChar.setFldCharType(STFldCharType.Enum.forString("end"));
//當前頁 end
run = p.createRun();
run.setFontSize(paperTemplate.getPtPageNumSize());
run.setText("頁 (共");
//總頁數 begin
run = p.createRun();
run.setFontSize(paperTemplate.getPtPageNumSize());
fldChar = run.getCTR().addNewFldChar();
fldChar.setFldCharType(STFldCharType.Enum.forString("begin"));
run = p.createRun();
run.setFontSize(paperTemplate.getPtPageNumSize());
ctText = run.getCTR().addNewInstrText();
ctText.setStringValue("NUMPAGES \\* MERGEFORMAT");
ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve"));
//run = p.createRun();
//run.setFontSize(paperTemplate.getPtPageNumSize());
fldChar = run.getCTR().addNewFldChar();
fldChar.setFldCharType(STFldCharType.Enum.forString("end"));
//總頁數 end
run = p.createRun();
run.setFontSize(paperTemplate.getPtPageNumSize());
run.setText("頁)");
}
}
}
/**
* 寫入試卷信息
*/
private void writePaperInfo() throws IOException, InvalidFormatException {
//頂部標題
if (paperTemplate.getPtTitle() && StringUtils.isNotEmpty(verifyPaper.getVerifyPaperTopName())) {
XWPFParagraph topTitle = createParagraph(paperTemplate.getPtTitleFontSize());
topTitle.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtTitleFontFamily());
run.setFontSize(paperTemplate.getPtTitleFontSize());
run.setText(verifyPaper.getVerifyPaperTopName());
run.setBold(paperTemplate.getPtTitleBold());
}
//試卷標題
if (StringUtils.isNotEmpty(verifyPaper.getVerifyPaperName())) {
XWPFParagraph topTitle = createParagraph(paperTemplate.getPtTitleSubFontSize());
topTitle.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtTitleSubFontFamily());
run.setFontSize(paperTemplate.getPtTitleSubFontSize());
run.setText(verifyPaper.getVerifyPaperName());
run.setBold(paperTemplate.getPtTitleSubBold());
}
// 答案及標準字樣
if (includeAnswer) {
XWPFParagraph topTitle = createParagraph(paperTemplate.getPtTitleSubFontSize());
topTitle.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtTitleSubFontFamily());
run.setFontSize(paperTemplate.getPtTitleSubFontSize());
run.setText("參考答案及評分標準");
}
//考試信息
if (paperTemplate.getPtPaperInfoShow()) {
XWPFParagraph topTitle = createParagraph(paperTemplate.getPtPaperInfoSize());
topTitle.setAlignment(ParagraphAlignment.CENTER);
boolean haPre = false;
if (paperTemplate.getPtShowAnswerMode()) {
XWPFRun run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText("考試形式:");
run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText(StringUtils.format(" {} ", verifyPaper.getVpAnswerMode() == 0 ? "閉捲" : "開卷"));
run.setUnderline(UnderlinePatterns.SINGLE);
haPre = true;
}
if (paperTemplate.getPtShowAnswerTime()) {
XWPFRun run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText((haPre ? " " : "") + "考試時間:");
run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText(StringUtils.format(" {} ", verifyPaper.getVpTotalTimeMinute()));
run.setUnderline(UnderlinePatterns.SINGLE);
run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText("分鐘");
haPre = true;
}
if (paperTemplate.getPtShowAnswerTime()) {
XWPFRun run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText((haPre ? " " : "") + "滿分:");
run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText(StringUtils.format(" {} ", verifyPaper.getVpTotalMark().stripTrailingZeros().toPlainString()));
run.setUnderline(UnderlinePatterns.SINGLE);
run = topTitle.createRun();
run.setFontFamily(paperTemplate.getPtPaperInfoFamily());
run.setFontSize(paperTemplate.getPtPaperInfoSize());
run.setText("分。");
haPre = true;
}
addNewBlankLine(docx);
}
//得分欄
if (paperTemplate.getPtScoreColumn()) {
int colNum = 2 + verifyPaper.getVerifyPaperQtList().size();
double colWidth = 2;
XWPFTable scoreTable = docx.createTable(3, colNum);
scoreTable.setTableAlignment(TableRowAlign.CENTER);
scoreTable.setWidth(getTwipFromCM(colWidth * colNum).intValue());
setTableBorder(scoreTable, true);
for (XWPFTableRow row : scoreTable.getRows()) {
row.setHeight(getTwipFromCM(1).intValue());
for (XWPFTableCell cell : row.getTableCells()) {
cell.setWidth(getTwipFromCM(colWidth).toString());
}
}
setCellText(scoreTable.getRows().get(0).getTableCells().get(0), "題 號",
paperTemplate.getPtScoreColumnFamily(), paperTemplate.getPtScoreColumnSize(),
paperTemplate.getPtScoreColumnBold(), false,
ParagraphAlignment.CENTER, XWPFTableCell.XWPFVertAlign.CENTER);
for (int i = 0; i < verifyPaper.getVerifyPaperQtList().size(); i++) {
setCellText(scoreTable.getRow(0).getCell(i + 1), this.quSortNoArr[i],
paperTemplate.getPtScoreColumnFamily(), paperTemplate.getPtScoreColumnSize(),
paperTemplate.getPtScoreColumnBold(), false,
ParagraphAlignment.CENTER, XWPFTableCell.XWPFVertAlign.CENTER);
}
setCellText(scoreTable.getRow(0).getCell(colNum - 1), "總 分",
paperTemplate.getPtScoreColumnFamily(), paperTemplate.getPtScoreColumnSize(),
paperTemplate.getPtScoreColumnBold(), false,
ParagraphAlignment.CENTER, XWPFTableCell.XWPFVertAlign.CENTER);
setCellText(scoreTable.getRow(1).getCell(0), "得 分",
paperTemplate.getPtScoreColumnFamily(), paperTemplate.getPtScoreColumnSize(),
paperTemplate.getPtScoreColumnBold(), false,
ParagraphAlignment.CENTER, XWPFTableCell.XWPFVertAlign.CENTER);
setCellText(scoreTable.getRow(2).getCell(0), "評閱人",
paperTemplate.getPtScoreColumnFamily(), paperTemplate.getPtScoreColumnSize(),
paperTemplate.getPtScoreColumnBold(), false,
ParagraphAlignment.CENTER, XWPFTableCell.XWPFVertAlign.CENTER);
addNewBlankLine(docx);
}
if (StringUtils.isNotEmpty(paperTemplate.getPtReadme())) {
XWPFTable readmeTable = docx.createTable(1, 2);
readmeTable.setTableAlignment(TableRowAlign.CENTER);
readmeTable.setWidth(getTwipFromCM(15).intValue());
setTableBorder(readmeTable, false);
XWPFTableCell cell1 = readmeTable.getRow(0).getCell(0);
cell1.setWidth(getTwipFromCM(2).toString());
XWPFParagraph paragraph = cell1.getParagraphs().get(0);
paragraph.setVerticalAlignment(TextAlignment.TOP);
paragraph.setAlignment(ParagraphAlignment.RIGHT);
XWPFRun run = paragraph.createRun();
run.setFontSize(paperTemplate.getPtReadmeFontSize());
run.setFontFamily(paperTemplate.getPtReadmeFontFamily());
if (paperTemplate.getPtReadmeBold()) run.setBold(true);
run.setText("註意:");
//右側註意事項
XWPFTableCell cell2 = readmeTable.getRow(0).getCell(1);
paragraph = cell2.getParagraphs().get(0);
paragraph.setSpacingLineRule(LineSpacingRule.AT_LEAST);
paragraph.setVerticalAlignment(TextAlignment.TOP);
paragraph.setAlignment(ParagraphAlignment.LEFT);
run = paragraph.createRun();
run.setFontSize(paperTemplate.getPtReadmeFontSize());
run.setFontFamily(paperTemplate.getPtReadmeFontFamily());
if (paperTemplate.getPtReadmeBold()) run.setBold(true);
run.setText(paperTemplate.getPtReadme());
addNewBlankLine(docx);
}
}
private XWPFParagraph createParagraph(Integer fontSize) {
XWPFParagraph paragraph = docx.createParagraph();
paragraph.setAlignment(ParagraphAlignment.LEFT);
paragraph.setVerticalAlignment(TextAlignment.CENTER);
if (fontSize != null) {
setParagraphTopPadding(paragraph, fontSize);
}
return paragraph;
}
/**
* 設置段落第一行頭部間隔
* 間隔為字體的一半
*
* @param paragraph
* @param fontSize
*/
private void setParagraphTopPadding(XWPFParagraph paragraph, Integer fontSize) {
BigInteger space = getTwipFromPound(fontSize.doubleValue()).divide(BigInteger.valueOf(2));
paragraph.setSpacingBefore(space.intValue());
}
/**
* 表格設置文字
*
* @param cell
* @param text
* @param fontFamily
* @param fontSize
* @param isBold
* @param isUnderLine
* @param align
* @param vAlign
*/
private void setCellText(XWPFTableCell cell, String text, String fontFamily, Integer fontSize, Boolean isBold, Boolean isUnderLine,
ParagraphAlignment align, XWPFTableCell.XWPFVertAlign vAlign
) {
XWPFParagraph paragraph = cell.getParagraphs().get(0);
if (align != null)
paragraph.setAlignment(align);
if (vAlign != null)
cell.setVerticalAlignment(vAlign);
XWPFRun run = paragraph.createRun();
run.setText(text);
if (StringUtils.isNotEmpty(fontFamily)) run.setFontFamily(fontFamily);
if (fontSize != null) run.setFontSize(fontSize);
if (isBold != null) run.setBold(isBold);
if (isUnderLine != null && isUnderLine) run.setUnderline(UnderlinePatterns.SINGLE);
}
/**
* 添加空行
*
* @param document
*/
private void addNewBlankLine(XWPFDocument document) {
XWPFParagraph paragraph = document.createParagraph();
paragraph.createRun().setText("");
}
/**
* 設置題型
*/
private void writeWordQts() throws IOException, InvalidFormatException {
for (int i = 0; i < verifyPaper.getVerifyPaperQtList().size(); i++) {
VerifyPaperQt qt = verifyPaper.getVerifyPaperQtList().get(i);
//處理標題
writeWordQtTitle(qt, i);
//處理試題
writeQuestionForQt(qt);
}
}
/**
* 寫入題型試題
*
* @return
*/
private void writeWordQtTitle(VerifyPaperQt qt, Integer qtIndex) {
String ext = qt.getVpQtDescription();
if (StringUtils.isEmpty(ext))
ext = StringUtils.format("(本大題{}小題,每小題{}分,共{}分)", qt.getVpQtQuNum(), qt.getVpQtQuMark().stripTrailingZeros().toPlainString(), qt.getVpQtMark().stripTrailingZeros().toPlainString());
String title = StringUtils.format("{}、{} {}", quSortNoArr[qtIndex], qt.getVerifyPaperQtName(), ext);
//表格
if (paperTemplate.getPtQtScoreColumn() != null && paperTemplate.getPtQtScoreColumn()) {
if (qtIndex > 1) addNewBlankLine(docx);
setQtTitleScoreColumn(title);
} else {
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQtTitleSize());
paragraph.setAlignment(ParagraphAlignment.LEFT); //字體左對齊
XWPFRun runText = paragraph.createRun();
runText.setBold(paperTemplate.getPtQtTitleBold());
runText.setText(title);
runText.setFontSize(paperTemplate.getPtQtTitleSize().intValue());
runText.setFontFamily(paperTemplate.getPtQtTitleFamily(), XWPFRun.FontCharRange.ascii);
}
}
/**
* 添加得分欄+標題
* 創建表格合併行
*
* @param title
*/
private void setQtTitleScoreColumn(String title) {
BigInteger w = this.ctSectPr.getPgSz().getW();
XWPFTable pTable = docx.createTable(2, 2);
pTable.setTableAlignment(TableRowAlign.LEFT);
CTTblPr ctTblPr = pTable.getCTTbl().getTblPr();
ctTblPr.getTblW().setType(STTblWidth.DXA);
//設置寬度
ctTblPr.getTblW().setW(w);
mergeCell(pTable, 0, 1, 1);
pTable.getRow(0).setHeight(getTwipFromCM(1).intValue());
pTable.getRow(1).setHeight(getTwipFromCM(1).intValue());
//得分欄 2 釐米
XWPFTableCell cellScore = pTable.getRow(0).getCell(0);
cellScore.setWidth(getTwipFromCM(2).toString());
setCellText(cellScore, "得分", paperTemplate.getPtQtScoreColumnFamily(), paperTemplate.getPtQtScoreColumnSize(), true, false, ParagraphAlignment.CENTER, XWPFTableCell.XWPFVertAlign.CENTER);
//標題欄
XWPFTableCell cellTitle = pTable.getRow(0).getCell(1);
cellTitle.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
XWPFParagraph paragraph = cellTitle.getParagraphs().get(0);
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQtTitleSize()).divide(BigInteger.valueOf(2)).intValue());
paragraph.setAlignment(ParagraphAlignment.LEFT);
paragraph.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun runText = paragraph.createRun();
if (paperTemplate.getPtQtTitleBold() != null) runText.setBold(paperTemplate.getPtQtTitleBold());
runText.setText(title);
runText.setFontSize(paperTemplate.getPtQtTitleSize().intValue());
runText.setFontFamily(paperTemplate.getPtQtTitleFamily(), XWPFRun.FontCharRange.ascii);
setTableBorder(pTable, false);
//設置邊框
CTTcBorders borders = CTTcBorders.Factory.newInstance();
CTBorder border = CTBorder.Factory.newInstance();
border.setVal(STBorder.SINGLE);
border.setColor(rgb_black);
border.setSz(BigInteger.ONE);
border.setSpace(BigInteger.ZERO);
borders.setLeft(border);
borders.setBottom(border);
borders.setRight(border);
borders.setTop(border);
CTTcPr ctTcPr1 = pTable.getRow(0).getCell(0).getCTTc().getTcPr();
if (ctTcPr1 == null) ctTcPr1 = pTable.getRow(0).getCell(0).getCTTc().addNewTcPr();
ctTcPr1.setTcBorders(borders);
CTTcPr ctTcPr2 = pTable.getRow(1).getCell(0).getCTTc().getTcPr();
if (ctTcPr2 == null) ctTcPr2 = pTable.getRow(1).getCell(0).getCTTc().addNewTcPr();
ctTcPr2.setTcBorders(borders);
}
//
// private void setQtTitleScoreColumn(String title) {
// BigInteger w = this.ctSectPr.getPgSz().getW();
// XWPFTable pTable = docx.createTable(1, 2);
// setTableBorder(pTable, false);
// pTable.setTableAlignment(TableRowAlign.LEFT);
// CTTblPr ctTblPr = pTable.getCTTbl().getTblPr();
// ctTblPr.getTblW().setType(STTblWidth.DXA);
// //設置寬度
// ctTblPr.getTblW().setW(w);
//
// pTable.getRow(0).setHeight(getTwipFromCM(2).intValue());
// //得分欄 2 釐米
// XWPFTableCell cellScore = pTable.getRow(0).getCell(0);
// cellScore.setWidth(getTwipFromCM(2).toString());
// setQtScoreColumnTable(cellScore);
//
// //標題欄
// XWPFTableCell cellTitle = pTable.getRow(0).getCell(1);
// cellTitle.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
//
// XWPFParagraph paragraph = cellTitle.getParagraphs().get(0);
// paragraph.setAlignment(ParagraphAlignment.LEFT);
// paragraph.setVerticalAlignment(TextAlignment.CENTER);
// XWPFRun runText = paragraph.createRun();
// if (paperTemplate.getPtQtTitleBold() != null) runText.setBold(paperTemplate.getPtQtTitleBold());
// runText.setText(title);
// runText.setFontSize(paperTemplate.getPtQtTitleSize().intValue());
// runText.setFontFamily(paperTemplate.getPtQtTitleFamily(), XWPFRun.FontCharRange.ascii);
// }
/**
* 合併單元格
*
* @param table 表格對象
* @param beginRowIndex 開始行索引
* @param endRowIndex 結束行索引
* @param colIndex 合併列索引
*/
private void mergeCell(XWPFTable table, int beginRowIndex, int endRowIndex, int colIndex) {
if (beginRowIndex == endRowIndex || beginRowIndex > endRowIndex) {
return;
}
//合併行單元格的第一個單元格
CTVMerge startMerge = CTVMerge.Factory.newInstance();
startMerge.setVal(STMerge.RESTART);
//合併行單元格的第一個單元格之後的單元格
CTVMerge endMerge = CTVMerge.Factory.newInstance();
endMerge.setVal(STMerge.CONTINUE);
CTTc ctTc = table.getRow(beginRowIndex).getCell(colIndex).getCTTc();
(ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr()).setVMerge(startMerge);
for (int i = beginRowIndex + 1; i <= endRowIndex; i++) {
ctTc = table.getRow(i).getCell(colIndex).getCTTc();
(ctTc.isSetTcPr() ? ctTc.getTcPr() : ctTc.addNewTcPr()).setVMerge(endMerge);
}
}
/**
* 獲取得分欄
*
* @return
*/
private void setQtScoreColumnTable(XWPFTableCell cell) {
XWPFTable xwpfTable = docx.createTable(2, 1);// new XWPFTable(CTTbl.Factory.newInstance(), cell, 2, 1);//
cell.insertTable(0, xwpfTable);
logger.info("表格:{}", cell.getTables().size());
xwpfTable = cell.getTables().get(0);
xwpfTable.setTableAlignment(TableRowAlign.CENTER);
xwpfTable.setWidth(getTwipFromCM(2).toString());
//設置邊框
setTableBorder(xwpfTable, true);
XWPFTableRow row1 = xwpfTable.getRow(0);
row1.setHeight(getTwipFromCM(1).intValue());
XWPFTableCell cell1 = row1.getCell(0);
cell1.setWidth(getTwipFromCM(2).toString());
cell1.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
XWPFParagraph p = cell1.getParagraphs().get(0);
p.setAlignment(ParagraphAlignment.CENTER); //字體左對齊
p.setVerticalAlignment(TextAlignment.CENTER);
XWPFRun rt = p.createRun();
rt.setBold(paperTemplate.getPtQtScoreColumnBold());
rt.setFontSize(paperTemplate.getPtScoreColumnSize());
rt.setFontFamily(paperTemplate.getPtScoreColumnFamily(), XWPFRun.FontCharRange.ascii);
rt.setText("得分");
XWPFTableRow row2 = xwpfTable.getRow(1);
row2.setHeight(getTwipFromCM(1).intValue());
}
/**
* 寫入題型試題
*
* @return
*/
private void writeQuestionForQt(VerifyPaperQt qt) throws IOException, InvalidFormatException {
for (VerifyPaperQu qu : qt.getVerifyPaperQuList()) {
getQuLines(qu, qu.getVpQuIndexQt());
}
}
/**
* 將文字寫入段落
*
* @param paragraph
* @param line
* @throws IOException
* @throws InvalidFormatException
*/
private void writeWordQuLine(XWPFParagraph paragraph, String line) throws IOException, InvalidFormatException {
for (LineIncludeImg lineImg : getContainImgArr(line)) {
if (StringUtils.isEmpty(lineImg.text) && lineImg.image == null) continue;
String imgLine = lineImg.text;
String picExtension = "";
if (imgLine == null) imgLine = "";
byte[] imgBytes = null;
if (lineImg.image != null) {
imgLine = "";
imgBytes = lineImg.image;
} else if (imgLine.startsWith("data:image/")) {
String base64Str = imgLine.substring(imgLine.indexOf(',') + 1);
picExtension = imgLine.substring(imgLine.indexOf('/') + 1, imgLine.indexOf(';'));
imgBytes = Base64.decode(base64Str);
for (int i = 0; i < imgBytes.length; ++i) {
if (imgBytes[i] < 0) {
imgBytes[i] += 256;
}
}
imgLine = "";
}
if (imgBytes != null) {
InputStream inputStream = new ByteArrayInputStream(imgBytes);
BufferedImage image = ImageIO.read(inputStream);
int width = image.getWidth();
int height = image.getHeight();
inputStream.close();
XWPFRun picRun = paragraph.createRun();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String picName = simpleDateFormat.format(DateUtils.getNowDate()) + "." + picExtension;
InputStream picStream = new ByteArrayInputStream(imgBytes);
//picRun.addPicture(picStream, getPictureType(picExtension), picName, width, height);
double scaling = 1.0;
//2)2023-06-13 優化 若圖片寬度超過word的最大寬值,圖片將會變形、所以通過等比縮小來處理 word文檔中:寬大最大650px(17釐米)
BigDecimal maxWidthWord = new BigDecimal(650);
BigDecimal widthDecimal = new BigDecimal(width);
if (widthDecimal.compareTo(maxWidthWord) > 0) {
BigDecimal divide = maxWidthWord.divide(widthDecimal, 2, RoundingMode.HALF_UP);
scaling = divide.doubleValue();
picRun.addPicture(picStream, getPictureType(picExtension), picName, Units.toEMU(width - width * scaling), Units.toEMU(height - height * scaling));
} else {
picRun.addPicture(picStream, getPictureType(picExtension), picName, Units.toEMU(width), Units.toEMU(height));
}
CTInline inline = picRun.getCTR().getDrawingList().get(0).getInlineArray(0);
inline.getDocPr().setId(1L); //id必須從1開始 解決打開文檔時 報錯(NPOI v2.4 已修複)
//此處是否可關閉
picStream.close();
} else if (StringUtils.isNotEmpty(imgLine)) {
//創建 run對象
XWPFRun runText = paragraph.createRun();
runText.setText(imgLine);
runText.setBold(paperTemplate.getPtQuStemBold());
runText.setFontSize(paperTemplate.getPtQuStemSize());
runText.setFontFamily(paperTemplate.getPtQuStemFamily(), XWPFRun.FontCharRange.ascii);
}
}
}
/**
* 獲取一道試題段落
*
* @param qe
* @param qIndex
* @return
*/
private void getQuLines(VerifyPaperQu qe, Integer qIndex) throws IOException, InvalidFormatException {
if (qe.getQuestionIsMultipleStem() == null || !qe.getQuestionIsMultipleStem()) {
//無子試題
//list.addAll();
getSingleLines(qe, qIndex);
} else {
//有子試題
getMultipleLines(qe, qIndex);
}
}
/**
* 獲取單題面行
*
* @param qe
* @return
*/
private void getSingleLines(VerifyPaperQu qe, Integer qIndex) throws IOException, InvalidFormatException {
VerifyPaperQuStem quStem = qe.getVerifyPaperQuStem();
//題面
String stem = StringUtils.getTextFromHTML(quStem.getQuStem(), imgRegex);
String line = StringUtils.format("{}. ", qIndex);
XWPFParagraph stemParagraph = createParagraph(paperTemplate.getPtQuStemSize());
//試題題號加粗
XWPFRun run = stemParagraph.createRun();
run.setText(line);
run.setFontSize(paperTemplate.getPtQuStemSize());
run.setBold(true);
writeWordQuLine(stemParagraph, stem);
//選項
for (int i = 0; i < 10; i++) {
char c = ((char) (65 + i));
String optionTag = String.valueOf(c).toUpperCase();
String pName = StringUtils.format("quOption{}{}", optionTag, i + 1);
String option = ReflectUtils.invokeGetter(quStem, pName);
if (StringUtils.isEmpty(option)) break;
option = StringUtils.getTextFromHTML(option, imgRegex);
XWPFParagraph optionParagraph = createParagraph(paperTemplate.getPtQuStemSize());
optionParagraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(optionParagraph, StringUtils.format("{}、{}", optionTag, option));
}
//答案
if (includeAnswer && StringUtils.isNotEmpty(quStem.getQuAnswer())) {
String answer = StringUtils.getTextFromHTML(quStem.getQuAnswer(), imgRegex);
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.AnswerStartWord, answer));
}
//評分點,填空題不顯示評分點
if (includeAnswer && StringUtils.isNotEmpty(quStem.getQuJudgePoint()) && !qe.getQuestionIsFillBlank()) {
JudgeRule judgeRule = JSONObject.parseObject(quStem.getQuJudgePoint(), JudgeRule.class);
if (judgeRule.getItems() != null && judgeRule.getItems().size() > 0) {
String points = StringUtils.joinWith(importRule.PointSeparateWord, judgeRule.getItems().get(0).getPoints().stream().map(p -> p.getAnswer()).toArray(String[]::new));
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.PointStartWord, points));
}
}
//評析
if (includeAnswer && StringUtils.isNotEmpty(quStem.getQuAnalyse())) {
String answer = StringUtils.getTextFromHTML(quStem.getQuAnalyse(), imgRegex);
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.AnalyseStartWord, answer));
}
if (includeAnswer) {
//難度
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.DifficultyStartWord, qe.getDifficulty()));
}
//知識點
if (includeAnswer && StringUtils.isNotEmpty(qe.getKnowledgeList())) {
//須處理關係 ?????????
//list.add(StringUtils.format("{}{}", importRule.KnowledgeStartWord,qe.getKnowledgeList().));
}
//年份
if (includeAnswer) {
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.YearStartWord, qe.getQuestionYear()));
}
//來源
if (includeAnswer && StringUtils.isNotEmpty(qe.getQuestionOrigin())) {
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.OriginStartWord, qe.getQuestionOrigin()));
}
docx.createParagraph().createRun().setText("\n");
//添加一個空行
//list.add("\n");
if (!includeAnswer && qe.getQuestionIsSubjective()) {
//自適應高度計算,根據答案文字數量
}
}
/**
* 獲取多題面試題行
*
* @param qe
* @return
*/
private void getMultipleLines(VerifyPaperQu qe, Integer qIndex) throws IOException, InvalidFormatException {
VerifyPaperQuStem quStem = qe.getVerifyPaperQuStem();
//題面
String stem = StringUtils.getTextFromHTML(quStem.getQuStem(), imgRegex);
String line = (StringUtils.format("{}.", qIndex.toString()));
XWPFParagraph stemParagraph = createParagraph(paperTemplate.getPtQuStemSize());
//試題題號加粗
XWPFRun run = stemParagraph.createRun();
run.setText(line);
run.setFontSize(paperTemplate.getPtQuStemSize());
run.setBold(true);
writeWordQuLine(stemParagraph, stem);
//子試題
for (int i = 0; i < qe.getSubQuestionList().size(); i++) {
getSubQuLines(qe.getSubQuestionList().get(i), i + 1);
}
//評析
if (includeAnswer && StringUtils.isNotEmpty(quStem.getQuAnalyse())) {
String answer = StringUtils.getTextFromHTML(quStem.getQuAnalyse(), imgRegex);
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.AnalyseStartWord, answer));
}
//難度
if (includeAnswer) {
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.DifficultyStartWord, qe.getDifficulty()));
//知識點
if (includeAnswer && StringUtils.isNotEmpty(qe.getKnowledgeList())) {
//list.add(StringUtils.format("{}{}", importRule.KnowledgeStartWord, qe.getKnowledgeName()));
}
//年份
paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.YearStartWord, qe.getQuestionYear()));
//來源
if (includeAnswer && StringUtils.isNotEmpty(qe.getQuestionOrigin())) {
paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize() * 2).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.OriginStartWord, qe.getQuestionOrigin()));
}
}
//添加一個空行
this.addNewBlankLine(docx);
}
/**
* 獲取子題面行
*
* @param subQe
* @return
*/
private void getSubQuLines(VerifyPaperQu subQe, Integer qSubIndex) throws IOException, InvalidFormatException {
//題面
VerifyPaperQuStem quStem = subQe.getVerifyPaperQuStem();
String stem = StringUtils.getTextFromHTML(quStem.getQuStem(), imgRegex);
String line = (StringUtils.format("{}). ", qSubIndex));
XWPFParagraph stemParagraph = createParagraph(paperTemplate.getPtQuStemSize());
stemParagraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize()).intValue());
//試題題號加粗
XWPFRun run = stemParagraph.createRun();
run.setText(line);
run.setFontSize(paperTemplate.getPtQuStemSize());
run.setBold(true);
writeWordQuLine(stemParagraph, stem);
//選項
for (int i = 0; i < 10; i++) {
char c = ((char) (65 + i));
String optionTag = String.valueOf(c).toUpperCase();
String pName = StringUtils.format("quOption{}{}", optionTag, i + 1);
String option = ReflectUtils.invokeGetter(quStem, pName);
if (StringUtils.isEmpty(option)) break;
option = StringUtils.getTextFromHTML(option, imgRegex);
XWPFParagraph optionParagraph = createParagraph(paperTemplate.getPtQuStemSize());
optionParagraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize()).intValue());
writeWordQuLine(optionParagraph, StringUtils.format("{}、{}", optionTag, option));
}
//答案
if (includeAnswer && StringUtils.isNotEmpty(quStem.getQuAnswer())) {
String answer = StringUtils.getTextFromHTML(quStem.getQuAnswer(), imgRegex);
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize()).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.AnswerStartWord, answer));
}
//評分點
if (includeAnswer && StringUtils.isNotEmpty(quStem.getQuJudgePoint()) && !subQe.getQuestionIsFillBlank()) {
JudgeRule judgeRule = JSONObject.parseObject(quStem.getQuJudgePoint(), JudgeRule.class);
if (judgeRule.getItems() != null && judgeRule.getItems().size() > 0) {
String points = StringUtils.joinWith(importRule.PointSeparateWord, judgeRule.getItems().get(0).getPoints().stream().map(p -> p.getAnswer()).toArray(String[]::new));
XWPFParagraph paragraph = createParagraph(paperTemplate.getPtQuStemSize());
paragraph.setIndentationLeft(getTwipFromPound(paperTemplate.getPtQuStemSize()).intValue());
writeWordQuLine(paragraph, StringUtils.format("{}{}", importRule.PointStartWord, points));
}
}
//添加一個空行
this.addNewBlankLine(docx);
if (!includeAnswer && subQe.getQuestionIsSubjective()) {
//自適應高度計算,根據答案文字數量
}
}
private boolean isMatchWithRegex(String regex, String input) {
if (StringUtils.isEmpty(regex)) return false;
return Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(input).find();
}
/**
* 通過圖片分割
*
* @param str
* @return
*/
public List<LineIncludeImg> getContainImgArr(String str) {
String imgReg = "<img\\s*/?[^>]*>";
List<LineIncludeImg> list = new ArrayList<>();
if (!isMatchWithRegex(imgReg, str)) {
list.add(new LineIncludeImg(str));
return list;
}
String getSrcReg = "\\bsrc\\b\\s*=\\s*['\"]?([^'\"]*)['\"]?";// "<img.*src=('|\")(.+?)('|\").*/?[^>]*>";
String removeSrcReg = "(^\\bsrc\\b\\s*=\\s*['\"]?)|(['\"]?$)";
//讀取base64
Matcher matcher = Pattern.compile(imgReg, Pattern.CASE_INSENSITIVE).matcher(str);
int _index = 0;
//2023-06-13 支持多個圖片
while (matcher.find()) {
if (matcher.start() > _index) {
list.add(new LineIncludeImg(str.substring(_index, matcher.start())));
}
_index = matcher.end();
String group = str.substring(matcher.start(), matcher.end());
Matcher mSrc = Pattern.compile(getSrcReg, Pattern.CASE_INSENSITIVE).matcher(group);
if (mSrc.find()) {
String srcText = mSrc.group();
String src = srcText.replaceAll(removeSrcReg, "");
if (src.startsWith("data:image/")) {
list.add(new LineIncludeImg(src));
} else if (StringUtils.ishttp(src)) {
String extend = FileTypeUtils.getFileType(src);
byte[] srcBase64 = ImageUtils.getImage(src);// UtilTools.getImageDataFromServer(src);
if (StringUtils.isEmpty(extend)) extend = FileTypeUtils.getFileExtendName(srcBase64);
//下載圖片獲取base64HttpUtils
list.add(new LineIncludeImg(srcBase64, extend));
}
}
}
if (_index < str.length()) {
list.add(new LineIncludeImg(str.substring(_index)));
}
return list;
}
/**
* 獲取圖片類型
*
* @param picExtension
* @return
*/
public int getPictureType(String picExtension) {
switch (picExtension.toUpperCase()) {
case "EMF":
return 2;
case "WMF":
return 3;
case "PICT":
return 4;
case "JPEG":
return 5;
case "PNG":
return 6;
case "DIB":
return 7;
case "GIF":
return 8;
case "TIFF":
return 9;
case "EPS":
return 10;
case "BMP":
return 11;
case "WPG":
return 12;
default:
return 5;
}
}
public class LineIncludeImg {
public LineIncludeImg(byte[] image, String extend) {
this.image = image;
this.extend = extend;
}
public LineIncludeImg(String text) {
this.text = text;
}
/**
* 文本內容
*/
public String text;
//圖片二進位數據
public byte[] image;
//擴展名,不帶 .
public String extend;
}
/**
* 每釐米567緹
*/
private double teePerCM = 567;
/**
* 每釐米28.35磅
*/
private double poundPerCm = 28.35;
/**
* 根據磅,獲取緹
*
* @return
*/
private BigInteger getTwipFromPound(double pound) {
Double d = pound * teePerCM / poundPerCm;
return BigInteger.valueOf(d.longValue());
}
/**
* 根據釐米,獲取緹
*
* @param cm
* @return
*/
private BigInteger getTwipFromCM(double cm) {
Double d = cm * teePerCM;
return BigInteger.valueOf(d.longValue());
}
private void setTableBorder(XWPFTable table, boolean hasBorder) {
if (hasBorder) {
table.setTopBorder(XWPFTable.XWPFBorderType.SINGLE, 1, 0, rgb_black);
table.setRightBorder(XWPFTable.XWPFBorderType.SINGLE, 1, 0, rgb_black);
table.setLeftBorder(XWPFTable.XWPFBorderType.SINGLE, 1, 0, rgb_black);
table.setBottomBorder(XWPFTable.XWPFBorderType.SINGLE, 1, 0, rgb_black);
table.setInsideVBorder(XWPFTable.XWPFBorderType.SINGLE, 1, 0, rgb_black);
table.setInsideHBorder(XWPFTable.XWPFBorderType.SINGLE, 1, 0, rgb_black);
} else {
table.setLeftBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
table.setRightBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
table.setTopBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
table.setBottomBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
table.setInsideHBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
table.setInsideVBorder(XWPFTable.XWPFBorderType.NONE, 0, 0, "");
}
}
/**
* 獲取預設模板
*
* @return
*/
public static PaperTemplate getDefaultPaperTemplate() {
PaperTemplate paperTemplate = new PaperTemplate();
paperTemplate.setPtFooterMargin(BigDecimal.ZERO);
paperTemplate.setPaperTemplateName("預設模板配置");
paperTemplate.setPaperTemplateCode("default-template");
paperTemplate.setPtPageSize("A4");
paperTemplate.setPtDirection(0);
paperTemplate.setPtColumNum(1);
paperTemplate.setPtEnFontFamily("");//Helvetica
paperTemplate.setPtTitle(true);
paperTemplate.setPtTitleBold(true);
paperTemplate.setPtTitleFontFamily("黑體");
paperTemplate.setPtTitleFontSize(18);
paperTemplate.setPtTitleSub("");
paperTemplate.setPtTitleSubBold(true);
paperTemplate.setPtTitleSubFontFamily("黑體");
paperTemplate.setPtTitleSubFontSize(18);
paperTemplate.setPtPaperInfoShow(true);
paperTemplate.setPtPaperInfoFamily("黑體");
paperTemplate.setPtPaperInfoSize(12);
paperTemplate.setPtPaperInfoBold(false);
paperTemplate.setPtShowAnswerMode(true);
paperTemplate.setPtShowAnswerTime(true);
paperTemplate.setPtShowPaperMark(true);
paperTemplate.setPtScoreColumn(true);
paperTemplate.setPtScoreColumnFamily("宋體");
paperTemplate.setPtScoreColumnSize(12);
paperTemplate.setPtScoreColumnBold(false);
paperTemplate.setPtReadme("");
paperTemplate.setPtReadmeBold(false);
paperTemplate.setPtReadmeFontFamily("宋體");
paperTemplate.setPtReadmeBold(false);
paperTemplate.setPtReadmeFontSize(12);
paperTemplate.setPtScoreColumn(true);
paperTemplate.setPtScoreColumnBold(false);
paperTemplate.setPtScoreColumnFamily("宋體");
paperTemplate.setPtScoreColumnSize(12);
paperTemplate.setPtQtScoreColumn(true);
paperTemplate.setPtQtScoreColumnBold(true);
paperTemplate.setPtQtScoreColumnFamily("宋體");
paperTemplate.setPtQtScoreColumnSize(12);
paperTemplate.setPtQtTitleBold(true);
paperTemplate.setPtQtTitleFamily("宋體");
paperTemplate.setPtQtTitleSize(12);
paperTemplate.setPtQuStemBold(false);
paperTemplate.setPtQuStemFamily("宋體");
paperTemplate.setPtQuStemSize(11);
paperTemplate.setPtMarginTop(BigDecimal.valueOf(2.54));
paperTemplate.setPtMarginBottom(BigDecimal.valueOf(2.54)