From 2ea56ab5c8ef96e6600a0cdb3656b06444bdffed Mon Sep 17 00:00:00 2001 From: huababa1 <2037205722@qq.com> Date: Mon, 20 Oct 2025 06:19:16 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E4=BF=AE=E6=94=B9=E3=80=91=E5=AE=8C?= =?UTF-8?q?=E5=96=84wps=E6=96=87=E5=AD=97=E8=A1=A8=E6=A0=BC=E8=80=83?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exam/controller/info/InfoController.java | 3 + .../JudgementWpsExcelServiceImpl.java | 4 + .../exam/service/wpsexcel/XlsxMaster.java | 3 +- .../exam/service/wpsexcel/cell/CellIng.java | 84 +++++--- .../service/wpsexcel/vo/WpsXlsxInfoVo.java | 2 + .../service/wpsword/docx4j/DocxMaster.java | 57 +++-- .../wpsword/docx4j/drawing/Drawing.java | 19 +- .../wpsword/docx4j/drawing/DrawingInline.java | 9 +- .../wpsword/docx4j/paragraph/Convert.java | 16 +- .../wpsword/docx4j/paragraph/Paragraphs.java | 202 ++++++++++++++---- .../wpsword/docx4j/section/SectionPage.java | 153 ++++++++++--- 11 files changed, 415 insertions(+), 137 deletions(-) diff --git a/src/main/java/com/example/exam/exam/controller/info/InfoController.java b/src/main/java/com/example/exam/exam/controller/info/InfoController.java index 17ec3ad..ba54259 100644 --- a/src/main/java/com/example/exam/exam/controller/info/InfoController.java +++ b/src/main/java/com/example/exam/exam/controller/info/InfoController.java @@ -74,4 +74,7 @@ public class InfoController { public Result> getButtonInfo(@RequestBody StuInfoVo stuInfoVo) throws IOException { return Result.success(autoForButtonService.getButtonInfo(stuInfoVo)); } + + + } diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java index 41ceef1..e481e07 100644 --- a/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java @@ -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); } diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxMaster.java b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxMaster.java index 4388753..47bd085 100644 --- a/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxMaster.java +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/XlsxMaster.java @@ -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)){ diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/cell/CellIng.java b/src/main/java/com/example/exam/exam/service/wpsexcel/cell/CellIng.java index 3f75caa..3b7af53 100644 --- a/src/main/java/com/example/exam/exam/service/wpsexcel/cell/CellIng.java +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/cell/CellIng.java @@ -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/字符宽,可根据字体调整 diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/vo/WpsXlsxInfoVo.java b/src/main/java/com/example/exam/exam/service/wpsexcel/vo/WpsXlsxInfoVo.java index 2507496..d37df5b 100644 --- a/src/main/java/com/example/exam/exam/service/wpsexcel/vo/WpsXlsxInfoVo.java +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/vo/WpsXlsxInfoVo.java @@ -27,4 +27,6 @@ public class WpsXlsxInfoVo { // 方式方法 private String method; + + private String keyWords; } diff --git a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/DocxMaster.java b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/DocxMaster.java index 5cb9910..c4bc9ed 100644 --- a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/DocxMaster.java +++ b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/DocxMaster.java @@ -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, diff --git a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/Drawing.java b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/Drawing.java index 001b2e5..4099032 100644 --- a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/Drawing.java +++ b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/Drawing.java @@ -77,7 +77,7 @@ public class Drawing { String width = String.format("%.2fcm", widthCm); return width; } - + // //布局 锁定横纵比 public static String getLayoutLock(List 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 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 judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { @@ -171,7 +180,15 @@ public class Drawing { } return "未知"; } + // 位置 → 垂直 → 绝对位置 + public static String getLayoutVerticalAbsolute(List 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 judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { if (anchor == null || anchor.getPositionV() == null) return "未知"; diff --git a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/DrawingInline.java b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/DrawingInline.java index 981eb00..e0ab025 100644 --- a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/DrawingInline.java +++ b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/drawing/DrawingInline.java @@ -165,7 +165,14 @@ public class DrawingInline { return "未知"; } } - + // 位置 → 水平 → 绝对位置 + public static String getLayoutHorizontalAbsolute(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + return "无"; + } + // 位置 → 垂直 → 绝对位置 + public static String getLayoutVerticalAbsolute(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + return "无"; + } // 位置 → 水平 → 相对于 public static String getLayoutHorizontalRelativeTo(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { if (anchor == null) return "未知"; diff --git a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Convert.java b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Convert.java index 1caa317..f6c440c 100644 --- a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Convert.java +++ b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Convert.java @@ -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 "正文"; } } diff --git a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Paragraphs.java b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Paragraphs.java index 18b8c8f..1f2baf9 100644 --- a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Paragraphs.java +++ b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/paragraph/Paragraphs.java @@ -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 twip(10 磅) + 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 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()); diff --git a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/section/SectionPage.java b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/section/SectionPage.java index a284bdd..d54860e 100644 --- a/src/main/java/com/example/exam/exam/service/wpsword/docx4j/section/SectionPage.java +++ b/src/main/java/com/example/exam/exam/service/wpsword/docx4j/section/SectionPage.java @@ -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 sections) { + List 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 chineseNumbers = Set.of("一","二","三","四","五","六","七","八","九","十"); + Set 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(".*[0-9]+\\s*PAGE.*[0-9]+\\s*PAGE.*")) { + return "1,2,3 ..."; + } + 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 "无";