Compare commits

...

2 Commits

Author SHA1 Message Date
hyc
8204c4d9b2 Merge pull request '【修改】完善wps文字表格考点' (#9) from hyc into master
Reviewed-on: #9
2025-10-20 06:19:51 +08:00
huababa1
2ea56ab5c8 【修改】完善wps文字表格考点 2025-10-20 06:19:16 +08:00
11 changed files with 415 additions and 137 deletions

View File

@@ -74,4 +74,7 @@ public class InfoController {
public Result<Map<String, String>> getButtonInfo(@RequestBody StuInfoVo stuInfoVo) throws IOException {
return Result.success(autoForButtonService.getButtonInfo(stuInfoVo));
}
}

View File

@@ -50,6 +50,10 @@ public class JudgementWpsExcelServiceImpl implements JudgementWpsExcelService {
if (docxInfo.length > 5 && docxInfo[5] != null && !docxInfo[5].isEmpty()) {
cell.add(docxInfo[5]);
}
if (docxInfo.length > 6 && docxInfo[6] != null && !docxInfo[6].isEmpty()) {
wpsXlsxInfoVo.setKeyWords(docxInfo[6]);
}
wpsXlsxInfoVo.setCell(cell);
wpsXlsxInfos.add(wpsXlsxInfoVo);
}

View File

@@ -90,6 +90,7 @@ public class XlsxMaster {
Class<?>[] paramTypes = {
org.apache.poi.ss.usermodel.Cell.class,
org.apache.poi.ss.usermodel.Workbook.class,
String.class
};
Method methodWithArgs = excelFunctions.getClass().getMethod(function, paramTypes);
@@ -98,7 +99,7 @@ public class XlsxMaster {
for (String cellRef : wpsXlsxInfoVo.getCell()) {
org.apache.poi.ss.usermodel.Cell poiCell = getPoiCellFromRef(workbook, sheetName, cellRef);
if (poiCell == null) continue;
String value = (String) methodWithArgs.invoke(excelFunctions, poiCell, workbook);
String value = (String) methodWithArgs.invoke(excelFunctions, poiCell, workbook,wpsXlsxInfoVo.getKeyWords());
if (value != null) {
if ("getCellDataFormat".equals(function)){

View File

@@ -34,13 +34,13 @@ public class CellIng {
Map.entry("SLANTEDDASHDOT", "斜点划线")
);
// 获取左框线样式
public String getLeftBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getLeftBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
BorderStyle border = style.getBorderLeft();
return border != null ? borderStyleChineseMap.getOrDefault(border.name() , border.name() ): "";
}
// 获取左框线颜色
public String getLeftBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getLeftBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
if (style instanceof XSSFCellStyle) {
XSSFCellStyle xssfStyle = (XSSFCellStyle) style;
@@ -51,14 +51,14 @@ public class CellIng {
}
// 获取上框线样式
public String getTopBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getTopBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
BorderStyle border = style.getBorderTop();
return border != null ? borderStyleChineseMap.getOrDefault(border.name() , border.name() ): "";
}
// 获取上框线颜色
public String getTopBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getTopBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
if (style instanceof XSSFCellStyle) {
XSSFCellStyle xssfStyle = (XSSFCellStyle) style;
@@ -68,14 +68,14 @@ public class CellIng {
}
// 获取右框线样式
public String getRightBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getRightBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
BorderStyle border = style.getBorderRight();
return border != null ? borderStyleChineseMap.getOrDefault(border.name() , border.name() ): "";
}
// 获取右框线颜色
public String getRightBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getRightBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
if (style instanceof XSSFCellStyle) {
XSSFCellStyle xssfStyle = (XSSFCellStyle) style;
@@ -85,14 +85,14 @@ public class CellIng {
}
// 获取下框线样式
public String getBottomBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getBottomBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
BorderStyle border = style.getBorderBottom();
return border != null ? borderStyleChineseMap.getOrDefault(border.name() , border.name() ): "";
}
// 获取下框线颜色
public String getBottomBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) {
public String getBottomBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb,String keyWords) {
CellStyle style = cell.getCellStyle();
if (style instanceof XSSFCellStyle) {
XSSFCellStyle xssfStyle = (XSSFCellStyle) style;
@@ -101,16 +101,30 @@ public class CellIng {
return "";
}
// 获取单元格的公式表达式(字符串形式)
public String getFormulaExpression(Cell cell, Workbook wb) {
public String getFormulaExpression(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
if (cell.getCellType() == CellType.FORMULA) {
return cell.getCellFormula();
}
return "";
}
// 获取单元格的公式表达式关键字
public String getFormulaExpressionContains(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
if (cell.getCellType() == CellType.FORMULA) {
String formula = cell.getCellFormula();
// 转为小写再比较
if (formula.toLowerCase().contains(keyWords.toLowerCase())) {
return keyWords; // 包含关键字时返回公式内容
} else {
return ""; // 不包含关键字
}
}
return "";
}
// 获取单元格的公式计算结果(已经计算出来的值,字符串形式)
public String getFormulaResult(Cell cell, Workbook wb) {
public String getFormulaResult(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
if (cell.getCellType() != CellType.FORMULA) {
return null; // 不是公式单元格返回null
@@ -136,7 +150,7 @@ public class CellIng {
}
// 获取单元格字体名称
public String getFontName(Cell cell, Workbook wb) {
public String getFontName(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -150,7 +164,7 @@ public class CellIng {
}
// 获取单元格字体字号
public String getFontSize(Cell cell, Workbook wb) {
public String getFontSize(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -168,7 +182,7 @@ public class CellIng {
// 获取单元格字形(加粗 / 斜体
public String getFontStyle(Cell cell, Workbook wb) {
public String getFontStyle(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -188,7 +202,7 @@ public class CellIng {
// 获取单元格下划线
// 获取单元格下划线类型
public String getUnderline(Cell cell, Workbook wb) {
public String getUnderline(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -215,7 +229,7 @@ public class CellIng {
}
// 获取单元格颜色
public String getFontColor(Cell cell, Workbook wb) {
public String getFontColor(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -240,7 +254,7 @@ public class CellIng {
return "";
}
// 删除线
public static String getCellStrikeThrough(Cell cell, Workbook wb) {
public static String getCellStrikeThrough(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -249,7 +263,7 @@ public class CellIng {
}
// 上标
public static String getCellSuperScript(Cell cell, Workbook wb) {
public static String getCellSuperScript(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -258,7 +272,7 @@ public class CellIng {
}
// 下标
public static String getCellSubScript(Cell cell, Workbook wb) {
public static String getCellSubScript(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -269,7 +283,7 @@ public class CellIng {
// 获取斜下框线样式
public static String getDiagonalDownBorderStyle(Cell cell, Workbook wb) {
public static String getDiagonalDownBorderStyle(Cell cell, Workbook wb,String keyWords) {
if (cell == null || !(cell instanceof XSSFCell)) return "";
XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle();
@@ -295,7 +309,7 @@ public class CellIng {
}
// 斜下框线→颜色
public static String getDiagonalDownBorderColor(Cell cell, Workbook wb) {
public static String getDiagonalDownBorderColor(Cell cell, Workbook wb,String keyWords) {
if (cell == null || !(cell instanceof XSSFCell)) return "";
XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle();
@@ -318,7 +332,7 @@ public class CellIng {
}
// 斜上框线→样式
public static String getDiagonalUpBorderStyle(Cell cell, Workbook wb) {
public static String getDiagonalUpBorderStyle(Cell cell, Workbook wb,String keyWords) {
if (cell == null || !(cell instanceof XSSFCell)) return "";
XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle();
@@ -340,7 +354,7 @@ public class CellIng {
}
// 斜上框线→颜色
public static String getDiagonalUpBorderColor(Cell cell, Workbook wb) {
public static String getDiagonalUpBorderColor(Cell cell, Workbook wb,String keyWords) {
if (cell == null || !(cell instanceof XSSFCell)) return "";
XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle();
@@ -378,14 +392,14 @@ public class CellIng {
// ===== 获取单元格文本String =====
public String getCellText(Cell cell, Workbook wb) {
public String getCellText(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
DataFormatter formatter = new DataFormatter();
return formatter.formatCellValue(cell);
}
// ===== 获取单元格值
public String getCellValue(Cell cell, Workbook wb) {
public String getCellValue(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
switch (cell.getCellType()) {
@@ -434,7 +448,7 @@ public class CellIng {
}
// 数字格式
public static String getCellDataFormat(Cell cell, Workbook wb) {
public static String getCellDataFormat(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -442,7 +456,7 @@ public class CellIng {
}
// 水平对齐
public static String getCellHorizontalAlignment(Cell cell, Workbook wb) {
public static String getCellHorizontalAlignment(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -451,7 +465,7 @@ public class CellIng {
}
// 垂直对齐
public static String getCellVerticalAlignment(Cell cell, Workbook wb) {
public static String getCellVerticalAlignment(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -460,7 +474,7 @@ public class CellIng {
}
// 缩进
public static String getCellIndent(Cell cell, Workbook wb) {
public static String getCellIndent(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "0";
CellStyle style = cell.getCellStyle();
if (style == null) return "0";
@@ -468,7 +482,7 @@ public class CellIng {
}
// 自动换行
public static String getCellWrapText(Cell cell, Workbook wb) {
public static String getCellWrapText(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -476,7 +490,7 @@ public class CellIng {
}
// 缩小字体填充ShrinkToFit
public static String getCellShrinkToFit(Cell cell, Workbook wb) {
public static String getCellShrinkToFit(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -484,7 +498,7 @@ public class CellIng {
}
// 合并单元格
public static String getCellMerged(Cell cell, Workbook wb) {
public static String getCellMerged(Cell cell, Workbook wb,String keyWords) {
if (cell == null || wb == null) return "";
Sheet sheet = cell.getSheet();
if (sheet == null) return "";
@@ -503,7 +517,7 @@ public class CellIng {
// 文本方向rotation
public static String getCellTextRotation(Cell cell, Workbook wb) {
public static String getCellTextRotation(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "0";
CellStyle style = cell.getCellStyle();
if (style == null) return "0";
@@ -511,7 +525,7 @@ public class CellIng {
}
// 文本样式(加粗/斜体/下划线等)
public static String getCellFontStyle(Cell cell, Workbook wb) {
public static String getCellFontStyle(Cell cell, Workbook wb,String keyWords) {
if (cell == null) return "";
CellStyle style = cell.getCellStyle();
if (style == null) return "";
@@ -534,7 +548,7 @@ public class CellIng {
}
// 高度(行高)
public static String getCellRowHeight(Cell cell, Workbook wb) {
public static String getCellRowHeight(Cell cell, Workbook wb,String keyWords) {
if (cell == null || cell.getSheet() == null) return "默认";
Row row = cell.getRow();
if (row == null) return "默认";
@@ -542,7 +556,7 @@ public class CellIng {
}
// 宽度(列宽)
public static String getCellColumnWidth(Cell cell, Workbook wb) {
public static String getCellColumnWidth(Cell cell, Workbook wb,String keyWords) {
if (cell == null || cell.getSheet() == null) return "默认";
int colWidth = cell.getSheet().getColumnWidth(cell.getColumnIndex()); // 单位 1/256字符
return String.format("%.2f pt", colWidth / 256.0 * 7); // 约 7pt/字符宽,可根据字体调整

View File

@@ -27,4 +27,6 @@ public class WpsXlsxInfoVo {
// 方式方法
private String method;
private String keyWords;
}

View File

@@ -116,28 +116,41 @@ public class DocxMaster {
if (realObj instanceof P) {
P paragraph = (P) realObj;
PPr pPr = paragraph.getPPr();
if (pPr == null || pPr.getSectPr() == null&& firstName.contains("段落")) {
// 开始查询段落的属性,非句子的属性
if (index == Integer.parseInt(indexParm)) {
// 查询出具体想要查询哪个段落的数据
// 目标对象
Paragraphs paragraphsFunction = new Paragraphs();
// 获取参数中类型的定义
Class<?>[] paramTypes = {P.class, StyleDefinitionsPart.class, WordprocessingMLPackage.class, NumberingDefinitionsPart.class};
// 带参数的方法调用示例
Method methodWithArgs = paragraphsFunction.getClass().getMethod(function, paramTypes);
// 实际参数值
Object[] arguments = {paragraph, stylePart, wordMLPackage, ndp};
String value = (String) methodWithArgs.invoke(obj, arguments);
if (value!=null){
// 写入结果
judgementWordsVOS = setJudgementWord(
judgementWordsVOS,
docxFunction + "@" + value,
firstName + examName + value
);
// ---------- 段落 ----------
if (firstName.contains("段落")) {
// 如果是段落,则只在 pPr != null 或者其他段落条件下执行
if (pPr == null || pPr.getSectPr() == null) {
// 开始查询段落的属性,非句子的属性
if (index == Integer.parseInt(indexParm)) {
// 查询出具体想要查询哪个段落的数据
// 目标对象
Paragraphs paragraphsFunction = new Paragraphs();
// 获取参数中类型的定义
Class<?>[] paramTypes = {P.class, StyleDefinitionsPart.class, WordprocessingMLPackage.class, NumberingDefinitionsPart.class};
// 带参数的方法调用示例
Method methodWithArgs = paragraphsFunction.getClass().getMethod(function, paramTypes);
// 实际参数值
Object[] arguments = {paragraph, stylePart, wordMLPackage, ndp};
String value = (String) methodWithArgs.invoke(obj, arguments);
if (value != null) {
if ("getParagraphListLvlText".equals(function) || ("getParagraphListAbstractNumId").equals(function) || ("getParagraphListLvlFuHao").equals(function)) {
judgementWordsVOS = setJudgementWord(
judgementWordsVOS,
docxFunction + "@" + value,
firstName + examName + "【是否正确】"
);
} else {
// 写入结果
judgementWordsVOS = setJudgementWord(
judgementWordsVOS,
docxFunction + "@" + value,
firstName + examName + value
);
}
}
}
}
}
}
}
}
@@ -372,7 +385,7 @@ public class DocxMaster {
WordprocessingMLPackage.class
);
String value = (String) method.invoke(
String value = (String) method.invoke(
null,
judgementWordsVOS,
inline,

View File

@@ -77,7 +77,7 @@ public class Drawing {
String width = String.format("%.2fcm", widthCm);
return width;
}
//
//布局 锁定横纵比
public static String getLayoutLock(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect();
@@ -133,6 +133,15 @@ public class Drawing {
}
return "未知";
}
// 位置 → 水平 → 绝对位置
public static String getLayoutHorizontalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getPositionH() == null) return "未知";
long offsetEmu = anchor.getPositionH().getPosOffset(); // EMU单位
double offsetCm = offsetEmu / 360000.0;
return String.format("相对于右侧%.2fcm", offsetCm);
}
// 位置 → 水平 → 相对于
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
@@ -171,7 +180,15 @@ public class Drawing {
}
return "未知";
}
// 位置 → 垂直 → 绝对位置
public static String getLayoutVerticalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getPositionV() == null) return "未知";
long offsetEmu = anchor.getPositionV().getPosOffset(); // EMU单位
double offsetCm = offsetEmu / 360000.0;
return String.format("相对于下侧%.2fcm", offsetCm);
}
// 位置 → 垂直 → 相对于
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getPositionV() == null) return "未知";

View File

@@ -165,7 +165,14 @@ public class DrawingInline {
return "未知";
}
}
// 位置 → 水平 → 绝对位置
public static String getLayoutHorizontalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
return "";
}
// 位置 → 垂直 → 绝对位置
public static String getLayoutVerticalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
return "";
}
// 位置 → 水平 → 相对于
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null) return "未知";

View File

@@ -34,12 +34,16 @@ public class Convert {
public static String convertOutlineLvl(String val) {
switch (val) {
case "0":
return "标题1";
case "1":
return "标题2";
default:
return null;
case "0": return "一级";
case "1": return "二级";
case "2": return "三级";
case "3": return "四级";
case "4": return "五级";
case "5": return "六级";
case "6": return "七级";
case "7": return "八级";
case "8": return "九级";
default: return "正文";
}
}

View File

@@ -54,7 +54,7 @@ public class Paragraphs {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getJc() != null) {
return Convert.convertJc(style.getPPr().getOutlineLvl().getVal().toString());
return Convert.convertOutlineLvl(style.getPPr().getOutlineLvl().getVal().toString());
}
}
return "正文";
@@ -103,45 +103,71 @@ public class Paragraphs {
}
/// 段落格式(缩进) 首行缩进 磅
public static String getParagraphIndFirstLine(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
PPrBase.Ind ind = pPr.getInd();
if (ind != null) {
if (ind.getFirstLine() != null) {
return ind.getFirstLine().intValue() + "";
}
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
Double firstLine = null;
if (pPr != null && pPr.getInd() != null && pPr.getInd().getFirstLine() != null) {
firstLine = pPr.getInd().getFirstLine().doubleValue();
} else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null) {
PPrBase.Ind indStyle = style.getPPr().getInd();
if (indStyle.getFirstLine() != null) {
return indStyle.getFirstLine().intValue() + "";
}
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null &&
style.getPPr().getInd().getFirstLine() != null) {
firstLine = style.getPPr().getInd().getFirstLine().doubleValue();
}
}
return "";
if (firstLine == null) return "";
// 1 字符 ≈ 200 twip即 10 磅)
double chars = firstLine / 240.0;
// 四舍五入到 0.5
double rounded = Math.round(chars * 2) / 2.0;
// 如果是整数,去掉小数点
if (rounded == (int) rounded) {
return (int) rounded + " 字符";
} else {
return rounded + " 字符";
}
}
/// 段落格式(缩进) 悬挂缩进 磅
public static String getParagraphIndHanging(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
PPrBase.Ind ind = pPr.getInd();
if (ind != null) {
if (ind.getHanging() != null) {
return ind.getHanging().intValue() + "";
}
Double hanging = null;
// 1⃣ 直接在段落中取
if (pPr != null && pPr.getInd() != null && pPr.getInd().getHanging() != null) {
hanging = pPr.getInd().getHanging().doubleValue();
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
// 2⃣ 样式中取
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null) {
PPrBase.Ind indStyle = style.getPPr().getInd();
if (indStyle.getHanging() != null) {
return indStyle.getHanging().intValue() + "";
}
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null &&
style.getPPr().getInd().getHanging() != null) {
hanging = style.getPPr().getInd().getHanging().doubleValue();
}
}
return "";
if (hanging == null) return "";
// 1 字符 ≈ 200 twip10 磅)
double chars = hanging / 240.0;
// 四舍五入到 0.5
double rounded = Math.round(chars * 2) / 2.0;
// 输出为整数或 .5 字符
if (rounded == (int) rounded) {
return (int) rounded + " 字符";
} else {
return rounded + " 字符";
}
}
/// 段落格式(间距) 段前行
@@ -166,39 +192,75 @@ public class Paragraphs {
}
/// 段落格式(间距) 段前磅
public static String getParagraphSpacingBefore(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
if (pPr != null && pPr.getSpacing() != null) {
if(pPr.getSpacing().getBefore()!=null){
return String.valueOf(pPr.getSpacing().getBefore().intValue() / 20)+"";
}
Double before = null;
// 1⃣ 段落自身定义
if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getBefore() != null) {
before = pPr.getSpacing().getBefore().doubleValue();
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
// 2⃣ 从样式定义中继承
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null) {
PPrBase.Spacing spacing = style.getPPr().getSpacing();
return String.valueOf(style.getPPr().getSpacing().getBefore().intValue() / 20)+"";
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null &&
style.getPPr().getSpacing().getBefore() != null) {
before = style.getPPr().getSpacing().getBefore().doubleValue();
}
}
return "";
if (before == null) return "";
// 转换为磅1磅 = 20twip
double point = before / 20.0;
// 四舍五入到 0.5
double rounded = Math.round(point * 2) / 2.0;
// 输出格式
if (rounded == (int) rounded) {
return (int) rounded + "";
} else {
return rounded + "";
}
}
/// 段落格式(间距) 段后磅
public static String getParagraphSpacingAfter(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
if (pPr != null && pPr.getSpacing() != null) {
if(pPr.getSpacing().getAfter()!=null){
return String.valueOf(pPr.getSpacing().getAfter().intValue() / 20)+"";
}
Double after = null;
// 1⃣ 段落自身定义
if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getAfter() != null) {
after = pPr.getSpacing().getAfter().doubleValue();
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
// 2⃣ 样式定义
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null) {
PPrBase.Spacing spacing = style.getPPr().getSpacing();
return String.valueOf(style.getPPr().getSpacing().getAfter().intValue() / 20)+"";
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null &&
style.getPPr().getSpacing().getAfter() != null) {
after = style.getPPr().getSpacing().getAfter().doubleValue();
}
}
return "";
if (after == null) return "";
// 转换为磅1 磅 = 20 twip
double point = after / 20.0;
// 四舍五入到 0.5 磅
double rounded = Math.round(point * 2) / 2.0;
// 输出格式:整数不带小数,半磅保留 .5
if (rounded == (int) rounded) {
return (int) rounded + "";
} else {
return rounded + "";
}
}
/// 段落格式(间距) 段后行
@@ -511,6 +573,57 @@ public class Paragraphs {
}
return "";
}
/// 段落 编号列表 编号序号
public static String getParagraphListLvlNumber(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
PPr pPr = paragraph.getPPr();
if (pPr == null || pPr.getNumPr() == null) return "";
BigInteger numId = pPr.getNumPr().getNumId() != null ? pPr.getNumPr().getNumId().getVal() : null;
BigInteger ilvl = pPr.getNumPr().getIlvl() != null ? pPr.getNumPr().getIlvl().getVal() : null;
if (numId == null || ilvl == null || ndp == null) return "";
// 获取文档所有段落
List<Object> paragraphs = wordMLPackage.getMainDocumentPart().getContent();
int count = 0;
for (Object obj : paragraphs) {
if (!(obj instanceof P)) continue;
P p = (P) obj;
if (p == paragraph) break; // 到当前段落停止计数
PPr pp = p.getPPr();
if (pp != null && pp.getNumPr() != null) {
BigInteger nid = pp.getNumPr().getNumId() != null ? pp.getNumPr().getNumId().getVal() : null;
BigInteger lvl = pp.getNumPr().getIlvl() != null ? pp.getNumPr().getIlvl().getVal() : null;
if (numId.equals(nid) && ilvl.equals(lvl)) count++;
}
}
// 获取 Lvl 对象
Numbering.Num num = ndp.getJaxbElement().getNum().stream()
.filter(n -> n.getNumId().equals(numId))
.findFirst().orElse(null);
if (num == null) return "";
BigInteger abstractNumId = num.getAbstractNumId().getVal();
Numbering.AbstractNum absNum = Convert.getAbstractNumById(ndp, abstractNumId);
if (absNum == null) return "";
Lvl lvl = absNum.getLvl().stream()
.filter(l -> l.getIlvl().equals(ilvl))
.findFirst().orElse(null);
if (lvl == null) return "";
// 获取编号模板和起始值
String lvlText = lvl.getLvlText() != null ? lvl.getLvlText().getVal() : "%1.";
BigInteger start = lvl.getStart() != null ? lvl.getStart().getVal() : BigInteger.ONE;
// 实际编号
int actualNumber = start.intValue() + count;
// 替换模板中的 %1、%2… 注意 ilvl 从 0 开始
String result = lvlText.replace("%" + (ilvl.intValue() + 1), String.valueOf(actualNumber));
return result;
}
// 段落边框 样式(带汉化)
public static String getParagraphBorderStyle(P paragraph, StyleDefinitionsPart stylePart,
@@ -523,6 +636,7 @@ public class Paragraphs {
CTBorder bottom = border.getBottom();
CTBorder left = border.getLeft();
CTBorder right = border.getRight();
if (top != null && bottom != null && left != null && right != null) {
String topVal = translateBorderVal(top.getVal().value());
String bottomVal = translateBorderVal(bottom.getVal().value());

View File

@@ -1,16 +1,16 @@
package com.example.exam.exam.service.wpsword.docx4j.section;
import com.example.exam.exam.service.wpsword.docx4j.utils.ColorUtils;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.JAXBException;
import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.dml.Graphic;
import org.docx4j.dml.GraphicData;
import org.docx4j.dml.wordprocessingDrawing.Anchor;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.mce.AlternateContent;
import org.docx4j.model.structure.DocumentModel;
import org.docx4j.model.fields.FieldUpdater;
import org.docx4j.model.structure.HeaderFooterPolicy;
import org.docx4j.model.structure.PageDimensions;
import org.docx4j.model.structure.SectionWrapper;
@@ -18,27 +18,26 @@ import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.Part;
import org.docx4j.openpackaging.parts.WordprocessingML.*;
import org.docx4j.vml.CTFill;
import org.docx4j.vml.CTTextbox;
import org.docx4j.wml.*;
import org.docx4j.wml.CTBackground;
import org.docx4j.wml.CTBorder;
import org.docx4j.wml.CTColumn;
import org.docx4j.wml.CTColumns;
import org.docx4j.wml.CTDocGrid;
import org.docx4j.wml.CTShd;
import org.docx4j.wml.STPageOrientation;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -77,7 +76,9 @@ public class SectionPage {
long topTwips = pgMar.getTop().longValue(); // twips
double topPt = topTwips / 20.0; // 转换为磅
double topCm = topPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", topPt, topCm);
return String.format("%.1f磅(%.2f厘米)", topPt, topCm);
// return String.format("%.2f厘米", topCm);
}
}
return null;
@@ -95,6 +96,7 @@ public class SectionPage {
double bottomPt = bottomTwips / 20.0; // 磅
double bottomCm = bottomPt * 0.0352778; // 厘米
return String.format("%.1f磅(%.2f厘米)", bottomPt, bottomCm);
// return String.format("%.2f厘米", bottomCm);
}
}
return null;
@@ -112,6 +114,7 @@ public class SectionPage {
double leftPt = leftTwips / 20.0; // 转换为磅
double leftCm = leftPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", leftPt, leftCm);
// return String.format("%.2f厘米", leftCm);
}
}
return null;
@@ -128,7 +131,8 @@ public class SectionPage {
long rightTwips = pgMar.getRight().longValue(); // twips
double rightPt = rightTwips / 20.0; // 转换为磅
double rightCm = rightPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm);
// return String.format("%.2f厘米", rightCm);
return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm);
}
}
return null;
@@ -146,6 +150,7 @@ public class SectionPage {
double gutterPt = gutterTwips / 20.0; // 转换为磅
double gutterCm = gutterPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", gutterPt, gutterCm);
// return String.format("%.2f厘米", gutterCm);
}
}
return null;
@@ -1169,33 +1174,130 @@ public class SectionPage {
}
// 页脚(元素) 包含页码 返回 是/否
// 页脚(元素) 页码
public static String getHeaderContainsPageNumber(
P paragraph,
HeaderFooterPolicy hfp,
WordprocessingMLPackage wordMLPackage,
List<SectionWrapper> sections) {
List<SectionWrapper> sections) throws IOException, SAXException, Docx4JException, JAXBException {
if (hfp == null) return "无页码";
if (hfp == null) return "";
// 获取页脚对象列表
// 更新字段,保证获取显示值
FieldUpdater updater = new FieldUpdater(wordMLPackage);
updater.update(true);
FooterPart[] footerParts = new FooterPart[]{
hfp.getFirstFooter(),
hfp.getEvenFooter(),
hfp.getDefaultFooter()
};
Set<String> chineseNumbers = Set.of("","","","","","","","","","");
Set<String> capitalChineseNumbers = Set.of("","","","","","","","","","");
for (FooterPart fp : footerParts) {
if (fp == null) continue;
String xml = XmlUtils.marshaltoString(fp.getJaxbElement(), true, true);
System.out.println("Footer XML:\n" + xml);
// 判断是否包含页码域 (PAGE 或 NUMPAGES)
if (xml.contains("PAGE") || xml.contains("NUMPAGES")) {
return "";
org.w3c.dom.Document doc = XmlUtils.getNewDocumentBuilder()
.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
NodeList pNodes = doc.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "p");
for (int i = 0; i < pNodes.getLength(); i++) {
org.w3c.dom.Element pElem = (org.w3c.dom.Element) pNodes.item(i);
// 检查 PAGE / NUMPAGES
NodeList instrNodes = pElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "instrText");
boolean hasPAGE = false, hasNUMPAGES = false;
for (int j = 0; j < instrNodes.getLength(); j++) {
String instr = instrNodes.item(j).getTextContent();
if (instr.contains("PAGE")) hasPAGE = true;
if (instr.contains("NUMPAGES")) hasNUMPAGES = true;
}
// 拼接 run 内文本 + 字段文本
NodeList rNodes = pElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "r");
StringBuilder textBuilder = new StringBuilder();
for (int j = 0; j < rNodes.getLength(); j++) {
Element rElem = (Element) rNodes.item(j);
NodeList tNodes = rElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "t");
for (int k = 0; k < tNodes.getLength(); k++) {
textBuilder.append(tNodes.item(k).getTextContent());
}
NodeList instrNodesR = rElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "instrText");
for (int k = 0; k < instrNodesR.getLength(); k++) {
textBuilder.append(instrNodesR.item(k).getTextContent());
}
}
String text = textBuilder.toString().trim();
System.out.println("页脚文本内容: " + text);
if (text.isEmpty()) continue;
boolean containsChineseNum = chineseNumbers.stream().anyMatch(text::contains);
boolean containsDigit = text.matches(".*\\d+.*");
// -------------------- 核心逻辑 --------------------
// 数字斜杠序列
if (text.matches(".*\\d+\\s*/\\s*(?:\\d+|PAGE|NUMPAGES).*")) {
return "1 / x";
}
if (containsChineseNum && hasPAGE && hasNUMPAGES) {
return "第一页 共x页"; // WPS 中文 + PAGE + NUMPAGES
}
if (containsDigit && hasPAGE && hasNUMPAGES) {
return "第1页 共x页"; // 普通数字 + PAGE + NUMPAGES
}
if (containsChineseNum && hasPAGE && !hasNUMPAGES) {
return "第一页"; // WPS 中文单页
}
// ✅ 关键部分:支持短横线页码形式
if (text.matches(".*[-]\\s*\\d+\\s*[-].*")) {
return "-1-, -2-, -3- ..."; // 短横线包裹数字
}
if (text.matches(".*[—]\\s*\\d+\\s*[—].*")) {
return "— 1 —, — 2 —, — 3 — ..."; // 数字横杠序列
}
// 半角数字序列
if (text.matches("\\d+\\s*PAGE.*\\d+\\s*PAGE.*")) {
return "1, 2, 3 ..."; // 数字序列
}
// 全角数字序列
if (text.matches(".*[-]+\\s*PAGE.*[-]+\\s*PAGE.*")) {
return " ...";
}
if (containsDigit && hasPAGE && !hasNUMPAGES) {
return "第1页"; // 普通数字单页
}
if (text.matches("[IVXLCDM]+.*")) {
return "I, II, III ..."; // 罗马数字
}
if (text.matches("[A-Z]+\\d*.*")) {
return "A, B, C ..."; // 大写字母
}
if (text.matches("[a-z]+\\d*.*")) {
return "a, b, c ..."; // 小写字母
}
}
}
return "";
return "无页码";
}
@@ -1203,9 +1305,6 @@ public class SectionPage {
// public static void readTextWatermark(
// P paragraph,
// HeaderFooterPolicy hfp,
@@ -1587,7 +1686,7 @@ public class SectionPage {
}
// 横向匹配
if (Math.abs(width - sh) <= tol && Math.abs(height - sw) <= tol) {
return entry.getKey() + " (横向)";
return entry.getKey();
}
}
@@ -1730,7 +1829,7 @@ public class SectionPage {
// 以 Top 为例获取颜色
CTBorder top = borders.getTop();
if (top != null && top.getColor() != null) {
return ColorUtils.getChineseColorName(top.getColor());
return com.example.exam.exam.service.wpsword.docx4j.utils.ColorUtils.getChineseColorName(top.getColor());
}
}
return "";