diff --git a/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/dal/mysql/paper/EducationPaperTaskMapper.java b/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/dal/mysql/paper/EducationPaperTaskMapper.java index d4b13a13..e1e6a57d 100644 --- a/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/dal/mysql/paper/EducationPaperTaskMapper.java +++ b/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/dal/mysql/paper/EducationPaperTaskMapper.java @@ -158,4 +158,8 @@ public interface EducationPaperTaskMapper extends BaseMapperX selectPaperCountBypaperId(@Param("paperId") String paperId); + List selectUserRoleId(Long loginUserId); + + List selectUserRole(List roleIds); + } diff --git a/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/service/paper/EducationPaperTaskServiceImpl.java b/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/service/paper/EducationPaperTaskServiceImpl.java index b0163733..d320f9f6 100644 --- a/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/service/paper/EducationPaperTaskServiceImpl.java +++ b/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/service/paper/EducationPaperTaskServiceImpl.java @@ -201,7 +201,25 @@ public class EducationPaperTaskServiceImpl implements IEducationPaperTaskService if (taskIds == null || taskIds.length == 0) { return "请选择删除数据!"; } - + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + ListroleIds= educationPaperTaskMapper.selectUserRoleId(loginUserId); + if (roleIds!=null&&roleIds.size()>0){ + List userRole= educationPaperTaskMapper.selectUserRole(roleIds); + if (userRole!=null&&userRole.size()>0) { + boolean flag = false; + for (String s : userRole) { + if ("超级管理员".equals(s) || "租户管理员".equals(s)) { + flag = true; + } + } + if (flag) { + // 3. 执行删除操作 + educationPaperTaskMapper.deleteEducationPaperTaskByTaskIds(taskIds); + monitorMapper.deleteByTaskIds(taskIds); + return "删除成功"; + } + } + } // 1. 找出没有关联试卷的任务ID List canDeleteTaskIds = new ArrayList<>(); List cannotDeleteTaskIds = new ArrayList<>(); @@ -250,6 +268,25 @@ public class EducationPaperTaskServiceImpl implements IEducationPaperTaskService public String deleteEducationPaperTaskByTaskId(String taskId) { EducationPaperTask educationPaperTask = educationPaperTaskMapper.selectById(taskId); if (educationPaperTask.getTaskType().equals("0") || educationPaperTask.getTaskType().equals("1") || educationPaperTask.getTaskType().equals("3")) { + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + ListroleIds= educationPaperTaskMapper.selectUserRoleId(loginUserId); + if (roleIds!=null&&roleIds.size()>0){ + List userRole= educationPaperTaskMapper.selectUserRole(roleIds); + if (userRole!=null&&userRole.size()>0) { + boolean flag = false; + for (String s : userRole) { + if ("超级管理员".equals(s) || "租户管理员".equals(s)) { + flag = true; + } + } + if (flag) { + // 3. 执行删除操作 + educationPaperTaskMapper.deleteEducationPaperTaskByTaskId(taskId); + monitorMapper.deleteByTaskId(taskId); + return "删除成功"; + } + } + } // 1. 找出没有关联试卷的任务ID List canDeleteTaskIds = new ArrayList<>(); List cannotDeleteTaskIds = new ArrayList<>(); diff --git a/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperPersonMapper.xml b/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperPersonMapper.xml index aa16438c..4d45b7c5 100644 --- a/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperPersonMapper.xml +++ b/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperPersonMapper.xml @@ -43,6 +43,7 @@ LEFT JOIN education_paper_person pp ON s.id = pp.person_id WHERE s.user_type = #{userType} + and s.deleted = 0 AND s.id IN ( SELECT person_id FROM education_paper_person WHERE session_id ='' and @@ -83,6 +84,7 @@ LEFT JOIN education_paper_person pp ON s.id = pp.person_id WHERE s.user_type = #{userType} + and s.deleted = 0 AND s.id NOT IN ( SELECT person_id FROM education_paper_person WHERE task_id =#{taskId} ) @@ -138,6 +140,7 @@ LEFT JOIN education_paper_person pp ON s.id = pp.person_id WHERE s.user_type = #{userType} + and s.deleted = 0 AND s.id IN ( SELECT person_id FROM education_paper_person WHERE session_id ='' and @@ -164,6 +167,7 @@ LEFT JOIN education_paper_person pp ON s.id = pp.person_id WHERE s.user_type = #{userType} + and s.deleted = 0 AND s.id NOT IN ( SELECT person_id FROM education_paper_person WHERE task_id =#{taskId} ) diff --git a/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperQuMapper.xml b/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperQuMapper.xml index 0ae24d7b..1b2a27dd 100644 --- a/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperQuMapper.xml +++ b/exam-module-exam/exam-module-exam-biz/src/main/resources/mapper/exam/EducationPaperQuMapper.xml @@ -40,12 +40,17 @@ LIMIT 1; + + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxMaster.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxMaster.java index 7c640a35..b33ee79e 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxMaster.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxMaster.java @@ -208,7 +208,7 @@ public class XlsxMaster { if (value != null) { judgementXlsxVOS = setJudgementXlsx( judgementXlsxVOS, - "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + docxFunction + "@" + value, "【" + sheetName + "】" + firstName + examName + value ); } @@ -240,7 +240,7 @@ public class XlsxMaster { if (value != null) { judgementXlsxVOS = setJudgementXlsx( judgementXlsxVOS, - "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + docxFunction + "@" + value, "【" + sheetName + "】" + firstName + examName + value ); } @@ -263,7 +263,7 @@ public class XlsxMaster { if (value != null) { judgementXlsxVOS = setJudgementXlsx( judgementXlsxVOS, - "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + docxFunction + "@" + value, "【" + sheetName + "】" + firstName + examName + value ); } @@ -305,7 +305,7 @@ public class XlsxMaster { if (value != null) { judgementXlsxVOS = setJudgementXlsx( judgementXlsxVOS, - "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + docxFunction + "@" + value, "【" + sheetName + "】" + firstName + examName + value ); } @@ -351,7 +351,7 @@ public class XlsxMaster { if (value != null) { judgementXlsxVOS = setJudgementXlsx( judgementXlsxVOS, - "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + docxFunction + "@" + value, "【" + sheetName + "】" + firstName + examName + value ); } @@ -375,7 +375,7 @@ public class XlsxMaster { if (value != null) { judgementXlsxVOS = setJudgementXlsx( judgementXlsxVOS, - "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + docxFunction + "@" + value, "【" + sheetName + "】" + firstName + examName + value ); } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/cell/CellIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/cell/CellIng.java index 9f26ef41..0d0f6d60 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/cell/CellIng.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/cell/CellIng.java @@ -1,15 +1,21 @@ package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.cell; +import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFFont; import org.apache.poi.hssf.usermodel.HSSFPalette; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.*; -import org.apache.poi.xssf.usermodel.XSSFCellStyle; -import org.apache.poi.xssf.usermodel.XSSFColor; -import org.apache.poi.xssf.usermodel.XSSFFont; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorderPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STBorderStyle; import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; +import java.lang.reflect.Method; + public class CellIng { // 获取左框线样式 @@ -23,7 +29,8 @@ public class CellIng { CellStyle style = cell.getCellStyle(); if (style instanceof XSSFCellStyle) { XSSFCellStyle xssfStyle = (XSSFCellStyle) style; - return getColorNameOrRGB(xssfStyle.getLeftBorderXSSFColor()); + + return ColorUtils.getChineseColorName(getColorNameOrRGB(xssfStyle.getLeftBorderXSSFColor())); } return "无"; } @@ -40,7 +47,7 @@ public class CellIng { CellStyle style = cell.getCellStyle(); if (style instanceof XSSFCellStyle) { XSSFCellStyle xssfStyle = (XSSFCellStyle) style; - return getColorNameOrRGB(xssfStyle.getTopBorderXSSFColor()); + return ColorUtils.getChineseColorName(getColorNameOrRGB(xssfStyle.getTopBorderXSSFColor())); } return "无"; } @@ -57,7 +64,7 @@ public class CellIng { CellStyle style = cell.getCellStyle(); if (style instanceof XSSFCellStyle) { XSSFCellStyle xssfStyle = (XSSFCellStyle) style; - return getColorNameOrRGB(xssfStyle.getRightBorderXSSFColor()); + return ColorUtils.getChineseColorName(getColorNameOrRGB(xssfStyle.getRightBorderXSSFColor())); } return "无"; } @@ -74,7 +81,7 @@ public class CellIng { CellStyle style = cell.getCellStyle(); if (style instanceof XSSFCellStyle) { XSSFCellStyle xssfStyle = (XSSFCellStyle) style; - return getColorNameOrRGB(xssfStyle.getBottomBorderXSSFColor()); + return ColorUtils.getChineseColorName(getColorNameOrRGB(xssfStyle.getBottomBorderXSSFColor())); } return "无"; } @@ -217,6 +224,178 @@ public class CellIng { return "无"; } + // 删除线 + public static String getCellStrikeThrough(Cell cell, Workbook wb) { + if (cell == null) return "否"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "否"; + return style.getFontIndexAsInt() >= 0 && wb.getFontAt(style.getFontIndexAsInt()).getStrikeout() + ? "是" : "否"; + } + + // 上标 + public static String getCellSuperScript(Cell cell, Workbook wb) { + if (cell == null) return "否"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "否"; + short typeOffset = wb.getFontAt(style.getFontIndexAsInt()).getTypeOffset(); + return typeOffset == Font.SS_SUPER ? "是" : "否"; + } + + // 下标 + public static String getCellSubScript(Cell cell, Workbook wb) { + if (cell == null) return "否"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "否"; + short typeOffset = wb.getFontAt(style.getFontIndexAsInt()).getTypeOffset(); + return typeOffset == Font.SS_SUB ? "是" : "否"; + } + + + + // 获取斜下框线样式 + public static String getDiagonalDownBorderStyle(Cell cell, Workbook wb) { + if (cell == null || !(cell instanceof XSSFCell)) return "无"; + XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle(); + + try { + // 通过反射获取私有方法 getCTBorder + Method method = XSSFCellStyle.class.getDeclaredMethod("getCTBorder"); + method.setAccessible(true); + CTBorder ctBorder = (CTBorder) method.invoke(style); + + if (ctBorder != null && ctBorder.isSetDiagonal()) { + // 判断斜线方向是否向下 + if (ctBorder.getDiagonalDown()) { + CTBorderPr borderPr = ctBorder.getDiagonal(); // 斜线的边框属性 + if (borderPr != null && borderPr.getStyle() != null) { + return borderPr.getStyle().toString(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "无"; + } + + // 斜下框线→颜色 + public static String getDiagonalDownBorderColor(Cell cell, Workbook wb) { + if (cell == null || !(cell instanceof XSSFCell)) return "无"; + XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle(); + + try { + Method method = XSSFCellStyle.class.getDeclaredMethod("getCTBorder"); + method.setAccessible(true); + CTBorder ctBorder = (CTBorder) method.invoke(style); + + if (ctBorder != null && ctBorder.isSetDiagonal() && ctBorder.getDiagonalDown()) { + CTBorderPr borderPr = ctBorder.getDiagonal(); + if (borderPr != null && borderPr.isSetColor()) { + String color = borderPr.getColor().getRgb().toString(); // 返回 RGB 字符串 + return color != null ? ColorUtils.getChineseColorName(color) : "无"; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "无"; + } + + // 斜上框线→样式 + public static String getDiagonalUpBorderStyle(Cell cell, Workbook wb) { + if (cell == null || !(cell instanceof XSSFCell)) return "无"; + XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle(); + + try { + Method method = XSSFCellStyle.class.getDeclaredMethod("getCTBorder"); + method.setAccessible(true); + CTBorder ctBorder = (CTBorder) method.invoke(style); + + if (ctBorder != null && ctBorder.isSetDiagonal() && ctBorder.getDiagonalUp()) { + CTBorderPr borderPr = ctBorder.getDiagonal(); + if (borderPr != null && borderPr.getStyle() != null) { + return borderPr.getStyle().toString(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "无"; + } + + // 斜上框线→颜色 + public static String getDiagonalUpBorderColor(Cell cell, Workbook wb) { + if (cell == null || !(cell instanceof XSSFCell)) return "无"; + XSSFCellStyle style = (XSSFCellStyle) cell.getCellStyle(); + + try { + Method method = XSSFCellStyle.class.getDeclaredMethod("getCTBorder"); + method.setAccessible(true); + CTBorder ctBorder = (CTBorder) method.invoke(style); + + if (ctBorder != null && ctBorder.isSetDiagonal() && ctBorder.getDiagonalUp()) { + CTBorderPr borderPr = ctBorder.getDiagonal(); + if (borderPr != null && borderPr.isSetColor()) { + String color = borderPr.getColor().getRgb().toString(); + return color != null ? ColorUtils.getChineseColorName(color) : "无"; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "无"; + } + + + + + + + + + + + + + + + + + // ===== 获取单元格文本(String) ===== + public String getCellText(Cell cell, Workbook wb) { + if (cell == null) return "无"; + DataFormatter formatter = new DataFormatter(); + return formatter.formatCellValue(cell); + } + + // ===== 获取单元格值 + public String getCellValue(Cell cell, Workbook wb) { + if (cell == null) return ""; + + switch (cell.getCellType()) { + case STRING: + return cell.getStringCellValue(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue().toString(); + } else { + return String.valueOf((long) cell.getNumericCellValue()); + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case FORMULA: + return cell.getCellFormula(); // 返回公式原文,例如 "A1+B1" + case BLANK: + case _NONE: + case ERROR: + default: + return ""; + } + } + + + @@ -238,4 +417,121 @@ public class CellIng { } return String.format("%02X%02X%02X", rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF); } + + // 数字格式 + public static String getCellDataFormat(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + return style.getDataFormatString(); + } + + // 水平对齐 + public static String getCellHorizontalAlignment(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + HorizontalAlignment alignment = style.getAlignment(); + return alignment != null ? alignment.name() : "无"; + } + + // 垂直对齐 + public static String getCellVerticalAlignment(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + VerticalAlignment alignment = style.getVerticalAlignment(); + return alignment != null ? alignment.name() : "无"; + } + + // 缩进 + public static String getCellIndent(Cell cell, Workbook wb) { + if (cell == null) return "0"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "0"; + return String.valueOf(style.getIndention()); + } + + // 自动换行 + public static String getCellWrapText(Cell cell, Workbook wb) { + if (cell == null) return "否"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "否"; + return style.getWrapText() ? "是" : "否"; + } + + // 缩小字体填充(ShrinkToFit) + public static String getCellShrinkToFit(Cell cell, Workbook wb) { + if (cell == null) return "否"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "否"; + return style.getShrinkToFit() ? "是" : "否"; + } + + // 合并单元格 + public static String getCellMerged(Cell cell, Workbook wb) { + if (cell == null || wb == null) return "否"; + Sheet sheet = cell.getSheet(); + if (sheet == null) return "否"; + + int row = cell.getRowIndex(); + int col = cell.getColumnIndex(); + + for (int i = 0; i < sheet.getNumMergedRegions(); i++) { + CellRangeAddress range = sheet.getMergedRegion(i); + if (range.isInRange(row, col)) { + return "是"; + } + } + return "否"; + } + + + // 文本方向(rotation) + public static String getCellTextRotation(Cell cell, Workbook wb) { + if (cell == null) return "0"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "0"; + return String.valueOf(style.getRotation()); // 旋转角度 + } + + // 文本样式(加粗/斜体/下划线等) + public static String getCellFontStyle(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + + Font font; + if (wb instanceof XSSFWorkbook) { + font = wb.getFontAt(style.getFontIndexAsInt()); + } else if (wb instanceof HSSFWorkbook) { + font = wb.getFontAt(style.getFontIndexAsInt()); + } else { + return "无"; + } + + StringBuilder sb = new StringBuilder(); + if (font.getBold()) sb.append("加粗 "); + if (font.getItalic()) sb.append("斜体 "); + if (font.getUnderline() != Font.U_NONE) sb.append("下划线 "); + if (sb.length() == 0) sb.append("常规"); + return sb.toString().trim(); + } + + // 高度(行高) + public static String getCellRowHeight(Cell cell, Workbook wb) { + if (cell == null || cell.getSheet() == null) return "默认"; + Row row = cell.getRow(); + if (row == null) return "默认"; + return String.valueOf(row.getHeight() / 20.0) + " pt"; // row.getHeight() 单位为 twips (1/20 pt) + } + + // 宽度(列宽) + public static String getCellColumnWidth(Cell cell, Workbook wb) { + 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/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java index 83cb212c..c5c3bed1 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java @@ -45,23 +45,93 @@ public class ChartHandler { if (chart == null) return "无图表"; CTChart ctChart = chart.getCTChart(); - if (ctChart == null) return "无图表定义"; + if (ctChart == null || ctChart.getPlotArea() == null) return "无图表定义"; - // 这里尝试获取第一种图表类型 - if (ctChart.getPlotArea() != null) { - if (ctChart.getPlotArea().getBarChartList().size() > 0) return "条形图"; - if (ctChart.getPlotArea().getLineChartList().size() > 0) return "折线图"; - if (ctChart.getPlotArea().getPieChartList().size() > 0) return "饼图"; - if (ctChart.getPlotArea().getAreaChartList().size() > 0) return "面积图"; - if (ctChart.getPlotArea().getScatterChartList().size() > 0) return "散点图"; - // 可根据需求继续扩展 + CTPlotArea plotArea = ctChart.getPlotArea(); + + // ===== 柱形 / 条形图 ===== + if (!plotArea.getBarChartList().isEmpty()) { + CTBarChart barChart = plotArea.getBarChartArray(0); + STBarDir.Enum dir = barChart.getBarDir().getVal(); + STBarGrouping.Enum grouping = barChart.getGrouping().getVal(); + + String baseName = (dir == STBarDir.COL) ? "柱形图" : + (dir == STBarDir.BAR) ? "条形图" : "柱形/条形图"; + + if (grouping == STBarGrouping.CLUSTERED) { + return "簇状" + baseName; + } else if (grouping == STBarGrouping.STACKED) { + return "堆积" + baseName; + } else if (grouping == STBarGrouping.PERCENT_STACKED) { + return "百分比堆积" + baseName; + } else { + return baseName; + } } + if (!plotArea.getBar3DChartList().isEmpty()) { + return "三维柱形/条形图"; + } + + // ===== 折线图 ===== + if (!plotArea.getLineChartList().isEmpty()) { + return "折线图"; + } + if (!plotArea.getLine3DChartList().isEmpty()) { + return "三维折线图"; + } + + // ===== 饼图 ===== + if (!plotArea.getPieChartList().isEmpty()) { + return "饼图"; + } + if (!plotArea.getPie3DChartList().isEmpty()) { + return "三维饼图"; + } + + // ===== 面积图 ===== + if (!plotArea.getAreaChartList().isEmpty()) { + return "面积图"; + } + if (!plotArea.getArea3DChartList().isEmpty()) { + return "三维面积图"; + } + + // ===== 散点图 ===== + if (!plotArea.getScatterChartList().isEmpty()) { + return "散点图"; + } + + // ===== 雷达图 ===== + if (!plotArea.getRadarChartList().isEmpty()) { + return "雷达图"; + } + + // ===== 气泡图 ===== + if (!plotArea.getBubbleChartList().isEmpty()) { + return "气泡图"; + } + + // ===== 组合图(多个 chart 类型) ===== + int chartTypeCount = 0; + if (!plotArea.getBarChartList().isEmpty() || !plotArea.getBar3DChartList().isEmpty()) chartTypeCount++; + if (!plotArea.getLineChartList().isEmpty() || !plotArea.getLine3DChartList().isEmpty()) chartTypeCount++; + if (!plotArea.getPieChartList().isEmpty() || !plotArea.getPie3DChartList().isEmpty()) chartTypeCount++; + if (!plotArea.getAreaChartList().isEmpty() || !plotArea.getArea3DChartList().isEmpty()) chartTypeCount++; + if (!plotArea.getScatterChartList().isEmpty()) chartTypeCount++; + if (!plotArea.getRadarChartList().isEmpty()) chartTypeCount++; + if (!plotArea.getBubbleChartList().isEmpty()) chartTypeCount++; + + if (chartTypeCount > 1) return "组合图"; + return "未知图表类型"; } + + + /** 图表-数据-数据系列 - 产生方式 */ public String getDataSeriesGeneration(XSSFChart chart, XSSFSheet sheet) { if (chart == null) return "无图表"; diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/range/RangIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/range/RangIng.java index fd63b8e0..bef109f3 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/range/RangIng.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/range/RangIng.java @@ -7,6 +7,7 @@ import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.*; +import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; import java.math.BigDecimal; import java.util.ArrayList; @@ -177,36 +178,55 @@ public class RangIng { CellStyle style = cell.getCellStyle(); short dataFormatIndex = style.getDataFormat(); String dataFormatString = style.getDataFormatString(); - System.out.println("数字格式索引: " + dataFormatIndex + ", 格式: " + dataFormatString); - return dataFormatString; + if(dataFormatString!=null){ + return dataFormatString; + } + return "无"; + } - //格式-合并单元格 (返回是否) +// 只有当 cellRefs 所有单元格都在同一个合并区域里时,才返回 "是" public String isMergedRegion(String cellRef, Workbook workbook, int sheetIndex) { List cellRefs = getCellRefsInRange(cellRef); - String cellref = cellRefs.get(0); - CellReference cr = new CellReference(cellref); - int rowIndex = cr.getRow(); - int colIndex = cr.getCol(); + if (cellRefs == null || cellRefs.isEmpty()) return "否"; Sheet sheet = workbook.getSheetAt(sheetIndex - 1); if (sheet == null) return "否"; int mergedRegionsCount = sheet.getNumMergedRegions(); + if (mergedRegionsCount == 0) return "否"; + + // 第一个单元格所在的合并区域 + CellReference firstCr = new CellReference(cellRefs.get(0)); + CellRangeAddress targetRange = null; for (int i = 0; i < mergedRegionsCount; i++) { CellRangeAddress range = sheet.getMergedRegion(i); - if (range.isInRange(rowIndex, colIndex)) { - return "是"; + if (range.isInRange(firstCr.getRow(), firstCr.getCol())) { + targetRange = range; + break; } } - return "否"; + + // 如果第一个单元格不在任何合并区域,直接否 + if (targetRange == null) return "否"; + + // 检查所有 cellRefs 是否都在同一个合并区域内 + for (String ref : cellRefs) { + CellReference cr = new CellReference(ref); + if (!targetRange.isInRange(cr.getRow(), cr.getCol())) { + return "否"; + } + } + + return "是"; } + //条件格式 //格式 //填充颜色 @@ -234,19 +254,23 @@ public class RangIng { // ===== 填充颜色 ===== if (rule.getPatternFormatting() != null) { XSSFPatternFormatting pf = rule.getPatternFormatting(); - Color bgColor = pf.getFillBackgroundColorColor(); - XSSFColor xssfColor = (XSSFColor) bgColor; - byte[] argb = xssfColor.getARGB(); - if (argb != null) { - String hex = String.format("%02X%02X%02X", argb[1], argb[2], argb[3]); - return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(hex); - } else { - return "无"; + XSSFColor xssfColor = pf.getFillBackgroundColorColor(); + if (xssfColor != null) { + byte[] argb = xssfColor.getARGB(); + if (argb != null && argb.length == 4) { + String hex = String.format("%02X%02X%02X", argb[1], argb[2], argb[3]); + String chineseColorName = ColorUtils.getChineseColorName(hex); + // 转成十进制 RGB + int r = argb[1] & 0xFF; + int g = argb[2] & 0xFF; + int b = argb[3] & 0xFF; + String rgb = String.format(chineseColorName+",RGB(%d,%d,%d)", r, g, b); + return rgb; + } } - - - + return "无"; } + } } } @@ -277,18 +301,22 @@ public class RangIng { // ===== 字体颜色 ===== if (rule.getFontFormatting() != null) { XSSFFontFormatting ff = rule.getFontFormatting(); - Color fontColor = ff.getFontColor(); - XSSFColor xssfColor = (XSSFColor) fontColor; - byte[] argb = xssfColor.getARGB(); - if (argb != null) { - String hex = String.format("%02X%02X%02X", argb[1], argb[2], argb[3]); - return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(hex); - } else { - return "无"; + XSSFColor xssfColor = ff.getFontColor(); + if (xssfColor != null) { + byte[] argb = xssfColor.getARGB(); + if (argb != null && argb.length == 4) { + String hex = String.format("%02X%02X%02X", argb[1], argb[2], argb[3]); + String chineseColorName = ColorUtils.getChineseColorName(hex); + int r = argb[1] & 0xFF; + int g = argb[2] & 0xFF; + int b = argb[3] & 0xFF; + String rgb = String.format(chineseColorName+",RGB(%d,%d,%d)", r, g, b); + return rgb; + } } - - + return "无"; } + } } } @@ -319,7 +347,16 @@ public class RangIng { System.out.println("下边框颜色: " + (borderColorBottom != null ? borderColorBottom.getARGBHex() : "无")); System.out.println("左边框颜色: " + (borderColorLeft != null ? borderColorLeft.getARGBHex() : "无")); System.out.println("右边框颜色: " + (borderColorRight != null ? borderColorRight.getARGBHex() : "无")); - return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(borderColorTop.getARGBHex() ); + if (borderColorTop != null && borderColorTop.getARGBHex() != null) { + String hex=borderColorTop.getARGBHex(); + if (hex != null && hex.length() == 8) { + hex = hex.substring(2); // 去掉前两位 Alpha + } + if (hex != null) { + return ColorUtils.getChineseColorName(hex); + } + } + return "无"; } else { System.out.println("边框颜色索引(旧版HSSF): top=" + style.getTopBorderColor() @@ -405,7 +442,13 @@ public class RangIng { XSSFCellStyle xssfStyle = (XSSFCellStyle) style; XSSFColor fillColor = xssfStyle.getFillForegroundXSSFColor(); if (fillColor != null && fillColor.getARGBHex() != null) { - return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(fillColor.getARGBHex() ); + String hex=fillColor.getARGBHex(); + if (hex != null && hex.length() == 8) { + hex = hex.substring(2); // 去掉前两位 Alpha + } + if (hex != null) { + return ColorUtils.getChineseColorName(hex); + } } } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/style/Style.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/style/Style.java index 4eaa8427..04a9b497 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/style/Style.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/style/Style.java @@ -9,6 +9,7 @@ import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphical import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageBreak; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetProtection; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; +import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; public class Style { @@ -33,7 +34,13 @@ public class Style { Sheet sheet = workbook.getSheetAt(sheetNum); if (sheet instanceof XSSFSheet) { XSSFColor color = ((XSSFSheet) sheet).getTabColor(); - if (color != null) return color.getARGBHex(); + String hex=color.getARGBHex(); + if (hex != null && hex.length() == 8) { + hex = hex.substring(2); // 去掉前两位 Alpha + } + if (hex != null) { + return ColorUtils.getChineseColorName(hex); + } } return "无"; } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxConversion.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxConversion.java index 37bda365..c786014d 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxConversion.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxConversion.java @@ -65,7 +65,7 @@ public class DocxConversion { String shortText = shorten(fullText); // 截断展示 setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + shortText, "w:p", String.valueOf(wpIndex), true, dataInfoVOS); - String[] sentences = fullText.split("。"); + String[] sentences = fullText.split("[。!?.!?]"); int sentenceIndex = 0; for (String sentence : sentences) { sentence = sentence.trim(); diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxMaster.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxMaster.java index 92f48b4b..0beceb58 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxMaster.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/DocxMaster.java @@ -188,8 +188,8 @@ public class DocxMaster { } } if (firstName.contains("句子")) { - // 修改的正则,支持非贪婪匹配到】结束,包括中间包含换行、点号等 - Pattern pattern = Pattern.compile("【(句子\\d+):(.+?)】"); + // 支持非贪婪匹配到】结束,包括中间包含换行、点号等 + Pattern pattern = Pattern.compile("【(句子\\d+):(.+?)】", Pattern.DOTALL); Matcher matcher = pattern.matcher(firstName); if (!matcher.find()) continue; @@ -201,57 +201,105 @@ public class DocxMaster { for (Object obj : paragraphs) { paragraphIndex++; Object realObj = XmlUtils.unwrap(obj); + if (!(realObj instanceof P)) continue; + P paragraph = (P) realObj; - if (realObj instanceof P) { - P paragraph = (P) realObj; + // 构建整段文本并记录每个 Run 的位置 + List runList = new ArrayList<>(); + List runPositions = new ArrayList<>(); + StringBuilder paragraphTextBuilder = new StringBuilder(); - List paragraphContent = paragraph.getContent(); + int posCounter = 0; + for (Object part : paragraph.getContent()) { + Object unwrappedPart = XmlUtils.unwrap(part); - for (Object part : paragraphContent) { - Object contentObj = XmlUtils.unwrap(part); - if (contentObj instanceof R) { + if (unwrappedPart instanceof R) { + R run = (R) unwrappedPart; + runList.add(run); - if (paragraphIndex == Integer.parseInt(indexParm)) { - R run = (R) contentObj; - - // 提取 run 的纯文本 - StringBuilder sb = new StringBuilder(); - for (Object rc : run.getContent()) { - Object realRc = XmlUtils.unwrap(rc); - if (realRc instanceof Text) { - sb.append(((Text) realRc).getValue()); - } - } - - String actualText = sb.toString().trim(); - - // 模糊匹配:实际文本包含期望文本,或期望文本包含实际文本 - if (isFuzzyMatch(expectedText, actualText)) { - try { - Class runTextClass = RunText.class; - Method method = runTextClass.getMethod(function, R.class, String.class); - String value = (String) method.invoke(null, run, actualText); - - if (value!=null){ - // 写入结果 - judgementWordsVOS = setJudgementWord( - judgementWordsVOS, - docxFunction + "@" + value, - firstName + examName + value - ); - } - } catch (Exception e) { - e.printStackTrace(); - } - } + String runText = getRunText(run); + paragraphTextBuilder.append(runText); + runPositions.add(new int[]{posCounter, posCounter + runText.length()}); + posCounter += runText.length(); + } else if (unwrappedPart instanceof P.Hyperlink) { + P.Hyperlink link = (P.Hyperlink) unwrappedPart; + for (Object linkPart : link.getContent()) { + Object unwrappedLinkPart = XmlUtils.unwrap(linkPart); + if (unwrappedLinkPart instanceof R) { + R run = (R) unwrappedLinkPart; + runList.add(run); + String runText = getRunText(run); // 包含超链接文本 + paragraphTextBuilder.append(runText); + runPositions.add(new int[]{posCounter, posCounter + runText.length()}); + posCounter += runText.length(); } } } } - } + String fullText = paragraphTextBuilder.toString(); + if (fullText.trim().isEmpty()) continue; + + // 用正则匹配句子(保留句子分隔符) + Pattern sentencePattern = Pattern.compile("[^。!?.!?]+[。!?.!?]?"); + Matcher sentenceMatcher = sentencePattern.matcher(fullText); + + while (sentenceMatcher.find()) { + String sentence = sentenceMatcher.group().trim(); + if (sentence.isEmpty()) continue; + + int sentenceStart = sentenceMatcher.start(); + int sentenceEnd = sentenceMatcher.end(); + + // 判断是否匹配期望文本 & 段落位置 + if (!isFuzzyMatch(expectedText, sentence) || paragraphIndex != Integer.parseInt(indexParm)) { + continue; + } + + // ⚡ 找到包含该句子的 Run 集合 + List sentenceRuns = new ArrayList<>(); + for (int a = 0; a < runList.size(); a++) { + int[] pos = runPositions.get(a); + if (pos[1] > sentenceStart && pos[0] < sentenceEnd) { + sentenceRuns.add(runList.get(a)); + } + } + + if (sentenceRuns.isEmpty()) continue; + + // ⚡ 一句话只调用一次方法 + try { + Class runTextClass = RunText.class; + Method method = runTextClass.getMethod( + function, List.class, String.class, P.class, StyleDefinitionsPart.class + ); + + // 反射调用方法,处理整个 Run 集合 + String value = (String) method.invoke(null, sentenceRuns, sentence,paragraph, stylePart); + + if (value != null) { + System.out.println("期望: " + expectedText); + System.out.println("结果: " + value + " ======== " + sentence); + + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } } + + + + + if (firstName.contains("图片")) { // 从 firstName 中提取图形编号,例如 【图形1:图片2】 Pattern pattern = Pattern.compile("【图形(\\d+):(.+?)】"); @@ -721,7 +769,7 @@ public class DocxMaster { // 提取 run 中的文本内容 - private static String getRunText(R run) { + public static String getRunText(R run) { StringBuilder sb = new StringBuilder(); for (Object content : run.getContent()) { if (content instanceof Text) { @@ -735,6 +783,11 @@ public class DocxMaster { } return sb.toString(); } + + + + + /** * 将 expectedText 中的 ... 处理为正则表达式的模糊匹配,并判断 actualText 是否匹配 */ @@ -777,5 +830,33 @@ public class DocxMaster { return false; } + /** + * 递归获取 R/Hyperlink 内文字,包括超链接里的文本 + */ + public static String getRunTextIncludingHyperlink(Object obj) { + if (obj == null) return ""; + StringBuilder sb = new StringBuilder(); + Object unwrapped = XmlUtils.unwrap(obj); + + if (unwrapped instanceof R) { + R run = (R) unwrapped; + for (Object rc : run.getContent()) { + sb.append(getRunTextIncludingHyperlink(rc)); + } + } else if (unwrapped instanceof Text) { + sb.append(((Text) unwrapped).getValue()); + } else if (unwrapped instanceof R.Tab) { + sb.append("\t"); + } else if (unwrapped instanceof Br) { + sb.append("\n"); + } else if (unwrapped instanceof P.Hyperlink) { + P.Hyperlink link = (P.Hyperlink) unwrapped; + for (Object linkPart : link.getContent()) { + sb.append(getRunTextIncludingHyperlink(linkPart)); + } + } + + return sb.toString(); + } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java index d79344a0..b39eb018 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java @@ -9,7 +9,6 @@ import org.docx4j.dml.picture.Pic; import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.dom4j.QName; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; @@ -20,6 +19,7 @@ import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; @@ -33,6 +33,8 @@ import java.lang.reflect.Method; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class Drawing { @@ -87,11 +89,19 @@ public class Drawing { //布局 环绕方式 public static String getLayoutHuanRao(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { - String layoutHuanRaoValue = anchor.getWrapSquare().getWrapText().value(); - layoutHuanRaoValue = getLayoutHuanRaoName(layoutHuanRaoValue); - return layoutHuanRaoValue; - } + if (anchor == null) return "未知"; + // 是否衬于文字下方 + boolean behindDoc = anchor.isBehindDoc(); + if (behindDoc) return "衬于文字下方型"; + // 浮于文字上方型 + if (anchor.getWrapNone() != null) return "浮于文字上方型"; + if (anchor.getWrapSquare() != null) return "四周型"; + if (anchor.getWrapTight() != null) return "紧密型"; + if (anchor.getWrapThrough() != null) return "穿越型"; + if (anchor.getWrapTopAndBottom() != null) return "上下型"; + return "无"; + } // 形状和样式 public static List getStyles(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { @@ -230,9 +240,9 @@ public class Drawing { CTLineProperties outline = pic.getSpPr().getLn(); if (outline.getSolidFill() != null) { if (outline.getSolidFill().getSrgbClr() != null) { - return outline.getSolidFill().getSrgbClr().getVal(); + return ColorUtils.getChineseColorName(outline.getSolidFill().getSrgbClr().getVal()); } else if (outline.getSolidFill().getSchemeClr() != null) { - return outline.getSolidFill().getSchemeClr().getVal().value(); + return ColorUtils.getChineseColorName(outline.getSolidFill().getSchemeClr().getVal().value()); } } } @@ -552,7 +562,9 @@ public class Drawing { String name = "【第" + betoLong + "个图形】【图片】" + huiZhiName; CTOuterShadowEffect shadow = getOuterShadow(anchor); - if (shadow == null) { + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + CTPresetShadowEffect presetShadow = getPresetShadow(anchor); + if (shadow == null&&innerShadow==null&&presetShadow==null) { return "否"; } return "是"; @@ -561,16 +573,33 @@ public class Drawing { // 获取“颜色”属性(阴影颜色) public static String getShadowColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTOuterShadowEffect shadow = getOuterShadow(anchor); - if (shadow == null) return "无"; - - if (shadow.getPrstClr() != null) { - return ColorUtils.getChineseColorName(shadow.getPrstClr().getVal().value()); + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + CTPresetShadowEffect presetShadow = getPresetShadow(anchor); + if (shadow != null) { + if (shadow.getPrstClr() != null) { + return ColorUtils.getChineseColorName(shadow.getPrstClr().getVal().value()); + } + if (shadow.getSrgbClr() != null) { + return ColorUtils.getChineseColorName(shadow.getSrgbClr().getVal()); + } } - if (shadow.getSrgbClr() != null) { - return ColorUtils.getChineseColorName(shadow.getSrgbClr().getVal()); + if (innerShadow!=null){ + if (innerShadow.getPrstClr() != null) { + return ColorUtils.getChineseColorName(shadow.getPrstClr().getVal().value()); + } + if (innerShadow.getSrgbClr() != null) { + return ColorUtils.getChineseColorName(shadow.getSrgbClr().getVal()); + } } - + if (presetShadow!=null){ + if (presetShadow.getPrstClr() != null) { + return ColorUtils.getChineseColorName(presetShadow.getPrstClr().getVal().value()); + } + if (presetShadow.getSrgbClr() != null) { + return ColorUtils.getChineseColorName(presetShadow.getSrgbClr().getVal()); + } + } return "未知"; } @@ -578,38 +607,90 @@ public class Drawing { //模糊 public static String getShadowBlur(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTOuterShadowEffect shadow = getOuterShadow(anchor); - if (shadow == null) return "无"; + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + if (shadow != null){ + // blurRad 单位是 EMUs,转成磅(1磅=12700 EMUs) + double blurPts = shadow.getBlurRad() / 12700.0; + return String.format("%.2f 磅", blurPts); + } + if (innerShadow!=null){ + // blurRad 单位是 EMUs,转成磅(1磅=12700 EMUs) + double blurPts = innerShadow.getBlurRad() / 12700.0; + return String.format("%.2f 磅", blurPts); + } + return "无"; - // blurRad 单位是 EMUs,转成磅(1磅=12700 EMUs) - double blurPts = shadow.getBlurRad() / 12700.0; - return String.format("%.2f 磅", blurPts); } //距离 public static String getShadowDistance(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTOuterShadowEffect shadow = getOuterShadow(anchor); - if (shadow == null) return "无"; + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + CTPresetShadowEffect presetShadow = getPresetShadow(anchor); + if (shadow != null) { + // dist 单位 EMUs,转成厘米 + double distCm = shadow.getDist() / 12700.0; + return String.format("%.2f 磅", distCm); + } + if (innerShadow!=null){ + // dist 单位 EMUs,转成厘米 + double distCm = innerShadow.getDist() / 12700.0; + return String.format("%.2f 磅", distCm); + } - // dist 单位 EMUs,转成厘米 - double distCm = shadow.getDist() / 12700.0; - return String.format("%.2f 磅", distCm); + if (presetShadow!=null){ + // dist 单位 EMUs,转成厘米 + double distCm = presetShadow.getDist() / 12700.0; + return String.format("%.2f 磅", distCm); + } + return "无"; } //大小 public static String getShadowScaleX(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTOuterShadowEffect shadow = getOuterShadow(anchor); - if (shadow == null) return "无"; + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + CTPresetShadowEffect presetShadow = getPresetShadow(anchor); + if (shadow != null) { + // sx 单位是 1/1000 百分比 + double scaleXPercent = shadow.getSx() / 1000.0; + return String.format("%.1f%%", scaleXPercent); + } + if (innerShadow!=null){ + // sx 单位是 1/1000 百分比 + return "无"; + } - // sx 单位是 1/1000 百分比 - double scaleXPercent = shadow.getSx() / 1000.0; - return String.format("%.1f%%", scaleXPercent); + if (presetShadow!=null){ + return "无"; + } + return "无"; } //角度 public static String getShadowDirection(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTOuterShadowEffect shadow = getOuterShadow(anchor); - if (shadow == null) return "无"; + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + CTPresetShadowEffect presetShadow = getPresetShadow(anchor); + if (shadow != null) { + // dir 单位是 1/60000 度 + double dirDegree = shadow.getDir() / 60000.0; + return String.format("%.1f°", dirDegree); + } + + + if (innerShadow!=null){ + // dir 单位是 1/60000 度 + double dirDegree = innerShadow.getDir() / 60000.0; + return String.format("%.1f°", dirDegree); + } + + if (presetShadow!=null){ + // dir 单位是 1/60000 度 + double dirDegree = presetShadow.getDir() / 60000.0; + return String.format("%.1f°", dirDegree); + } + + + return "无"; - // dir 单位是 1/60000 度 - double dirDegree = shadow.getDir() / 60000.0; - return String.format("%.1f°", dirDegree); } public static String getShadowAlpha(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { @@ -713,8 +794,80 @@ public class Drawing { // 获取“预设” public static String getShadowPresetType(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + CTInnerShadowEffect innerShadow= getInnerShadow(anchor); + CTPresetShadowEffect presetShadow = getPresetShadow(anchor); + if (shadow != null ) { - return "无预设"; + Long blurRadObj = shadow.getBlurRad(); + Long distObj = shadow.getDist(); + Long dirObj = (long) shadow.getDir(); + + long blurRad = blurRadObj != null ? blurRadObj : 0L; + long dist = distObj != null ? distObj : 0L; + long dir = dirObj != null ? dirObj : 0L; + // 转换为角度 + double angle = dir / 60000.0; + + // 1️⃣ 偏移阴影(外部) + if (angle == 45 ) return "偏移右下"; + if (angle == 90 ) return "偏移下"; + if (angle == 135) return "偏移左下"; + if (angle == 0&&dist!=0 ) return "偏移右"; + if (angle == 180 ) return "偏移左"; + if (angle ==315 ) return "偏移右上"; + if (angle == 270 ) return "偏移上"; + if (angle == 225) return "偏移左上"; + if (dist==0) return "中"; + + } + if (innerShadow!=null){ + + Long blurRadObj = innerShadow.getBlurRad(); + Long distObj = innerShadow.getDist(); + Long dirObj = (long) innerShadow.getDir(); + + long blurRad = blurRadObj != null ? blurRadObj : 0L; + long dist = distObj != null ? distObj : 0L; + long dir = dirObj != null ? dirObj : 0L; + // 转换为角度 + double angle = dir / 60000.0; + // 2️⃣ 内部阴影(dist 较小,blurRad 中等) + if (angle == 45 ) return "内部右下"; + if (angle == 90 ) return "内部下"; + if (angle == 135) return "内部左下"; + if (angle == 00&&dist!=0 ) return "内部右"; + if (angle == 180 ) return "内部左"; + if (angle ==315 ) return "内部右上"; + if (angle == 270 ) return "内部上"; + if (angle == 225) return "内部左上"; + if (dist==0) return "中"; + } + + if (presetShadow!=null){ + Long blurRadObj = shadow.getBlurRad(); + Long distObj = shadow.getDist(); + Long dirObj = (long) shadow.getDir(); + + long blurRad = blurRadObj != null ? blurRadObj : 0L; + long dist = distObj != null ? distObj : 0L; + long dir = dirObj != null ? dirObj : 0L; + // 转换为角度 + double angle = dir / 60000.0; + + + // 3️⃣ 透视阴影(dist 和 blurRad 都较大) + if (angle == 225) return "透视左上"; + if (angle ==315 ) return "透视右上"; + if (angle == 90 ) return "透视下"; + if (angle == 135) return "透视左下"; + if (angle == 45 ) return "透视右下"; + if (dist==0) return "中"; + } + + + + return "无"; } @@ -1199,6 +1352,50 @@ public static String getSolidFillColor(List judgementWordsVOS, return String.format("%.2f 磅", distPt); } + // 图片-形状效果→柔化边缘→绘制(是否) + public static String isSoftEdgeEnabled(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) + return "否"; + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object obj = (anyObj instanceof JAXBElement) ? ((JAXBElement) anyObj).getValue() : anyObj; + + try { + String xml = XmlUtils.marshaltoString(obj, true, true); + // 如果 XML 中存在 就表示启用 + if( xml.contains(" judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) + return "无柔化"; + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object obj = (anyObj instanceof JAXBElement) ? ((JAXBElement) anyObj).getValue() : anyObj; + + try { + String xml = XmlUtils.marshaltoString(obj, true, true); + Pattern pattern = Pattern.compile(""); + Matcher matcher = pattern.matcher(xml); + if (matcher.find()) { + int rad = Integer.parseInt(matcher.group(1)); + // EMU 转磅 + double sizePt = rad * 72.0 / 914400.0; + return String.format("%.2f 磅", sizePt); + } + } catch (Exception e) { + e.printStackTrace(); + } + return "无柔化"; + } + // ----- 辅助方法 ----- @@ -1385,12 +1582,41 @@ public static String getSolidFillColor(List judgementWordsVOS, CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); if (spPr != null && spPr.getEffectLst() != null && spPr.getEffectLst().getOuterShdw() != null) { + return spPr.getEffectLst().getOuterShdw(); } } return null; } + private static CTInnerShadowEffect getInnerShadow(Anchor anchor) { + if (anchor == null || anchor.getDocPr() == null) return null; + + if (anchor.getGraphic() != null && + anchor.getGraphic().getGraphicData() != null && + anchor.getGraphic().getGraphicData().getPic() != null) { + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr != null && spPr.getEffectLst() != null && spPr.getEffectLst().getInnerShdw() != null) { + return spPr.getEffectLst().getInnerShdw(); + } + } + return null; + } + private static CTPresetShadowEffect getPresetShadow(Anchor anchor) { + if (anchor == null || anchor.getGraphic() == null) return null; + + Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0); + if (graphicData instanceof JAXBElement) { + graphicData = ((JAXBElement) graphicData).getValue(); + } + + if (graphicData instanceof CTPresetShadowEffect) { + return (CTPresetShadowEffect) graphicData; + } + + return null; + } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/DrawingInline.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/DrawingInline.java index 39c4e68b..ef0e97b9 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/DrawingInline.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/DrawingInline.java @@ -203,7 +203,7 @@ public class DrawingInline { } } } - return colorValue; + return ColorUtils.getChineseColorName(colorValue); } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java index 3e6eda13..76e03c5d 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java @@ -1,11 +1,13 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph; import jakarta.xml.bind.JAXBElement; +import org.docx4j.XmlUtils; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart; import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart; import org.docx4j.wml.*; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextAlignment; +import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; import java.math.BigInteger; @@ -143,7 +145,9 @@ public class Paragraphs { public static String getParagraphSpacingBeforeLines(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSpacing() != null) { - return pPr.getSpacing().getBeforeLines().toString(); + if (pPr.getSpacing().getBeforeLines()!=null){ + return pPr.getSpacing().getBeforeLines().toString(); + } } if (pPr != null && pPr.getPStyle() != null && stylePart != null) { String styleId = pPr.getPStyle().getVal(); @@ -159,7 +163,9 @@ public class Paragraphs { public static String getParagraphSpacingBefore(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSpacing() != null) { - return pPr.getSpacing().getBefore().toString(); + if(pPr.getSpacing().getBefore()!=null){ + return pPr.getSpacing().getBefore().toString(); + } } if (pPr != null && pPr.getPStyle() != null && stylePart != null) { String styleId = pPr.getPStyle().getVal(); @@ -175,7 +181,9 @@ public class Paragraphs { public static String getParagraphSpacingAfter(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSpacing() != null) { - return pPr.getSpacing().getAfter().toString(); + if(pPr.getSpacing().getAfter()!=null){ + return pPr.getSpacing().getAfter().toString(); + } } if (pPr != null && pPr.getPStyle() != null && stylePart != null) { String styleId = pPr.getPStyle().getVal(); @@ -192,7 +200,9 @@ public class Paragraphs { public static String getParagraphSpacingAfterLines(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSpacing() != null) { - return pPr.getSpacing().getAfterLines().toString(); + if (pPr.getSpacing().getAfterLines()!=null){ + return pPr.getSpacing().getAfterLines().toString(); + } } if (pPr != null && pPr.getPStyle() != null && stylePart != null) { String styleId = pPr.getPStyle().getVal(); @@ -205,40 +215,153 @@ public class Paragraphs { return ""; } - /// 段落格式(间距) 间距 - public static String getParagraphSpacingLine(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { + // 段落行距 + // 段落行距 + public static String getParagraphSpacingLine(P paragraph, StyleDefinitionsPart stylePart, + WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { + if (paragraph == null) return "无"; + PPr pPr = paragraph.getPPr(); - if (pPr != null && pPr.getSpacing() != null) { - return pPr.getSpacing().getLine().toString(); + try { + String xml = XmlUtils.marshaltoString(stylePart.getJaxbElement(), true, true); + System.out.println(xml); + } catch (Exception e) { + e.printStackTrace(); } - 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 spacing.getLine().toString(); + // 1️⃣ 段落自身行距 + if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getLine() != null) { + int lineVal = pPr.getSpacing().getLine().intValue(); + // Word 中 line 属性值:单倍=240, 1.5倍=360, 双倍=480 + switch (lineVal) { + case 240: return "单倍"; + case 360: return "1.5 倍"; + case 480: return "双倍"; + default: return "多倍行距"; } } - return ""; + + // 2️⃣ 从段落样式继承 + if (pPr != null && pPr.getPStyle() != null && stylePart != null) { + String styleId = pPr.getPStyle().getVal(); + if (styleId != null) { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null + && style.getPPr().getSpacing().getLine() != null) { + int lineVal = style.getPPr().getSpacing().getLine().intValue(); + switch (lineVal) { + case 240: return "单倍"; + case 360: return "1.5 倍"; + case 480: return "双倍"; + default: return "多倍行距"; + } + } + } + } + + // 3️⃣ 根据标题样式 Word 默认行距 + if (pPr != null && pPr.getPStyle() != null) { + String styleId = pPr.getPStyle().getVal(); + if (styleId != null ) { + switch (styleId) { + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + case "8": + case "9": + return "1.5 倍"; + default: + return "单倍"; + } + } + } + + // 4️⃣ 没有信息返回默认 + return "无"; } - /// 段落格式(间距) 间距值 - public static String getParagraphSpacingLines(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { + + // 转换行距数值为磅数 +// 段落行距 + public static String getParagraphSpacingLines(P paragraph, StyleDefinitionsPart stylePart, + WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { + if (paragraph == null) return "无"; + PPr pPr = paragraph.getPPr(); - if (pPr != null && pPr.getSpacing() != null) { - return pPr.getSpacing().getLine().toString(); + + // 1️⃣ 段落自身行距 + if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getLine() != null) { + int lineVal = pPr.getSpacing().getLine().intValue(); + String lineRule = (pPr.getSpacing().getLineRule() != null) ? pPr.getSpacing().getLineRule().toString() : "auto"; + return convertLineValueToReadable(lineVal, lineRule); } + + // 2️⃣ 从段落样式继承 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 spacing.getLine().toString(); + if (styleId != null) { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null + && style.getPPr().getSpacing().getLine() != null) { + int lineVal = style.getPPr().getSpacing().getLine().intValue(); + String lineRule = (style.getPPr().getSpacing().getLineRule() != null) ? + style.getPPr().getSpacing().getLineRule().toString() : "auto"; + return convertLineValueToReadable(lineVal, lineRule); + } } } - return ""; + + // 3️⃣ 根据标题样式 Word 默认行距(返回磅数) + if (pPr != null && pPr.getPStyle() != null) { + String styleId = pPr.getPStyle().getVal(); + if (styleId != null ) { + switch (styleId) { + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + case "8": + case "9": + return convertLineValueToReadable(360, "auto"); // 默认单倍 + default: + return convertLineValueToReadable(240, "auto"); // 默认单倍 + } + + } + } + + + // 4️⃣ 没有信息返回默认 + return "无"; } + // 转换行距数值为磅数 + private static String convertLineValueToReadable(int lineTwips, String lineRule) { + // twips -> point + double points = lineTwips / 20.0; // 1 pt = 20 twips + + switch (lineRule) { + case "auto": + case "exact": + case "atLeast": + return String.format("%.1f 磅", points); // 返回磅数,保留一位小数 + default: + return String.format("%.1f 磅", points); + } + } + + + + + + + /// 段落 编号列表 存在 public static String getParagraphListIsTrue(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); @@ -254,9 +377,9 @@ public class Paragraphs { if (pPr != null && pPr.getNumPr() != null && pPr.getNumPr().getNumId() != null) { PPrBase.NumPr numPr = pPr.getNumPr(); BigInteger ilvl = numPr.getIlvl() != null ? numPr.getIlvl().getVal() : null; - return (ilvl != null ? String.valueOf(ilvl.intValue()) : "未知"); + return (ilvl != null ? String.valueOf(ilvl.intValue()) : "无"); } else { - return "未知"; + return "无"; } } /// 段落 编号列表 编号样式 @@ -287,7 +410,7 @@ public class Paragraphs { } } } - return "未知"; + return "无"; } /// 段落 编号列表 编号格式 public static String getParagraphListLvlText(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { @@ -317,7 +440,7 @@ public class Paragraphs { } } } - return "未知"; + return "无"; } /// 段落 编号列表 项目符号 public static String getParagraphListLvlFuHao(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { @@ -346,7 +469,7 @@ public class Paragraphs { } } } - return "未知"; + return "无"; } /// 段落 编号列表 列表类型 public static String getParagraphListLvlType(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { @@ -379,7 +502,7 @@ public class Paragraphs { } } } - return "未知"; + return "无"; } // 段落边框 样式 @@ -405,7 +528,7 @@ public class Paragraphs { return top.getVal().value() + "-" + bottom.getVal().value() + "-" + left.getVal().value() + "-" + right.getVal().value(); } } - return "未知"; + return "无"; } // 段落边框 - 颜色 @@ -425,13 +548,13 @@ public class Paragraphs { // 判断上下左右边框是否一致 if (top.getColor().equals(bottom.getColor()) && top.getColor().equals(left.getColor()) && top.getColor().equals(right.getColor())) { // 说明样式一样 - return top.getColor(); + return ColorUtils.getChineseColorName(top.getColor()); } else { // 说明样式不一样 return top.getColor() + "-" + bottom.getColor() + "-" + left.getColor() + "-" + right.getColor(); } } - return "未知"; + return "无"; } // 段落边框 - 宽度 @@ -457,7 +580,7 @@ public class Paragraphs { return top.getSz().toString() + "-" + bottom.getSz().toString() + "-" + left.getSz().toString() + "-" + right.getSz().toString(); } } - return "未知"; + return "无"; } // 段落底纹 - 填充颜色 @@ -468,7 +591,7 @@ public class Paragraphs { CTShd shd = pPr.getShd(); return shd.getFill(); } - return "未知"; + return "无"; } // 段落底纹 - 图案样式 @@ -479,7 +602,7 @@ public class Paragraphs { CTShd shd = pPr.getShd(); return shd.getVal().value(); } - return "未知"; + return "无"; } // 段落底纹 - 图案颜色 @@ -490,7 +613,7 @@ public class Paragraphs { CTShd shd = pPr.getShd(); return shd.getColor(); } - return "未知"; + return "无"; } // 首字下沉 - 是否存在 @@ -513,7 +636,7 @@ public class Paragraphs { return framePr.getDropCap().value(); // drop | none | margin } } - return "未知"; + return "无"; } // 首字下沉 - 行数 @@ -525,21 +648,24 @@ public class Paragraphs { return String.valueOf(framePr.getLines().intValue()); // twips } } - return "未知"; + return "无"; } // 首字下沉 - 距正文 +// 段落首字下沉水平间距(转换为磅) public static String getParagraphDropCapHSpace(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getFramePr() != null) { CTFramePr framePr = pPr.getFramePr(); if (framePr.getHSpace() != null) { - return String.valueOf(framePr.getHSpace().intValue()); // twips + double points = framePr.getHSpace().intValue() / 20.0; // twips -> pt + return String.format("%.1f磅", points); } } - return "未知"; + return "无"; } + // 首字下沉 - 字体 public static String getParagraphDropCapFont(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { PPr pPr = paragraph.getPPr(); @@ -550,7 +676,7 @@ public class Paragraphs { return fontName; } } - return "未知"; + return "无"; } // 分栏 - 栏数 @@ -574,7 +700,7 @@ public class Paragraphs { // return String.valueOf(cols.getNum().intValue()); // } // -// return "未知"; +// return "无"; // } public static String getParagraphCols(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { @@ -664,7 +790,7 @@ public class Paragraphs { CTColumns cols = sectPr.getCols(); return cols.isSep() ? "是" : "否"; } - return "未知"; + return "无"; } // 分栏 - 各栏间距 @@ -697,7 +823,7 @@ public class Paragraphs { return value; } } - return "未知"; + return "无"; } // 分栏 - 各栏宽度 public static String getParagraphColW(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { @@ -729,7 +855,7 @@ public class Paragraphs { return value; } } - return "未知"; + return "无"; } // 分栏 - 栏宽相等 @@ -752,7 +878,7 @@ public class Paragraphs { CTColumns cols = sectPr.getCols(); return cols.isEqualWidth() ? "是" : "否"; } - return "未知"; + return "无"; } // 中文版式 - 双行合一 @@ -768,4 +894,6 @@ public class Paragraphs { return "否"; } + // + } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/RunText.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/RunText.java index 24117974..69e5528b 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/RunText.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/RunText.java @@ -3,63 +3,162 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph; import org.docx4j.XmlUtils; import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart; import org.docx4j.wml.*; +import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; import java.math.BigInteger; +import java.util.List; public class RunText { - // 句子-字号 - public static String getParagraphAlignment(R run, String text) { - if (run == null) { - return null ; - } - RPr rPr = run.getRPr(); - String eastAsiaFont = null, asciiFont = null, hAnsiFont = null; - String fontSize = null; + // 句子-字号(返回第一个有效值) + public static String getParagraphAlignment(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; - if (rPr != null) { - // 字体 - if (rPr.getRFonts() != null) { - RFonts fonts = rPr.getRFonts(); - eastAsiaFont = fonts.getEastAsia(); - asciiFont = fonts.getAscii(); - hAnsiFont = fonts.getHAnsi(); - } - // 字号(单位是半磅;20 = 10.0pt) - if (rPr.getSz() != null) { - BigInteger sz = rPr.getSz().getVal(); - fontSize = sz != null ? (sz.intValue() / 2.0) + " pt" : null; + // 1️⃣ 优先检查 Run 本身 + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null) { + // 直接定义的字号 + if (rPr.getSz() != null && rPr.getSz().getVal() != null) { + return (rPr.getSz().getVal().intValue() / 2.0) + " pt"; + } + // 有时字号在 szCs + if (rPr.getSzCs() != null && rPr.getSzCs().getVal() != null) { + return (rPr.getSzCs().getVal().intValue() / 2.0) + " pt"; + } } } - System.out.println("文本内容: " + text); - System.out.println("中文字体: " + (eastAsiaFont != null ? eastAsiaFont : "未设置")); - System.out.println("西文字体(ASCII): " + (asciiFont != null ? asciiFont : "未设置")); - System.out.println("西文字体(HAnsi): " + (hAnsiFont != null ? hAnsiFont : "未设置")); - System.out.println("字号: " + (fontSize != null ? fontSize : "未设置")); - System.out.println("--------"); - return fontSize != null ? fontSize : "未设置"; + // 2️⃣ 段落级别字号(直接在 里) + if (paragraph != null && paragraph.getPPr() != null && paragraph.getPPr().getRPr() != null) { + ParaRPr pRPr = paragraph.getPPr().getRPr(); + if (pRPr.getSz() != null && pRPr.getSz().getVal() != null) { + return (pRPr.getSz().getVal().intValue() / 2.0) + " pt"; + } + if (pRPr.getSzCs() != null && pRPr.getSzCs().getVal() != null) { + return (pRPr.getSzCs().getVal().intValue() / 2.0) + " pt"; + } + } + + // 3️⃣ 从样式表中继承(标题、正文等) + if (paragraph != null && stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null) { + RPr styleRPr = style.getRPr(); + if (styleRPr.getSz() != null && styleRPr.getSz().getVal() != null) { + return (styleRPr.getSz().getVal().intValue() / 2.0) + " pt"; + } + if (styleRPr.getSzCs() != null && styleRPr.getSzCs().getVal() != null) { + return (styleRPr.getSzCs().getVal().intValue() / 2.0) + " pt"; + } + } + + // 🔹 如果是标题样式(Heading1, Heading2...),可设置默认字号 + if (styleId.toLowerCase().startsWith("heading")) { + switch (styleId.toLowerCase()) { + case "heading1": return "24 pt"; // 默认大标题 + case "heading2": return "18 pt"; + case "heading3": return "14 pt"; + default: return "12 pt"; // 兜底 + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 4️⃣ 全部没有 → 返回默认 + return "无"; } - // 字体-字形 ( 加粗 + 倾斜) - public static String getParagraphEastAsiaFontAlignment(R run, String text) { - if (run == null) { - return null ; - } - RPr rPr = run.getRPr(); - String eastAsiaFont = null, asciiFont = null, hAnsiFont = null; - String fontSize = null; - if (rPr != null) { - // 字体 - if (rPr.getRFonts() != null) { - RFonts fonts = rPr.getRFonts(); - eastAsiaFont = fonts.getEastAsia(); - asciiFont = fonts.getAscii(); - hAnsiFont = fonts.getHAnsi(); + + + + // 字体 - 中文字体(返回第一个有效值) + public static String getParagraphEastAsiaFont(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + + // 1️⃣ Run 级别字体 + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getRFonts() != null && rPr.getRFonts().getEastAsia() != null) { + return rPr.getRFonts().getEastAsia(); } } - return eastAsiaFont != null ? eastAsiaFont : "未设置"; + + // 2️⃣ 段落级别字体 + if (paragraph != null && paragraph.getPPr() != null && paragraph.getPPr().getRPr() != null) { + ParaRPr pRPr = paragraph.getPPr().getRPr(); + if (pRPr.getRFonts() != null && pRPr.getRFonts().getEastAsia() != null) { + return pRPr.getRFonts().getEastAsia(); + } + } + + // 3️⃣ 样式表字体(例如标题、正文) + if (paragraph != null && stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getRFonts() != null + && style.getRPr().getRFonts().getEastAsia() != null) { + return style.getRPr().getRFonts().getEastAsia(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 4️⃣ 全部没有 → 返回默认 + return "无"; } + + + // 字体 - 英文字体 + public static String getParagraphAsciiFont(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + + // 1️⃣ Run 级别字体 + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getRFonts() != null && rPr.getRFonts().getAscii() != null) { + return rPr.getRFonts().getAscii(); + } + } + + // 2️⃣ 段落级别字体 + if (paragraph != null && paragraph.getPPr() != null && paragraph.getPPr().getRPr() != null) { + ParaRPr pRPr = paragraph.getPPr().getRPr(); + if (pRPr.getRFonts() != null && pRPr.getRFonts().getAscii() != null) { + return pRPr.getRFonts().getAscii(); + } + } + + // 3️⃣ 样式表字体(例如标题、正文、Normal) + if (paragraph != null && stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getRFonts() != null + && style.getRPr().getRFonts().getAscii() != null) { + return style.getRPr().getRFonts().getAscii(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 4️⃣ 全部没有 → 返回默认 + return "无"; + } + + + + // 字体-字形 ( 加粗 + 倾斜) public static void getParagraphFontStyle(R run, String text) { RPr rPr = run.getRPr(); @@ -83,81 +182,32 @@ public class RunText { } System.out.println("--------"); } - // 字体-字形 ( 加粗 + 倾斜) - public static String getParagraphFontStyleIsBoldAndIsItalic(R run, String text) { - RPr rPr = run.getRPr(); - boolean isBold = false; - boolean isItalic = false; - - if (rPr != null) { - isBold = rPr.getB() != null && rPr.getB().isVal(); - isItalic = rPr.getI() != null && rPr.getI().isVal(); - } - - System.out.println("文本: " + text); - if (isBold && isItalic) { - return "是"; - }else { - return "否"; - } + // 字体-字形 (加粗 + 倾斜) + public static String getParagraphFontStyleIsBoldAndIsItalic(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + boolean isBold = isBold(runs, paragraph, stylePart); + boolean isItalic = isItalic(runs, paragraph, stylePart); + return (isBold && isItalic) ? "是" : "否"; } + // 字体-字形 (加粗) - public static String getParagraphFontStyleIsBold(R run, String text) { - RPr rPr = run.getRPr(); - boolean isBold = false; - boolean isItalic = false; - - if (rPr != null) { - isBold = rPr.getB() != null && rPr.getB().isVal(); - isItalic = rPr.getI() != null && rPr.getI().isVal(); - } - - System.out.println("文本: " + text + " 是否加粗:" + isBold + " 是否斜体:" + isItalic); - - if (isBold) { - return "是"; - }else { - return "否"; - } + public static String getParagraphFontStyleIsBold(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + return isBold(runs, paragraph, stylePart) ? "是" : "否"; } + // 字体-字形 (倾斜) - public static String getParagraphFontStyleIsItalic(R run, String text) { - RPr rPr = run.getRPr(); - boolean isBold = false; - boolean isItalic = false; - - if (rPr != null) { - isBold = rPr.getB() != null && rPr.getB().isVal(); - isItalic = rPr.getI() != null && rPr.getI().isVal(); - } - - System.out.println("文本: " + text); - if (isItalic) { - return "是"; - }else { - return "否"; - } + public static String getParagraphFontStyleIsItalic(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + return isItalic(runs, paragraph, stylePart) ? "是" : "否"; } + // 字体-字形 (常规) - public static String getParagraphFontStyleIsNomral(R run, String text) { - RPr rPr = run.getRPr(); - boolean isBold = false; - boolean isItalic = false; - - if (rPr != null) { - isBold = rPr.getB() != null && rPr.getB().isVal(); - isItalic = rPr.getI() != null && rPr.getI().isVal(); - } - - System.out.println("文本: " + text); - if (isBold || isItalic) { - return "否"; - }else { - return "是"; - } - + public static String getParagraphFontStyleIsNormal(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + boolean isBold = isBold(runs, paragraph, stylePart); + boolean isItalic = isItalic(runs, paragraph, stylePart); + return (!isBold && !isItalic) ? "是" : "否"; } + + // 字体-所有文字 // public static void getParagraphFontColor(R run, StyleDefinitionsPart stylePart, String text) { // RPr rPr = run.getRPr(); @@ -188,55 +238,104 @@ public class RunText { // System.out.println("--------"); // } - public static String getParagraphFontColor(R run, String text) { - RPr rPr = run.getRPr(); - String color = "默认"; - if (rPr != null) { - if (rPr.getColor() != null && rPr.getColor().getVal() != null) { - color = rPr.getColor().getVal(); // 如 "FF0000" - } - } - System.out.println("文本: " + text); - System.out.println("字体颜色: " + color); - System.out.println("--------"); - return color; - } + // 字体颜色 + public static String getParagraphFontColor(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "默认"; - public static String getParagraphFontUnderlineVal(R run, String text) { - RPr rPr = run.getRPr(); - String underlineVal = null; - - if (rPr != null) { - if (rPr.getU() != null) { - if (rPr.getU().getVal() != null) { - underlineVal = rPr.getU().getVal().value(); // single, double, none 等 - } + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getColor() != null && rPr.getColor().getVal() != null) { + return ColorUtils.getChineseColorName(rPr.getColor().getVal()); } } - return underlineVal; - - } - public static String getParagraphFontUnderlineColor(R run, String text) { - RPr rPr = run.getRPr(); - - String underlineColor = null; - - if (rPr != null) { - if (rPr.getU() != null) { - - if (rPr.getU().getColor() != null) { - underlineColor = rPr.getU().getColor(); // 例如 "0000FF" + // 如果 run 里没有,检查段落样式继承 + if (paragraph != null && stylePart != null) { + PPr pPr = paragraph.getPPr(); + if (pPr != null && pPr.getPStyle() != null) { + String styleId = pPr.getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getColor() != null) { + return ColorUtils.getChineseColorName(style.getRPr().getColor().getVal()); + } + } catch (Exception e) { + e.printStackTrace(); } } } - return underlineColor; - + return "默认"; } + + // 下划线类型 + public static String getParagraphFontUnderlineVal(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return null; + + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getU() != null && rPr.getU().getVal() != null) { + return rPr.getU().getVal().value(); // single, double, none 等 + } + } + + // 样式继承 + if (paragraph != null && stylePart != null) { + PPr pPr = paragraph.getPPr(); + if (pPr != null && pPr.getPStyle() != null) { + String styleId = pPr.getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getU() != null) { + return style.getRPr().getU().getVal().value(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + return null; + } + + // 下划线颜色 + public static String getParagraphFontUnderlineColor(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return null; + + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getU() != null && rPr.getU().getColor() != null) { + return ColorUtils.getChineseColorName(rPr.getU().getColor()); + } + } + + // 样式继承 + if (paragraph != null && stylePart != null) { + PPr pPr = paragraph.getPPr(); + if (pPr != null && pPr.getPStyle() != null) { + String styleId = pPr.getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getU() != null + && style.getRPr().getU().getColor() != null) { + return ColorUtils.getChineseColorName(style.getRPr().getU().getColor()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + return null; + } + + // 字体-效果-删除线 - public static void getParagraphStrike(R run, String text) { + public static void getParagraphStrike(R run, String text, P paragraph, StyleDefinitionsPart stylePart) { RPr rPr = run.getRPr(); boolean isStrike = false; @@ -296,81 +395,134 @@ public class RunText { // System.out.println("--------"); // } - //缩放比例 - public static String getParagraphFontSpacingScale(R run, String text) { - RPr rPr = run.getRPr(); - - String scale = "100%"; - - - if (rPr != null) { - if (rPr.getW() != null && rPr.getW().getVal() != null) { - scale = rPr.getW().getVal().toString() + "%"; - } - } - System.out.println("缩放比例: " + scale); - return scale; - } - //字符间距 - public static String getParagraphFontSpacing(R run, String text) { - RPr rPr = run.getRPr(); - String spacing = null; - if (rPr != null) { - if (rPr.getSpacing() != null && rPr.getSpacing().getVal() != null) { - spacing = rPr.getSpacing().getVal().toString() + " twips"; - } - } - System.out.println("字符间距: " + spacing); - return spacing; - } - //字符位置 - public static String getParagraphFontPosition(R run, String text) { - RPr rPr = run.getRPr(); - - String position = null; - - if (rPr != null) { - if (rPr.getPosition() != null && rPr.getPosition().getVal() != null) { - position = rPr.getPosition().getVal().toString() + " pt(1/2)"; + // 缩放比例 + public static String getParagraphFontSpacingScale(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + // 1. run 级别 + if (runs != null) { + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getW() != null && rPr.getW().getVal() != null) { + return rPr.getW().getVal().toString() + "%"; + } } } - System.out.println("字符位置: " + position); - return position; - } - //最小间距 - public static String getParagraphFontKern(R run, String text) { - RPr rPr = run.getRPr(); - - String kern = null; - - if (rPr != null) { - if (rPr.getKern() != null && rPr.getKern().getVal() != null) { - kern = rPr.getKern().getVal().toString() + " (1/100 pt)"; - } - - } - - System.out.println("最小间距 (Kern): " + kern); - return kern; - } - - //对齐到网格 - public static String getParagraphFontSnapToGrid(R run, String text) { - RPr rPr = run.getRPr(); - - String snapToGrid = "否"; - - if (rPr != null) { - if (rPr.getSnapToGrid() != null) { - snapToGrid = rPr.getSnapToGrid().isVal() ? "是" : "否"; + // 2. style 级别 + if (stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getW() != null) { + return style.getRPr().getW().getVal().toString() + "%"; } } - System.out.println("对齐到网格: " + snapToGrid); - return snapToGrid; + // 3. 默认值 + return "100%"; } + // 字符间距(转换为磅) + public static String getParagraphFontSpacing(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs != null) { + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getSpacing() != null && rPr.getSpacing().getVal() != null) { + int twips = rPr.getSpacing().getVal().intValue(); + double pt = twips / 20.0; // 1/20 pt + return pt + " 磅"; + } + } + } + + if (stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getSpacing() != null) { + int twips = style.getRPr().getSpacing().getVal().intValue(); + double pt = twips / 20.0; + return pt + " 磅"; + } + } + return null; // 默认无间距 + } + + // 字符位置(上标下标,转换为磅) + public static String getParagraphFontPosition(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs != null) { + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getPosition() != null && rPr.getPosition().getVal() != null) { + int halfPts = rPr.getPosition().getVal().intValue(); + double pt = halfPts / 2.0; // 1/2 pt + return pt + " 磅"; + } + } + } + + if (stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getPosition() != null) { + int halfPts = style.getRPr().getPosition().getVal().intValue(); + double pt = halfPts / 2.0; + return pt + " 磅"; + } + } + return null; // 默认正常位置 + } + + // 最小间距 (Kern) 转换为磅 + public static String getParagraphFontKern(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs != null) { + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getKern() != null && rPr.getKern().getVal() != null) { + int kernVal = rPr.getKern().getVal().intValue(); // 1/100 pt + double pt = kernVal / 100.0; + return pt + " 磅"; + } + } + } + + if (stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getKern() != null) { + int kernVal = style.getRPr().getKern().getVal().intValue(); + double pt = kernVal / 100.0; + return pt + " 磅"; + } + } + return null; // 默认无字距调整 + } + + // 对齐到网格 + public static String getParagraphFontSnapToGrid(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs != null) { + for (R run : runs) { + if (run == null) continue; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getSnapToGrid() != null) { + return rPr.getSnapToGrid().isVal() ? "是" : "否"; + } + } + } + + if (stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getSnapToGrid() != null) { + return style.getRPr().getSnapToGrid().isVal() ? "是" : "否"; + } + } + + return "否"; // 默认不对齐 + } + + // 字体-文本效果 public static void getParagraphTextEffect(R run, String text) { @@ -387,35 +539,319 @@ public class RunText { System.out.println("--------"); } - //文本填充 - public static String getParagraphTextFillEffect(R run, String text) { - // 转换为 XML 文本 - String runXml = XmlUtils.marshaltoString(run, true); - return runXml.contains("w14:textFill") ? "是" : "否"; + // 文本填充效果 + public static String getParagraphTextFillEffect(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + if (runXml.contains("w14:textFill")) return "是"; + } + return "否"; + } + // 文本轮廓 + public static String getParagraphTextOutline(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + if (runXml.contains("w14:textOutline")) return "是"; + } + return "否"; } - //文本轮廓 - public static String getParagraphTextOutline(R run, String text) { - // 转换为 XML 文本 - String runXml = XmlUtils.marshaltoString(run, true); - return runXml.contains("w14:textOutline") ? "是" : "否"; + + // 阴影 + public static String getParagraphTextShadow(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + if (runXml.contains("w14:shadow")) return "是"; + } + return "否"; } - //阴影 - public static String getParagraphTextShadow(R run, String text) { - // 转换为 XML 文本 - String runXml = XmlUtils.marshaltoString(run, true); - return runXml.contains("w14:shadow") ? "是" : "否"; + + // 倒影 + public static String getParagraphTextReflection(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + if (runXml.contains("w14:reflection")) return "是"; + } + return "否"; } - //倒影 - public static String getParagraphTextReflection(R run, String text) { - // 转换为 XML 文本 - String runXml = XmlUtils.marshaltoString(run, true); - return runXml.contains("w14:reflection") ? "是" : "否"; + + // 发光 + public static String getParagraphTextGlow(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + if (runXml.contains("w14:glow")) return "是"; + } + return "否"; } - //发光 - public static String getParagraphTextGlow(R run, String text) { - // 转换为 XML 文本 - String runXml = XmlUtils.marshaltoString(run, true); - return runXml.contains("w14:glow") ? "是" : "否"; + + // =====句子边框的样式===== + public static String getParagraphTextStyle(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + int idx = runXml.indexOf("= 0) { + int valStart = runXml.indexOf("w:val=\"", idx) + 7; + int valEnd = runXml.indexOf("\"", valStart); + if (valStart > 7 && valEnd > valStart) { + return runXml.substring(valStart, valEnd); + } + } + } + return "无"; // 默认无边框 } + + // =====句子边框的颜色===== + public static String getParagraphTextColor(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + int idx = runXml.indexOf("= 0) { + // 先取 w:color + int colorStart = runXml.indexOf("w:color=\"", idx); + if (colorStart >= 0) { + colorStart += 9; + int colorEnd = runXml.indexOf("\"", colorStart); + if (colorEnd > colorStart) { + String substring = runXml.substring(colorStart, colorEnd); + if ("auto".equalsIgnoreCase(substring)) { + return ColorUtils.getChineseColorName("000000"); + } + return ColorUtils.getChineseColorName(substring); + } + } + + // 如果没有 w:color,再取 w:themeColor + int themeStart = runXml.indexOf("w:themeColor=\"", idx); + if (themeStart >= 0) { + themeStart += 14; + int themeEnd = runXml.indexOf("\"", themeStart); + if (themeEnd > themeStart) { + String theme = runXml.substring(themeStart, themeEnd); + if ("AUTO".equalsIgnoreCase(theme)) { + return ColorUtils.getChineseColorName("000000"); + } + return ColorUtils.getChineseColorName(theme); + } + } + } + } + return "无"; // 默认无边框颜色 + } + + // =====句子边框的宽度===== + public static String getParagraphTextWidth(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "默认"; + for (R run : runs) { + if (run == null) continue; + String runXml = XmlUtils.marshaltoString(run, true); + int idx = runXml.indexOf("= 0) { + int sizeStart = runXml.indexOf("w:sz=\"", idx) + 6; + int sizeEnd = runXml.indexOf("\"", sizeStart); + if (sizeStart > 6 && sizeEnd > sizeStart) { + String val = runXml.substring(sizeStart, sizeEnd); + try { + int width = Integer.parseInt(val); + return width + "pt"; // Word XML 单位通常为 1/8 pt + } catch (NumberFormatException e) { + return val; + } + } + } + } + return "默认"; // 默认宽度 + } + + // =====句子-字体(效果)删除线===== + public static String getStrikeThrough(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getStrike() != null) { + return "是"; + } + } + return "无"; + } + + // =====句子-字体(效果)双删除线===== + public static String getDoubleStrikeThrough(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getDstrike() != null) { + return "是"; + } + } + return "否"; + } + + + // 句子-字体(效果)上标 + public static String getSuperScript(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getVertAlign() != null) { + CTVerticalAlignRun vertAlign = run.getRPr().getVertAlign(); + if (vertAlign.getVal() != null && vertAlign.getVal() == STVerticalAlignRun.SUPERSCRIPT) { + return "是"; + } + } + } + return "否"; + } + + // 句子-字体(效果)下标 + public static String getSubScript(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getVertAlign() != null) { + CTVerticalAlignRun vertAlign = run.getRPr().getVertAlign(); + if (vertAlign.getVal() != null && vertAlign.getVal() == STVerticalAlignRun.SUBSCRIPT) { + return "是"; + } + } + } + return "否"; + } + + // 句子-字体(效果)小型大写字母 + public static String getSmallCaps(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "否"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getSmallCaps() != null) { + return "是"; + } + } + return "否"; + } + + // 句子-字体(效果)全部大写字母 + public static String getCaps(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getCaps() != null) { + return "是"; + } + } + return "否"; + } + + // 句子底纹-填充颜色 + public static String getShadingFillColor(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getShd() != null) { + CTShd shd = run.getRPr().getShd(); + String fill = shd.getFill(); + if (fill != null) { + return ColorUtils.getChineseColorName(fill); + } + } + } + return "无"; + } + + // 句子底纹-图案样式 + public static String getShadingPattern(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getShd() != null) { + CTShd shd = run.getRPr().getShd(); + STShd pattern = shd.getVal(); + if (pattern != null) { + return pattern.toString(); + } + } + } + return "无"; + } + + + + // 句子底纹-图案颜色 + public static String getShadingPatternColor(List runs, String text, P paragraph, StyleDefinitionsPart stylePart) { + if (runs == null || runs.isEmpty()) return "无"; + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getShd() != null) { + CTShd shd = run.getRPr().getShd(); + String color = shd.getColor(); + if (color != null) { + return ColorUtils.getChineseColorName(color); + } + } + } + return "无"; + } + private static boolean isBold(List runs, P paragraph, StyleDefinitionsPart stylePart) { + // 1️⃣ Run 级别 + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getB() != null && run.getRPr().getB().isVal()) { + return true; + } + } + // 2️⃣ 段落级别 + if (paragraph != null && paragraph.getPPr() != null && paragraph.getPPr().getRPr() != null) { + ParaRPr pRPr = paragraph.getPPr().getRPr(); + if (pRPr.getB() != null && pRPr.getB().isVal()) { + return true; + } + } + // 3️⃣ 样式表级别 + if (paragraph != null && stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getB() != null && style.getRPr().getB().isVal()) { + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return false; + } + + private static boolean isItalic(List runs, P paragraph, StyleDefinitionsPart stylePart) { + // 1️⃣ Run 级别 + for (R run : runs) { + if (run != null && run.getRPr() != null && run.getRPr().getI() != null && run.getRPr().getI().isVal()) { + return true; + } + } + // 2️⃣ 段落级别 + if (paragraph != null && paragraph.getPPr() != null && paragraph.getPPr().getRPr() != null) { + ParaRPr pRPr = paragraph.getPPr().getRPr(); + if (pRPr.getI() != null && pRPr.getI().isVal()) { + return true; + } + } + // 3️⃣ 样式表级别 + if (paragraph != null && stylePart != null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + try { + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getI() != null && style.getRPr().getI().isVal()) { + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return false; + } + + } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/HeaderInfo.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/HeaderInfo.java index 8c596504..e18dc711 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/HeaderInfo.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/HeaderInfo.java @@ -13,4 +13,6 @@ public class HeaderInfo { public Double fontSizePt; public String colorHex; public String alignment; + public String bold; + public String italic; } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java index a8b62079..e2163728 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java @@ -14,6 +14,7 @@ import org.docx4j.openpackaging.parts.Part; import org.docx4j.openpackaging.parts.WordprocessingML.*; import org.docx4j.wml.*; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageBorders; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz; @@ -50,75 +51,119 @@ public class SectionPage { } } //节页面设置-上边距 - public static String getSectionPageSettingsTop(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getSectionPageSettingsTop(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSectPr() != null) { SectPr sectPr = pPr.getSectPr(); SectPr.PgMar pgMar = sectPr.getPgMar(); - if (pgMar != null) { - System.out.println("上边距 (twips): " + pgMar.getTop()); - return pgMar.getTop().toString(); + if (pgMar != null && pgMar.getTop() != null) { + long topTwips = pgMar.getTop().longValue(); // twips + double topPt = topTwips / 20.0; // 转换为磅 + double topCm = topPt * 0.0352778; // 转换为厘米 + return String.format("%.1f磅(%.2f厘米)", topPt, topCm); } } return null; } + //节页面设置-下边距 - public static String getSectionPageSettingsBottom(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getSectionPageSettingsBottom(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSectPr() != null) { SectPr sectPr = pPr.getSectPr(); SectPr.PgMar pgMar = sectPr.getPgMar(); - if (pgMar != null) { - System.out.println("下边距 (twips): " + pgMar.getBottom()); - return pgMar.getBottom().toString(); + if (pgMar != null && pgMar.getBottom() != null) { + long bottomTwips = pgMar.getBottom().longValue(); // twips + double bottomPt = bottomTwips / 20.0; // 磅 + double bottomCm = bottomPt * 0.0352778; // 厘米 + return String.format("%.1f磅(%.2f厘米)", bottomPt, bottomCm); } } return null; } + //节页面设置-左边距 - public static String getSectionPageSettingsLeft(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getSectionPageSettingsLeft(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSectPr() != null) { SectPr sectPr = pPr.getSectPr(); SectPr.PgMar pgMar = sectPr.getPgMar(); - if (pgMar != null) { - System.out.println("左边距 (twips): " + pgMar.getLeft()); - return pgMar.getLeft().toString(); + if (pgMar != null && pgMar.getLeft() != null) { + long leftTwips = pgMar.getLeft().longValue(); // twips + double leftPt = leftTwips / 20.0; // 转换为磅 + double leftCm = leftPt * 0.0352778; // 转换为厘米 + return String.format("%.1f磅(%.2f厘米)", leftPt, leftCm); } } return null; } + //节页面设置-右边距 - public static String getSectionPageSettingsRight(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getSectionPageSettingsRight(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSectPr() != null) { SectPr sectPr = pPr.getSectPr(); SectPr.PgMar pgMar = sectPr.getPgMar(); - if (pgMar != null) { - System.out.println("右边距 (twips): " + pgMar.getRight()); - return pgMar.getRight().toString(); + if (pgMar != null && pgMar.getRight() != null) { + long rightTwips = pgMar.getRight().longValue(); // twips + double rightPt = rightTwips / 20.0; // 转换为磅 + double rightCm = rightPt * 0.0352778; // 转换为厘米 + return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm); } } return null; } + //装订线边距 - public static String getSectionPageSettingsGutter(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getSectionPageSettingsGutter(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getSectPr() != null) { SectPr sectPr = pPr.getSectPr(); SectPr.PgMar pgMar = sectPr.getPgMar(); - if (pgMar != null) { - System.out.println("上边距 (twips): " + pgMar.getGutter()); - return pgMar.getGutter().toString(); + if (pgMar != null && pgMar.getGutter() != null) { + long gutterTwips = pgMar.getGutter().longValue(); // twips + double gutterPt = gutterTwips / 20.0; // 转换为磅 + double gutterCm = gutterPt * 0.0352778; // 转换为厘米 + return String.format("%.1f磅(%.2f厘米)", gutterPt, gutterCm); } } return null; } + //每页行数 + public static String getSectionPageLineNumber(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (paragraph == null || paragraph.getPPr() == null) return null; + + SectPr sectPr = paragraph.getPPr().getSectPr(); + if (sectPr == null) return null; + + CTDocGrid docGrid = sectPr.getDocGrid(); + if (docGrid == null || docGrid.getLinePitch() == null) return null; + + // 每行高度(twips) + int linePitchTwips = docGrid.getLinePitch().intValue(); + + // 获取页面可用高度(twips) + SectPr.PgMar pgMar = sectPr.getPgMar(); + int pageHeightTwips = 0; + if (pgMar != null) { + // 页面高度 = 页面总高 - 上下页边距 + pageHeightTwips = (sectPr.getPgSz() != null && sectPr.getPgSz().getH() != null + ? sectPr.getPgSz().getH().intValue() : 0) + - (pgMar.getTop() != null ? pgMar.getTop().intValue() : 0) + - (pgMar.getBottom() != null ? pgMar.getBottom().intValue() : 0); + } + + if (pageHeightTwips <= 0 || linePitchTwips <= 0) return null; + + int linesPerPage = pageHeightTwips / linePitchTwips; + + return linesPerPage + "行/页"; + } // 节页面设置 - 纸张方向 public static String getSectionPageOrientation(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { @@ -268,6 +313,23 @@ public class SectionPage { return convertAlignToChinese(align); } + //加粗 + public static String getOddHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultHeader() == null) { + return null; + } + String bold = extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).bold; + return bold; + } + //倾斜 + public static String getOddHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultHeader() == null) { + return null; + } + String italic = extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).italic; + return italic; + } + // ----- 偶数页眉 ----- //偶数页眉文本 public static String getEvenHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { @@ -296,7 +358,22 @@ public class SectionPage { String align = hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).alignment : null; return convertAlignToChinese(align); } - + //加粗 + public static String getEvenHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenHeader() == null) { + return null; + } + String bold = extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).bold; + return bold; + } + //倾斜 + public static String getEvenHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenHeader() == null) { + return null; + } + String italic = extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).italic; + return italic; + } // ----- 首页眉 ----- //首页眉文本 public static String getFirstHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { @@ -328,7 +405,22 @@ public class SectionPage { String align = hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).alignment : null; return convertAlignToChinese(align); } - + //加粗 + public static String getFirstHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstHeader() == null) { + return null; + } + String bold = extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).bold; + return bold; + } + //倾斜 + public static String getFirstHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstHeader() == null) { + return null; + } + String italic = extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).italic; + return italic; + } // 页脚 @@ -376,6 +468,42 @@ public class SectionPage { HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); return convertAlignToChinese(info.alignment); } + //颜色 + public static String getOddFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) return null; + String colorHex = extractFooterInfo(hfp.getDefaultFooter(),wordMLPackage).colorHex; + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); + } + + public static String getOddFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) return null; + String text = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage).text; + if (text==null){ + return null; + } + return text; + } + + + + //加粗 + public static String getOddFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) { + return null; + } + String bold = extractFooterInfo(hfp.getDefaultFooter(),wordMLPackage).bold; + return bold; + } + //倾斜 + public static String getOddFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) { + return null; + } + String italic = extractFooterInfo(hfp.getDefaultFooter(),wordMLPackage).italic; + return italic; + } + + // 偶数页 //字体 @@ -397,24 +525,75 @@ public class SectionPage { HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); return convertAlignToChinese(info.alignment); } + public static String getEvenFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getEvenFooter() != null ? extractFooterInfo(hfp.getEvenFooter(),wordMLPackage).text : null; + } + //颜色 + public static String getEvenFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenFooter() == null) return null; + String colorHex = extractFooterInfo(hfp.getEvenFooter(),wordMLPackage).colorHex; + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); + } - + //加粗 + public static String getEvenFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenFooter() == null) { + return null; + } + String bold = extractFooterInfo(hfp.getEvenFooter(),wordMLPackage).bold; + return bold; + } + //倾斜 + public static String getEvenFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenFooter() == null) { + return null; + } + String italic = extractFooterInfo(hfp.getEvenFooter(),wordMLPackage).italic; + return italic; + } // ===== 首页页脚 ===== public static String getFirstFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); return info == null ? null : info.font; } public static String getFirstFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); return info == null ? null : String.valueOf(info.fontSizePt); } public static String getFirstFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); return convertAlignToChinese(info.alignment); } + public static String getFirstFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstFooter() == null) return null; + String colorHex = extractFooterInfo(hfp.getFirstFooter(),wordMLPackage).colorHex; + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); + } + public static String getFirstFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getFirstFooter() != null ? extractFooterInfo(hfp.getFirstFooter(),wordMLPackage).text : null; + } + //加粗 + public static String getFirstFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstFooter() == null) { + return null; + } + String bold = extractFooterInfo(hfp.getFirstFooter(),wordMLPackage).bold; + return bold; + } + //倾斜 + public static String getFirstFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstFooter() == null) { + return null; + } + String italic = extractFooterInfo(hfp.getFirstFooter(),wordMLPackage).italic; + return italic; + } private static FooterPart getFooterPartByParagraph(P targetParagraph, WordprocessingMLPackage wordMLPackage, List sections, boolean isOddPage) { MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); List allContent = mainPart.getContent(); @@ -479,29 +658,7 @@ public class SectionPage { } for (Object o : paragraph.getContent()) { - if (o instanceof R) { - R run = (R) o; - RPr rPr = run.getRPr(); - if (rPr != null) { - if (info.font == null) { - info.font = getFontFromRunOrStyle(rPr, styleId, wordMLPackage); - } - if (info.fontSizePt == null) { - info.fontSizePt = getFontSizeFromRunOrStyle(rPr, styleId, wordMLPackage); - } - if (rPr.getColor() != null) { - info.colorHex = rPr.getColor().getVal(); - } - } - - // 文字内容 - for (Object rContent : run.getContent()) { - Object val = (rContent instanceof JAXBElement) ? ((JAXBElement) rContent).getValue() : rContent; - if (val instanceof Text) { - textBuilder.append(((Text) val).getValue()); - } - } - } + extractTextFromContent(o, textBuilder, info, styleId, wordMLPackage); } } } @@ -511,6 +668,60 @@ public class SectionPage { } + /** + * 递归解析内容,包括文本框里的段落 + */ + private static void extractTextFromContent(Object o, StringBuilder builder, + HeaderInfo info, String styleId, + WordprocessingMLPackage wordMLPackage) { + Object unwrapped = XmlUtils.unwrap(o); + + if (unwrapped instanceof P) { + P p = (P) unwrapped; + for (Object child : p.getContent()) { + extractTextFromContent(child, builder, info, styleId, wordMLPackage); + } + } else if (unwrapped instanceof R) { + R run = (R) unwrapped; + RPr rPr = run.getRPr(); + if (rPr != null) { + if (info.font == null) { + info.font = getFontFromRunOrStyle(rPr, styleId, wordMLPackage); + } + if (info.fontSizePt == null) { + info.fontSizePt = getFontSizeFromRunOrStyle(rPr, styleId, wordMLPackage); + } + if (rPr.getColor() != null) { + info.colorHex = rPr.getColor().getVal(); + } + } + for (Object rContent : run.getContent()) { + Object val = (rContent instanceof JAXBElement) ? ((JAXBElement) rContent).getValue() : rContent; + extractTextFromContent(val, builder, info, styleId, wordMLPackage); + } + } else if (unwrapped instanceof Text) { + builder.append(((Text) unwrapped).getValue()); + } else if (unwrapped instanceof org.docx4j.dml.wordprocessingDrawing.Inline + || unwrapped instanceof org.docx4j.dml.wordprocessingDrawing.Anchor) { + // drawing 对象,里面可能有文本框 + try { + String xml = XmlUtils.marshaltoString(unwrapped, true, true); + if (xml.contains("txbxContent")) { + // 提取文本框内部内容 + List innerPs = (List) XmlUtils.unwrap(((Inline) unwrapped) + .getGraphic().getGraphicData().getAny()); + for (Object inner : innerPs) { + extractTextFromContent(inner, builder, info, styleId, wordMLPackage); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } else if (unwrapped instanceof JAXBElement) { + extractTextFromContent(((JAXBElement) unwrapped).getValue(), builder, info, styleId, wordMLPackage); + } + } + // 修改你的 getFooterInfo,改为递归遍历拿到所有段落的格式 private static HeaderInfo getFooterInfo(FooterPart footerPart, WordprocessingMLPackage wordMLPackage) { @@ -643,34 +854,37 @@ public class SectionPage { // 页脚(元素) 包含页码 返回 是/否 + public static String getHeaderContainsPageNumber( + P paragraph, + HeaderFooterPolicy hfp, + WordprocessingMLPackage wordMLPackage, + List sections) { - public static String getHeaderContainsPageNumber( - P paragraph, - HeaderFooterPolicy hfp, - WordprocessingMLPackage wordMLPackage, - List sections) { + if (hfp == null) return "否"; - if (sections == null || sections.isEmpty()) { - return null; - } + // 获取页脚对象列表 + FooterPart[] footerParts = new FooterPart[]{ + hfp.getFirstFooter(), + hfp.getEvenFooter(), + hfp.getDefaultFooter() + }; - for (SectionWrapper section : sections) { - SectPr sectPr = section.getSectPr(); - if (sectPr == null) continue; + 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 "是"; + } + } + + return "否"; + } - if (sectPr.getPgNumType() != null) { - // 这里可以进一步获取格式,比如: - if (sectPr.getPgNumType().getFmt()!=null){ - System.out.println("检测到页码格式: " + String.valueOf(sectPr.getPgNumType().getFmt())); - return "是"; - } - } - } - return null; - } @@ -947,21 +1161,20 @@ public class SectionPage { SectPr.PgSz pgSz = currentSectPr.getPgSz(); BigInteger w = pgSz.getW(); BigInteger h = pgSz.getH(); - // 常用纸张判断 (宽 x 高, 单位 twip) if (w != null && h != null) { - if (w.equals(BigInteger.valueOf(11906)) && h.equals(BigInteger.valueOf(16838))) return "A4"; - if (w.equals(BigInteger.valueOf(16838)) && h.equals(BigInteger.valueOf(23811))) return "A3"; - if (w.equals(BigInteger.valueOf(8504)) && h.equals(BigInteger.valueOf(11906))) return "B5"; - if (w.equals(BigInteger.valueOf(23811)) && h.equals(BigInteger.valueOf(33685))) return "A2"; - if (w.equals(BigInteger.valueOf(11906)) && h.equals(BigInteger.valueOf(16838))) return "Letter"; // 可根据需要补充 + if ((w.equals(BigInteger.valueOf(11906)) && h.equals(BigInteger.valueOf(16838)))||(h.equals(BigInteger.valueOf(11906)) && w.equals(BigInteger.valueOf(16838)))) return "A4"; + if ((w.equals(BigInteger.valueOf(16838)) && h.equals(BigInteger.valueOf(23811))) ||((h.equals(BigInteger.valueOf(16838)) && w.equals(BigInteger.valueOf(23811))) )) return "A3"; + if ((w.equals(BigInteger.valueOf(8504)) && h.equals(BigInteger.valueOf(11906)))||(h.equals(BigInteger.valueOf(8504)) && w.equals(BigInteger.valueOf(11906)))) return "B5"; + if ((w.equals(BigInteger.valueOf(23811)) && h.equals(BigInteger.valueOf(33685)))||(h.equals(BigInteger.valueOf(23811)) && w.equals(BigInteger.valueOf(33685)))) return "A2"; + if ((w.equals(BigInteger.valueOf(11906)) && h.equals(BigInteger.valueOf(16838)))||(h.equals(BigInteger.valueOf(11906)) && w.equals(BigInteger.valueOf(16838)))) return "Letter"; // 可根据需要补充 } return "自定义纸张"; } - // 宽度 + //宽度 public static String getPageSetWidth(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); List allContent = mainPart.getContent(); @@ -1062,8 +1275,83 @@ public class SectionPage { return "无"; } + // 页面背景(页面颜色)-填充方式 + // 页面背景(页面颜色)-纯色填充→颜色 + // 页面背景(页面颜色)-渐变填充→预设 + // 页面背景(页面颜色)- 纹理填充→纹理 + // 页面背景(页面颜色)-图案填充→图案 + // 页面背景(页面边框)-样式 + public static String getPageBorderStyle(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + SectPr sectPr = getSectPr(paragraph, wordMLPackage); + if (sectPr != null && sectPr.getPgBorders() != null) { + SectPr.PgBorders borders = sectPr.getPgBorders(); + + // 以 Top 边框为例获取样式 + CTBorder top = borders.getTop(); + if (top != null && top.getVal() != null) { + return top.getVal().toString(); + } + } + return "无"; + } + + + // 页面背景(页面边框)-颜色 + public static String getPageBorderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + SectPr sectPr = getSectPr(paragraph, wordMLPackage); + if (sectPr != null && sectPr.getPgBorders() != null) { + SectPr.PgBorders borders = sectPr.getPgBorders(); + + // 以 Top 为例获取颜色 + CTBorder top = borders.getTop(); + if (top != null && top.getColor() != null) { + return top.getColor(); + } + } + return "无"; + } + + // 页面背景(页面边框)-宽度 + public static String getPageBorderWidth(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + SectPr sectPr = getSectPr(paragraph, wordMLPackage); + if (sectPr != null && sectPr.getPgBorders() != null) { + SectPr.PgBorders borders = sectPr.getPgBorders(); + + CTBorder top = borders.getTop(); + if (top != null && top.getSz() != null) { + // Word 中单位为 eighths of a point(1/8 pt) + return (top.getSz().intValue() / 8.0) + " 磅"; + } + } + return "无"; + } + // 辅助方法:获取段落所在节的 SectPr + private static SectPr getSectPr(P paragraph, WordprocessingMLPackage wordMLPackage) { + MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); + List allContent = mainPart.getContent(); + SectPr currentSectPr = null; + + for (Object obj : allContent) { + Object unwrapped = XmlUtils.unwrap(obj); + if (unwrapped instanceof P) { + P p = (P) unwrapped; + if (p.getPPr() != null && p.getPPr().getSectPr() != null) { + currentSectPr = p.getPPr().getSectPr(); + } + if (p == paragraph) break; + } + } + + // 如果段落没有 SectPr,则使用 body 的 SectPr + if (currentSectPr == null) { + currentSectPr = mainPart.getJaxbElement().getBody().getSectPr(); + } + + return currentSectPr; + } + // ------------------------------------------- // 私有方法:从参数中找到 VML 水印 XML 字符串(只取第一个匹配) private static String getVMLWatermarkXml(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { @@ -1268,6 +1556,12 @@ public class SectionPage { if (rPr.getColor() != null) { info.colorHex = rPr.getColor().getVal(); } + // 加粗 + info.bold = (rPr.getB() != null && Boolean.TRUE.equals(rPr.getB().isVal())) ? "是" : "否"; + + // 倾斜 + info.italic = (rPr.getI() != null && Boolean.TRUE.equals(rPr.getI().isVal())) ? "是" : "否"; + } // 取文字内容 diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextBoxFontUtils.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextBoxFontUtils.java new file mode 100644 index 00000000..56966594 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextBoxFontUtils.java @@ -0,0 +1,81 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.text; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + + +import jakarta.xml.bind.JAXBElement; +import org.docx4j.dml.wordprocessingDrawing.Anchor; +import org.docx4j.wml.P; +import org.docx4j.wml.R; +import org.docx4j.wml.RPr; +import org.docx4j.wml.U; + +public class TextBoxFontUtils { + + // 封装文字属性的类 + public static class TextBoxFontInfo { + public String text; + public String font; + public String color; + public boolean bold; + public boolean italic; + public String underlineStyle; + public String underlineColor; + + @Override + public String toString() { + return String.format("文字: %s, 字体: %s, 颜色: %s, 加粗: %b, 倾斜: %b, 下划线类型: %s, 下划线颜色: %s", + text, font, color, bold, italic, underlineStyle, underlineColor); + } + } + + public static List getTextBoxFontInfoList(List judgementWordsVOS, Anchor anchor, int betoLong, Object wordMLPackage) throws Exception { + List result = new ArrayList<>(); + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return result; + + Object graphicDataObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object graphicData = (graphicDataObj instanceof JAXBElement) ? ((JAXBElement) graphicDataObj).getValue() : graphicDataObj; + + // 只处理文本框 + if (!graphicData.getClass().getSimpleName().equals("CTWordprocessingShape")) return result; + + Field txbxField = graphicData.getClass().getDeclaredField("txbx"); + txbxField.setAccessible(true); + Object txbx = txbxField.get(graphicData); + if (txbx == null) return result; + + Field contentField = txbx.getClass().getDeclaredField("txbxContent"); + contentField.setAccessible(true); + List paras = (List) contentField.get(txbx); + + for (Object pObj : paras) { + if (pObj instanceof P) { + P p = (P) pObj; + for (Object rObj : p.getContent()) { + if (rObj instanceof R) { + R r = (R) rObj; + RPr rPr = r.getRPr(); + TextBoxFontInfo info = new TextBoxFontInfo(); + info.text = r.toString(); + + if (rPr != null) { + info.font = rPr.getRFonts() != null ? rPr.getRFonts().getAscii() : "默认字体"; + info.color = rPr.getColor() != null ? rPr.getColor().getVal() : "默认颜色"; + info.bold = Boolean.TRUE.equals(rPr.getB()); + info.italic = Boolean.TRUE.equals(rPr.getI()); + info.underlineStyle = rPr.getU() != null ? rPr.getU().getVal().toString() : "无"; + info.underlineColor = rPr.getColor() != null ? rPr.getColor().getVal() : "默认颜色"; + } + + result.add(info); + } + } + } + } + + return result; + } +} + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextInfo.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextInfo.java index d549d227..1c433431 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextInfo.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/text/TextInfo.java @@ -1,17 +1,23 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.text; +import com.microsoft.schemas.vml.CTLine; import jakarta.xml.bind.*; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.docx4j.TraversalUtil; import org.docx4j.XmlUtils; +import org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTTextboxInfo; import org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape; import org.docx4j.dml.*; +import org.docx4j.dml.CTColor; +import org.docx4j.dml.picture.Pic; import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.vml.CTTextbox; import org.docx4j.wml.*; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.DocxSetInfo; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert; @@ -19,16 +25,1175 @@ import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.JudgementWordsVO; import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.*; +import java.io.ByteArrayInputStream; import java.io.StringWriter; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class TextInfo { + + // 获取文本框高度(EMU -> cm) + public static String getLayoutHeight(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage){ + if (anchor.getExtent() != null) { + long hEmu = anchor.getExtent().getCy(); // EMUs + double cm = hEmu / 360000.0; // 1cm = 360000 EMUs + return String.format("%.2f 厘米", cm); + } + return null; + } + + // 获取文本框宽度(EMU -> cm) + public static String getLayoutWeight(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage){ + if (anchor.getExtent() != null) { + long wEmu = anchor.getExtent().getCx(); + double cm = wEmu / 360000.0; + return String.format("%.2f 厘米", cm); + } + return null; + } + + // 锁定横纵比(LockAspectRatio) + public static String getLayoutLock(List judgementWordsVOS,Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor != null && + anchor.getCNvGraphicFramePr() != null && + anchor.getCNvGraphicFramePr().getGraphicFrameLocks() != null) { + + Boolean lock = anchor.getCNvGraphicFramePr() + .getGraphicFrameLocks() + .isNoChangeAspect(); + + if (Boolean.TRUE.equals(lock)) { + return "是"; + } else { + return "否"; + } + } + return "无"; // 或者返回 null,看你的业务需求 + } + + + + // 获取环绕方式(Wrap type) + public static String getLayoutHuanRao(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage){ + if (anchor == null) return "无"; + // 是否衬于文字下方 + boolean behindDoc = anchor.isBehindDoc(); + if (behindDoc) return "衬于文字下方型"; + // 浮于文字上方型 + if (anchor.getWrapNone() != null) return "浮于文字上方型"; + if (anchor.getWrapSquare() != null) return "四周型"; + if (anchor.getWrapTight() != null) return "紧密型"; + if (anchor.getWrapThrough() != null) return "穿越型"; + if (anchor.getWrapTopAndBottom() != null) return "上下型"; + + return "无"; + } + + + //绘制 + public static String getShapeProfileHuiZhi(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return "否"; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + + Object wspObj; + if (anyObj instanceof JAXBElement) { + wspObj = ((JAXBElement) anyObj).getValue(); + } else { + wspObj = anyObj; + } + + // 直接判断轮廓线属性 + if (wspObj instanceof org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) { + org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape wsp = + (org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) wspObj; + + if (wsp.getSpPr() != null && wsp.getSpPr().getLn() != null) { + return "是"; + } + } + + return "否"; + + } + + + // 形状轮廓(RGB颜色) + public static String getShapeProfileColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + + Object wspObj; + if (anyObj instanceof JAXBElement) { + wspObj = ((JAXBElement) anyObj).getValue(); + } else { + wspObj = anyObj; + } + + if (wspObj instanceof org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) { + org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape wsp = + (org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) wspObj; + + if (wsp.getSpPr() != null && wsp.getSpPr().getLn() != null) { + org.docx4j.dml.CTLineProperties ln = wsp.getSpPr().getLn(); + + // 优先获取 srgbClr + if (ln.getSolidFill() != null) { + if (ln.getSolidFill().getSrgbClr() != null) { + return ColorUtils.getChineseColorName(ln.getSolidFill().getSrgbClr().getVal()); + } else if (ln.getSolidFill().getSchemeClr() != null) { + return ColorUtils.getChineseColorName(ln.getSolidFill().getSchemeClr().getVal().value()); + } + } + } + } + + return null; + } + + + // 形状轮廓 宽度 + public static String getShapeProfileKuanDu(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return "无"; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object obj; + if (anyObj instanceof JAXBElement) { + obj = ((JAXBElement) anyObj).getValue(); + } else { + obj = anyObj; + } + + // ========= 图片情况 ========= + if (obj instanceof org.docx4j.dml.picture.Pic) { + org.docx4j.dml.picture.Pic pic = (org.docx4j.dml.picture.Pic) obj; + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties outline = pic.getSpPr().getLn(); + if (outline.getW() != null) { + long widthEMU = outline.getW(); + double widthPoints = widthEMU / 12700.0; // EMU → pt + return String.format("%.2f 磅", widthPoints); + } + } + } + + // ========= 文本框情况 ========= + if (obj instanceof org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) { + org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape wsp = + (org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) obj; + + if (wsp.getSpPr() != null && wsp.getSpPr().getLn() != null) { + org.docx4j.dml.CTLineProperties outline = wsp.getSpPr().getLn(); + if (outline.getW() != null) { + long widthEMU = outline.getW(); + double widthPoints = widthEMU / 12700.0; // EMU → pt + return String.format("%.2f 磅", widthPoints); + } + } + } + + return "无"; + } + + + // 形状轮廓 线型(复合类型) + public static String getShapeProfileXianXing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object obj; + if (anyObj instanceof JAXBElement) { + obj = ((JAXBElement) anyObj).getValue(); + } else { + obj = anyObj; + } + + // ========= 图片情况 ========= + if (obj instanceof org.docx4j.dml.picture.Pic) { + org.docx4j.dml.picture.Pic pic = (org.docx4j.dml.picture.Pic) obj; + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties outline = pic.getSpPr().getLn(); + if (outline.getCmpd() != null) { + String val = outline.getCmpd().value(); // sng、dbl、tri 等 + return getLineTypeName(val); // 你的方法,转成中文描述 + } + } + } + + // ========= 文本框情况 ========= + if (obj instanceof org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) { + org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape wsp = + (org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) obj; + + if (wsp.getSpPr() != null && wsp.getSpPr().getLn() != null) { + org.docx4j.dml.CTLineProperties outline = wsp.getSpPr().getLn(); + if (outline.getCmpd() != null) { + String val = outline.getCmpd().value(); + return getLineTypeName(val); + } + } + } + + return null; + } + + // 形状轮廓 虚线(短划线类型) + public static String getShapeProfileDashVal(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object obj; + if (anyObj instanceof JAXBElement) { + obj = ((JAXBElement) anyObj).getValue(); + } else { + obj = anyObj; + } + + // ========= 图片情况 ========= + if (obj instanceof org.docx4j.dml.picture.Pic) { + org.docx4j.dml.picture.Pic pic = (org.docx4j.dml.picture.Pic) obj; + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties outline = pic.getSpPr().getLn(); + if (outline.getPrstDash() != null) { + return getDashStyleName(outline.getPrstDash().getVal().value()); + } + } + } + + // ========= 文本框情况 ========= + if (obj instanceof org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) { + org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape wsp = + (org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape) obj; + + if (wsp.getSpPr() != null && wsp.getSpPr().getLn() != null) { + org.docx4j.dml.CTLineProperties outline = wsp.getSpPr().getLn(); + if (outline.getPrstDash() != null) { + return getDashStyleName(outline.getPrstDash().getVal().value()); + } + } + } + + return null; + } + + + //形状轮廓 透明度 + public static String getShapeProfileTouMing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return "0%"; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object obj; + if (anyObj instanceof JAXBElement) { + obj = ((JAXBElement) anyObj).getValue(); + } else { + obj = anyObj; + } + + try { + // 统一用 JAXBElement 包装,避免 marshal 爆红 + JAXBElement jaxbObj = new JAXBElement<>( + new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "shape"), + (Class) obj.getClass(), + obj + ); + + // 转 XML 字符串 + String xml = XmlUtils.marshaltoString(jaxbObj, true, true); + + // 正则匹配 + Pattern pattern = Pattern.compile(""); + Matcher matcher = pattern.matcher(xml); + if (matcher.find()) { + int alphaVal = Integer.parseInt(matcher.group(1)); + // Docx4j alpha 范围 0~100000,透明度 = 100 - alpha% + int percent = Math.round((100000 - alphaVal) / 100000.0f * 100); + return percent + "%"; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return "0%"; + } + + + + + // 形状效果(阴影) + public static List getShapeYinYing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws JAXBException { + String name = "【第" + betoLong + "个图形】【图片】"; + String firstName = "【形状效果(阴影)】"; + String huiZhiName = firstName + "【绘制】"; + String yanSeName = firstName + "【颜色】"; + String touMingDuName = firstName + "【透明度】"; + String daXiaoName = firstName + "【大小】"; + String moHuName = firstName + "【模糊】"; + String parentId = Convert.getStringRandom(); + + // 先取 Any 对象 + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + JAXBContext context = JAXBContext.newInstance(value.getClass()); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + StringWriter writer = new StringWriter(); + marshaller.marshal(value, writer); + System.out.println(writer.toString()); + + + + // 判断是否是 Pic 类型 + if (value instanceof Pic) { + Pic pic = (Pic) value; + + if (pic.getSpPr() != null) { + // 获取阴影效果 + CTOuterShadowEffect shadow = null; + if (pic.getSpPr().getEffectLst() != null) { + shadow = pic.getSpPr().getEffectLst().getOuterShdw(); + } + + if (shadow == null) { + // 无阴影效果 + return judgementWordsVOS; + } + + // 有阴影,设置绘制为“是” + + // 颜色 + if (shadow.getPrstClr() != null) { + String colorVal = shadow.getPrstClr().getVal().value(); + + // 透明度,尝试通过颜色变换取alpha + try { + Object egColorTransform = shadow.getPrstClr().getEGColorTransform(); + if (egColorTransform != null) { + Method getAlphaMethod = egColorTransform.getClass().getMethod("getAlpha"); + Object alpha = getAlphaMethod.invoke(egColorTransform); + if (alpha != null) { + Method getValMethod = alpha.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(alpha); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + double opaquePercent = val / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + String transparency = String.format("%.1f%%", transparencyPercent); + } + } + } + } catch (Exception e) { + System.out.println("无法获取透明度信息: " + e.getMessage()); + } + } else if (shadow.getSrgbClr() != null) { + String colorVal = shadow.getSrgbClr().getVal(); + // 透明度处理同上,可重复写或封装 + } + + // 大小 - 阴影距离,单位 cm + double distCm = shadow.getDist() / 360000.0; + + // 模糊半径,单位 cm + double blurCm = shadow.getBlurRad() / 360000.0; + + // 你可以继续根据需要打印或返回其他阴影属性,比如方向、缩放、倾斜等 + // shadow.getDir(), shadow.getSx(), shadow.getSy(), shadow.getKx(), shadow.getKy() + + } else { + System.out.println("pic.getSpPr()为空,无法获取形状效果"); + } + } else { + System.out.println("value不是Pic类型,实际类型:" + value.getClass().getName()); + } + + return judgementWordsVOS; + } + + + // 获取“绘制”属性(是否绘制阴影) + public static String getShadowDraw(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + String firstName = "【形状效果(阴影)】"; + String huiZhiName = firstName + "【绘制】"; + String name = "【第" + betoLong + "个图形】【图片】" + huiZhiName; + + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) { + return "否"; + } + return "是"; + } + + // 获取“颜色”属性(阴影颜色) + public static String getShadowColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + // 主题色 + if (shadow.getSchemeClr() != null) { + CTSchemeColor schemeColor = shadow.getSchemeClr(); + if (schemeColor.getVal() != null) { + return ColorUtils.getChineseColorName(schemeColor.getVal().value()); + } + } + + if (shadow.getPrstClr() != null) { + return ColorUtils.getChineseColorName(shadow.getPrstClr().getVal().value()); + } + if (shadow.getSrgbClr() != null) { + return ColorUtils.getChineseColorName(shadow.getSrgbClr().getVal()); + } + + return "无"; + } + + + //模糊 + public static String getShadowBlur(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + // blurRad 单位是 EMUs,转成磅(1磅=12700 EMUs) + double blurPts = shadow.getBlurRad() / 12700.0; + return String.format("%.2f 磅", blurPts); + } + //距离 + public static String getShadowDistance(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + // dist 单位 EMUs,转成厘米 + double distCm = shadow.getDist() / 12700.0; + return String.format("%.2f 磅", distCm); + } + //大小 + public static String getShadowScaleX(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + // sx 单位是 1/1000 百分比 + double scaleXPercent = shadow.getSx() / 1000.0; + return String.format("%.1f%%", scaleXPercent); + } + //角度 + public static String getShadowDirection(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + // dir 单位是 1/60000 度 + double dirDegree = shadow.getDir() / 60000.0; + return String.format("%.1f°", dirDegree); + } + //透明度 + public static String getShadowAlpha(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null) return "无"; + + try { + // 获取 Anchor 下的 graphicData + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object graphicData = anyObj; + if (anyObj instanceof JAXBElement) { + graphicData = ((JAXBElement) anyObj).getValue(); + } + + // 判断是否是 Pic 或 WpsShape + CTShapeProperties spPr = null; + if (graphicData instanceof Pic) { + Pic pic = (Pic) graphicData; + spPr = pic.getSpPr(); + } else if (graphicData.getClass().getName().contains("CTWordprocessingShape")) { + // 文本框情况 + Method getSpPrMethod = graphicData.getClass().getMethod("getSpPr"); + spPr = (CTShapeProperties) getSpPrMethod.invoke(graphicData); + } + + if (spPr == null || spPr.getEffectLst() == null) return "无"; + + CTOuterShadowEffect shadow = spPr.getEffectLst().getOuterShdw(); + if (shadow == null) return "无"; + + // 尝试 srgbClr + if (shadow.getSrgbClr() != null) { + CTSRgbColor srgb = shadow.getSrgbClr(); + String alphaStr = parseAlphaFromColor(srgb.getEGColorTransform()); + if (alphaStr != null) return alphaStr; + return "0%"; + } + + // 尝试 schemeClr + if (shadow.getSchemeClr() != null) { + CTSchemeColor scheme = shadow.getSchemeClr(); + String alphaStr = parseAlphaFromColor(scheme.getEGColorTransform()); + if (alphaStr != null) return alphaStr; + return "0%"; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return "无"; + } + + + +//形状填充 + + //填充方式 + public static String getFillType(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws JAXBException { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + + // 解开 JAXBElement + if (anyObj instanceof JAXBElement) { + anyObj = ((JAXBElement) anyObj).getValue(); + } + + // 必须是 WordprocessingShape 类型 + if (anyObj instanceof CTWordprocessingShape) { + CTWordprocessingShape shape = (CTWordprocessingShape) anyObj; + + // spPr 里存储了填充信息 + CTShapeProperties spPr = shape.getSpPr(); + if (spPr == null) { + return "无填充"; + } + + CTSolidColorFillProperties solidFill = spPr.getSolidFill(); + if (solidFill != null) { + return "纯色填充"; + } + + CTBlipFillProperties blipFill = spPr.getBlipFill(); + if (blipFill != null) { + return "图片填充"; + } + + CTPatternFillProperties pattFill = spPr.getPattFill(); + if (pattFill != null) { + return "图案填充"; + } + + return "无"; + } + + return null; + } + // marshalShapeProperties(spPr); + public static String getSolidFillColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getAny().isEmpty()) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + + // 解开 JAXBElement + if (anyObj instanceof JAXBElement) { + anyObj = ((JAXBElement) anyObj).getValue(); + } + + // 必须是文本框类型 + if (!(anyObj instanceof CTWordprocessingShape)) { + return null; + } + + CTWordprocessingShape shape = (CTWordprocessingShape) anyObj; + CTShapeProperties spPr = shape.getSpPr(); + if (spPr == null) { + return null; + } + + CTSolidColorFillProperties solidFill = spPr.getSolidFill(); + if (solidFill == null) { + return null; + } + + // 处理 srgbClr + CTSRgbColor rgbColor = solidFill.getSrgbClr(); + if (rgbColor != null && rgbColor.getVal() != null) { + String hex = rgbColor.getVal(); + System.out.println("文本框纯色填充 (srgbClr) = " + hex); + return ColorUtils.getChineseColorName(hex); + } + + // 处理 schemeClr + CTSchemeColor schemeColor = solidFill.getSchemeClr(); + if (schemeColor != null && schemeColor.getVal() != null) { + String schemeName = schemeColor.getVal().value(); + System.out.println("文本框纯色填充 (schemeClr) = " + schemeName); + return ColorUtils.getChineseColorName(schemeName); + } + + System.out.println("文本框纯色填充属性存在,但颜色值为空"); + return null; + } + + // 纯色填充→透明度 + public static String getPictureFillTransparency(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getAny().isEmpty()) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + + // 解开 JAXBElement + if (anyObj instanceof JAXBElement) { + anyObj = ((JAXBElement) anyObj).getValue(); + } + + // 必须是文本框类型 + if (!(anyObj instanceof CTWordprocessingShape)) { + return null; + } + + CTWordprocessingShape shape = (CTWordprocessingShape) anyObj; + CTShapeProperties spPr = shape.getSpPr(); + if (spPr == null) { + return null; + } + + CTSolidColorFillProperties solidFill = spPr.getSolidFill(); + if (solidFill == null) { + return null; + } + + + // 检查 srgbClr + if (solidFill.getSrgbClr() != null) { + CTSRgbColor srgbColor = solidFill.getSrgbClr(); + List egColorTransformList = srgbColor.getEGColorTransform(); + if (egColorTransformList != null && !egColorTransformList.isEmpty()) { + for (Object transform : egColorTransformList) { + if (transform instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) transform; + Object alphaObj = jaxbElement.getValue(); + if (jaxbElement.getName().getLocalPart().equals("alpha")) { + try { + Method getValMethod = alphaObj.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(alphaObj); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + double opaquePercent = val / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + return String.format("%.1f%%", transparencyPercent); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + return "0%"; // 默认不透明 + } + + // 主题色也可能包含 alpha + if (solidFill.getSchemeClr() != null) { + CTSchemeColor schemeColor = solidFill.getSchemeClr(); + List egColorTransformList = schemeColor.getEGColorTransform(); + if (egColorTransformList != null && !egColorTransformList.isEmpty()) { + for (Object transform : egColorTransformList) { + if (transform instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) transform; + if (jaxbElement.getName().getLocalPart().equals("alpha")) { + Object alphaObj = jaxbElement.getValue(); + try { + Method getValMethod = alphaObj.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(alphaObj); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + double opaquePercent = val / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + return String.format("%.1f%%", transparencyPercent); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + return "0%"; // 默认不透明 + } + + return "0%"; // 无透明属性,默认完全不透明 + } + + + // 纹理填充 → 透明度 + //todo + public static String getTextureFillTransparency( + List judgementWordsVOS, + Anchor anchor, + int betoLong, + WordprocessingMLPackage wordMLPackage) throws Exception { + try { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + + if (!(value instanceof Pic)) { + return "无纹理填充透明度"; + } + + Pic pic = (Pic) value; + + if (pic.getSpPr() == null || pic.getSpPr().getBlipFill() == null) { + return "无纹理填充透明度"; + } + + Object blipObj = pic.getSpPr().getBlipFill().getBlip(); + if (blipObj == null) { + return "无纹理填充透明度"; + } + + // 调用 getAlphaBiLevelOrAlphaCeilingOrAlphaFloor 方法 + Method getAlphaListMethod = blipObj.getClass().getMethod("getAlphaBiLevelOrAlphaCeilingOrAlphaFloor"); + List alphaList = (List) getAlphaListMethod.invoke(blipObj); + + if (alphaList != null) { + for (Object alphaObj : alphaList) { + Object val = alphaObj; + if (alphaObj instanceof JAXBElement) { + val = ((JAXBElement) alphaObj).getValue(); + } + + // 判断元素类名 + if ("CTAlphaModulateFixedEffect".equals(val.getClass().getSimpleName())) { + // 找到 alphaModFix,取 amt 属性 + Method getAmtMethod = val.getClass().getMethod("getAmt"); + Number amtNum = (Number) getAmtMethod.invoke(val); + int amt = amtNum.intValue(); + + System.out.println("Found alpha modulate amt: " + amt); + + double transparencyPercent = (1.0 - amt / 100000.0) * 100.0; + if (transparencyPercent < 0) transparencyPercent = 0; + if (transparencyPercent > 100) transparencyPercent = 100; + + return String.format("%.0f%%", transparencyPercent); + + +// // 计算透明度百分比 +// double transparencyPercent = (1.0 - amt / 100000.0) * 100.0; +// if (transparencyPercent < 0) transparencyPercent = 0; +// if (transparencyPercent > 100) transparencyPercent = 100; +// +// return String.format("%.0f%%", transparencyPercent); + + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + return "读取异常"; + } + + return "无纹理填充透明度"; + } + + private static String getAlphaModFixTransparencyFromBlipFill(Object blipFill) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + if (blipFill == null) { + return "无"; + } + // 通过反射获取 blip 对象 + Method getBlipMethod = blipFill.getClass().getMethod("getBlip"); + Object blip = getBlipMethod.invoke(blipFill); + if (blip == null) { + return "无"; + } + try { + + + // blip 里通常有 getAlphaModFix 方法 + Method getAlphaModFixMethod = blip.getClass().getMethod("getAlphaModFix"); + Object alphaModFix = getAlphaModFixMethod.invoke(blip); + if (alphaModFix == null) { + return "无"; + } + + // alphaModFix 有 getAmt 方法,返回 Long + Method getAmtMethod = alphaModFix.getClass().getMethod("getAmt"); + Long amt = (Long) getAmtMethod.invoke(alphaModFix); + + if (amt == null) { + return "无"; + } + + // 计算透明度百分比 (amt 代表不透明度,范围0-100000) + double transparencyPercent = (1.0 - amt / 100000.0) * 100.0; + if (transparencyPercent < 0) transparencyPercent = 0; + if (transparencyPercent > 100) transparencyPercent = 100; + + return String.format("%.1f%%", transparencyPercent); + + } catch (NoSuchMethodException e) { + // 如果没有getAlphaModFix方法,再尝试用反射查找 getAny() 里是否有 CTAlphaModulateFixed 节点 + try { + Method getAnyMethod = blip.getClass().getMethod("getAny"); + List anyList = (List) getAnyMethod.invoke(blip); + if (anyList != null) { + for (Object obj : anyList) { + Object element = obj; + if (obj instanceof JAXBElement) { + element = ((JAXBElement) obj).getValue(); + } + if (element.getClass().getSimpleName().equals("CTAlphaModulateFixed")) { + Method getAmtMethod = element.getClass().getMethod("getAmt"); + Long amt = (Long) getAmtMethod.invoke(element); + if (amt != null) { + double transparencyPercent = (1.0 - amt / 100000.0) * 100.0; + if (transparencyPercent < 0) transparencyPercent = 0; + if (transparencyPercent > 100) transparencyPercent = 100; + return String.format("%.1f%%", transparencyPercent); + } + } + } + } + } catch (Exception ex) { + // 忽略,没找到 + } + } catch (Exception e) { + e.printStackTrace(); + } + return "无"; + } + public static String getTextureFillTransparencyFromBlip(CTBlip blip) throws Exception { + // 尝试通过反射调用 getAny() + Method getAnyMethod = blip.getClass().getMethod("getAny"); + List anyList = (List) getAnyMethod.invoke(blip); + + for (Object obj : anyList) { + if (obj instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) obj; + String localName = jaxbElement.getName().getLocalPart(); + + if ("alphaModFix".equals(localName) || "alphaMod".equals(localName) || "alpha".equals(localName)) { + Object valObj = jaxbElement.getValue(); + + // valObj 应该有 getAmt() 或 getVal() 方法 + try { + Method getAmtMethod = valObj.getClass().getMethod("getAmt"); + Object amtObj = getAmtMethod.invoke(valObj); + + if (amtObj instanceof BigInteger) { + int amt = ((BigInteger) amtObj).intValue(); + double opaquePercent = amt / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + return String.format("%.1f%%", transparencyPercent); + } + } catch (NoSuchMethodException nsme) { + // 尝试 getVal() + Method getValMethod = valObj.getClass().getMethod("getVal"); + Object val2 = getValMethod.invoke(valObj); + if (val2 instanceof BigInteger) { + int amt = ((BigInteger) val2).intValue(); + double opaquePercent = amt / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + return String.format("%.1f%%", transparencyPercent); + } + } + } + } + } + return "0%"; // 找不到则默认不透明 + } + + //图案填充→图案 + public static String getPatternFillPattern(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPatternFillProperties pattFill = getShapeProperties(anchor) == null ? null : getShapeProperties(anchor).getPattFill(); + if (pattFill == null) return "无图案填充"; + + // 这里直接返回枚举toString或value(),也可以直接用toString() + Object prst = pattFill.getPrst(); + if (prst != null) { + return prst.toString(); + } + return "未知图案"; + } + //图案填充→前景色 + public static String getPatternFillForeColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPatternFillProperties pattFill = getShapeProperties(anchor) == null ? null : getShapeProperties(anchor).getPattFill(); + if (pattFill == null) return "无图案填充"; + + if (pattFill.getFgClr() != null) { + System.out.println(extractColor(pattFill.getFgClr())); + return ColorUtils.getChineseColorName(extractColor(pattFill.getFgClr())); + + } + return "无前景色"; + } + //图案填充→背景色 + public static String getPatternFillBackColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPatternFillProperties pattFill = getShapeProperties(anchor) == null ? null : getShapeProperties(anchor).getPattFill(); + if (pattFill == null) return "无图案填充"; + + if (pattFill.getBgClr() != null) { + System.out.println(extractColor(pattFill.getBgClr())); + return ColorUtils.getChineseColorName(extractColor(pattFill.getBgClr())); + + } + return "无背景色"; + } + + //形状效果(倒影) + // 1. 是否绘制倒影 + public static String getShadowDrawing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return "否"; + } + + Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0); + if (graphicData instanceof JAXBElement) { + graphicData = ((JAXBElement) graphicData).getValue(); + } + + CTShapeProperties spPr = null; + try { + // 图片类型 + if (graphicData instanceof Pic) { + Pic pic = (Pic) graphicData; + spPr = pic.getSpPr(); + } + // 文本框类型 + else if (graphicData.getClass().getName().contains("CTWordprocessingShape")) { + Method getSpPrMethod = graphicData.getClass().getMethod("getSpPr"); + spPr = (CTShapeProperties) getSpPrMethod.invoke(graphicData); + } + + if (spPr == null || spPr.getEffectLst() == null) return "否"; + + // 判断是否有阴影效果(CTOuterShadowEffect) + CTOuterShadowEffect shadow = spPr.getEffectLst().getOuterShdw(); + if (shadow != null) return "是"; + + } catch (Exception e) { + e.printStackTrace(); + } + + return "否"; + } + + + // 2. 预设类型(shadow preset) +// public static String getShadowPreset(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { +// CTReflectionEffect shadow = getReflectionEffect(anchor); +// if (shadow == null) return "无预设"; +// +// // 例如根据blurRad、dist、dir组合判断 +// BigInteger blurRad = BigInteger.valueOf(shadow.getBlurRad()); +// BigInteger dist = BigInteger.valueOf(shadow.getDist()); +// Long dir = (long) shadow.getDir(); +// +// return String.format( +// dir != null ? dir.toString() : "无"); +// } +// public static String getShadowPresetFromAnchor(Anchor anchor) { +// if (anchor == null +// || anchor.getGraphic() == null +// || anchor.getGraphic().getGraphicData() == null) { +// return "无倒影"; +// } +// +// Object graphicDataObj = anchor.getGraphic().getGraphicData().getAny().get(0); +// Object graphicData = graphicDataObj instanceof JAXBElement ? ((JAXBElement) graphicDataObj).getValue() : graphicDataObj; +// +// CTShapeProperties spPr = null; +// +// // 文本框 +// try { +// Method getSpPr = graphicData.getClass().getMethod("getSpPr"); +// spPr = (CTShapeProperties) getSpPr.invoke(graphicData); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// if (spPr == null || spPr.getEffectLst() == null || spPr.getEffectLst().getReflection() == null) { +// return "无倒影"; +// } +// +// CTReflectionEffect reflection = spPr.getEffectLst().getReflection(); +// +// Integer syVal = reflection.getSy(); // 用 Integer 接收 +// int sy = syVal != null ? syVal : 0; // 再拆箱为 int +// Integer endPosVal = reflection.getEndPos(); +// int endPos = endPosVal != null ? endPosVal : 0; +// Integer blurRadVal = Math.toIntExact(reflection.getBlurRad()); +// int blurRad = blurRadVal != null ? blurRadVal : 0; +// +// +// // 映像预设匹配(可以根据实际值微调) +// String preset = "未知"; +// if (sy <= -90000) { +// preset = "全映像"; +// } else if (sy <= -70000) { +// preset = "半映像"; +// } else if (sy < 0) { +// preset = "紧密映像"; +// } +// +// // EMU 转磅 +// double endPosPt = endPos / 12700.0; +// double blurRadPt = blurRad / 12700.0; +// +// return String.format("%s %.1f磅 / %.1f磅", preset, endPosPt, blurRadPt); +// } + + + // 3. 透明度(单位%) + public static String getShadowTransparency( + List judgementWordsVOS, + Anchor anchor, + int betoLong, + WordprocessingMLPackage wordMLPackage) { + + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null) { + return "无倒影"; + } + + Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0); + if (graphicData instanceof JAXBElement) { + graphicData = ((JAXBElement) graphicData).getValue(); + } + + CTShapeProperties spPr = null; + + try { + // 图片类型 + if (graphicData instanceof Pic) { + Pic pic = (Pic) graphicData; + spPr = pic.getSpPr(); + } + // 文本框类型 + else if (graphicData.getClass().getName().contains("CTWordprocessingShape")) { + Method getSpPrMethod = graphicData.getClass().getMethod("getSpPr"); + spPr = (CTShapeProperties) getSpPrMethod.invoke(graphicData); + } + + if (spPr == null || spPr.getEffectLst() == null || spPr.getEffectLst().getReflection() == null) { + return "无倒影"; + } + + CTReflectionEffect reflection = spPr.getEffectLst().getReflection(); + + Integer stA = reflection.getStA(); // 起始透明度 + Integer endA = reflection.getEndA(); // 结束透明度 + + if (stA == null && endA == null) { + return "0%"; + } + + int stAVal = stA != null ? stA : 0; + + // 透明度范围是0~100000,值越大越不透明 + double stTransparencyPercent = 100.0 - (stAVal / 1000.0); + + return String.format("%.1f%%", stTransparencyPercent); + + } catch (Exception e) { + e.printStackTrace(); + } + + return "无倒影"; + } + + + // 4. 大小(比例,单位%) + public static String getShadowSize(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws JAXBException { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + CTReflectionEffect shadow = getReflectionEffect(anchor); + if (shadow == null) return "无大小"; + + // 这里使用 sy 代表倒影大小比例,单位是 1/100000 + Integer sy = shadow.getEndPos(); + if (sy == null) return "无大小"; + + // 转换为百分比,sy=100000表示100% + // 计算绝对值,因为sy可能是负值(表示倒影翻转) + double percent = Math.abs(sy) / 1000.0; + + return String.format("%.1f%%", percent); + } + + + // 5. 模糊半径(单位磅) + public static String getShadowBlurs(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTReflectionEffect reflection = getReflectionEffect(anchor); + if (reflection == null) return "无模糊"; + + Integer blurRad = Math.toIntExact(reflection.getBlurRad()); + if (blurRad == null) return "无模糊"; + + // EMU转磅 + double blurPt = blurRad * 72.0 / 914400.0; + return String.format("%.2f 磅", blurPt); + } + + // 6. 距离(单位磅) + public static String getShadowDistances(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTReflectionEffect reflection = getReflectionEffect(anchor); + if (reflection == null) return "无距离"; + + Integer dist = Math.toIntExact(reflection.getDist()); + if (dist == null) return "无距离"; + + // EMU转磅 + double distPt = dist * 72.0 / 914400.0; + return String.format("%.2f 磅", distPt); + } + + + + + + + + + + // 文本内容 public static List getTextInfo(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage){ String name = "【第" + betoLong + "个图形】【文本框】"; @@ -141,8 +1306,7 @@ public class TextInfo { // 旋转角度(Word 存的是 1/60000 度) Long rotValue = Long.valueOf(textInfo.getBodyPr().getRot()); int angle = rotValue != null ? (int)(rotValue / 60000) : 0; - - // 判断 +// 判断 if ("horz".equals(vertValue) && angle == 0) { return "水平"; } @@ -158,8 +1322,12 @@ public class TextInfo { if ("eaVert".equals(vertValue) && angle == 270) { return "中文字符旋转270度"; } + if ("eaVert".equals(vertValue) && angle == 0) { + return "竖排"; + } return "未知方向"; + } } return null; @@ -195,30 +1363,7 @@ public class TextInfo { } - //纯色填充→颜色 - public static String getSolidFillColor(List judgementWordsVOS, Anchor anchor, int betoLong,WordprocessingMLPackage wordMLPackage) { - try { - Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0); - if (graphicData instanceof JAXBElement) { - JAXBElement jaxbElement = (JAXBElement) graphicData; - Object value = jaxbElement.getValue(); - if (value instanceof CTWordprocessingShape) { - CTWordprocessingShape textInfo = (CTWordprocessingShape) value; - if (textInfo.getSpPr() != null && textInfo.getSpPr().getSolidFill() != null) { - if (textInfo.getSpPr().getSolidFill().getSrgbClr() != null) { - return ColorUtils.getChineseColorName(textInfo.getSpPr().getSolidFill().getSrgbClr().getVal()); - } else if (textInfo.getSpPr().getSolidFill().getSchemeClr() != null) { - // 如果是schemeClr,也可处理,简单返回其val字符串 - return textInfo.getSpPr().getSolidFill().getSchemeClr().getVal().value(); - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } + //纯色填充→透明度 public static String getTextBoxSolidFillTransparency(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { @@ -319,7 +1464,219 @@ public class TextInfo { return "否"; } - //字体 字号 + + // 获取预设 +// 获取阴影预设效果 + public static String getTextShadowPresetColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + XPath xpath = XPathFactory.newInstance().newXPath(); + + // shadow 元素的 prst 属性 + Node node = (Node) xpath.evaluate("//*[local-name()='shadow']", doc, XPathConstants.NODE); + if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element ele = (org.w3c.dom.Element) node; + // 遍历并打印所有属性 + org.w3c.dom.NamedNodeMap attrs = ele.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node attr = attrs.item(i); + System.out.println(attr.getNodeName() + " = " + attr.getNodeValue()); + } + String preset = ele.getAttribute("prst"); + if (preset != null && !preset.isEmpty()) { + return preset; + } + } + } + return "无"; + } + + + // 获取阴影颜色 + public static String getTextShadowColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + List anyList = anchor.getGraphic().getGraphicData().getAny(); + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + XPath xpath = XPathFactory.newInstance().newXPath(); + Node node = (Node) xpath.evaluate("//*[local-name()='shadow']/*[local-name()='srgbClr' or local-name()='schemeClr']", doc, XPathConstants.NODE); + if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element ele = (org.w3c.dom.Element) node; + + // 打印属性(调试用) + org.w3c.dom.NamedNodeMap attrs = ele.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node attr = attrs.item(i); + System.out.println(attr.getNodeName() + " = " + attr.getNodeValue()); + } + + // 获取颜色值 + String colorVal = ele.getAttribute("w14:val"); + if (colorVal == null || colorVal.isEmpty()) { + colorVal = ele.getAttribute("val"); // 兼容没有命名空间的情况 + } + + if (colorVal != null && !colorVal.isEmpty()) { + + return ColorUtils.getChineseColorName(colorVal); // 返回大写十六进制颜色 + } + } + } + return "无"; + } + + // 获取阴影透明度 + public static String getTextShadowAlpha(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + XPath xpath = XPathFactory.newInstance().newXPath(); + + // 递归查找 shadow 下所有 alpha 元素 + Node node = (Node) xpath.evaluate("//*[local-name()='shadow']//*[local-name()='alpha']", doc, XPathConstants.NODE); + if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element ele = (org.w3c.dom.Element) node; + + // 遍历并打印属性 + org.w3c.dom.NamedNodeMap attrs = ele.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node attr = attrs.item(i); + System.out.println(attr.getNodeName() + " = " + attr.getNodeValue()); + } + + // 尝试获取 w14:val 或 val + String valStr = ele.getAttribute("w14:val"); + if (valStr == null || valStr.isEmpty()) { + valStr = ele.getAttribute("val"); + } + if (valStr != null && !valStr.isEmpty()) { + try { + double val = Double.parseDouble(valStr); + // 转换为百分比 + double percent = val / 1000.0; + return new BigDecimal(percent).setScale(1, RoundingMode.HALF_UP) + "%"; + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + return "无"; + } + + + // 获取阴影模糊(返回磅) + public static String getTextShadowBlur(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + XPath xpath = XPathFactory.newInstance().newXPath(); + + // 查找 shadow 元素 + Node node = (Node) xpath.evaluate("//*[local-name()='shadow']", doc, XPathConstants.NODE); + if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element ele = (org.w3c.dom.Element) node; + + // 获取 blurRad 属性 + String valStr = ele.getAttribute("w14:blurRad"); + if (valStr == null || valStr.isEmpty()) { + valStr = ele.getAttribute("blurRad"); + } + + if (valStr != null && !valStr.isEmpty()) { + try { + double blurRad = Double.parseDouble(valStr); + double blurPt = blurRad / 12700.0; // EMU -> pt + return new BigDecimal(blurPt).setScale(1, RoundingMode.HALF_UP).toPlainString() + "磅"; + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + return "无"; + } + + + // 获取阴影距离(返回磅) + public static String getTextShadowDistance(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + XPath xpath = XPathFactory.newInstance().newXPath(); + + // 查找 shadow 元素 + Node node = (Node) xpath.evaluate("//*[local-name()='shadow']", doc, XPathConstants.NODE); + if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element ele = (org.w3c.dom.Element) node; + + String distVal = ele.getAttribute("w14:dist"); + if (distVal == null || distVal.isEmpty()) { + distVal = ele.getAttribute("dist"); + } + + if (distVal != null && !distVal.isEmpty()) { + try { + double distEmu = Double.parseDouble(distVal); + double distPt = distEmu / 12700.0; // EMU -> 磅 + return new BigDecimal(distPt).setScale(1, RoundingMode.HALF_UP).toPlainString() + "pt"; + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + return "无"; + } + + + // 获取阴影大小(返回%) + public static String getTextShadowSize(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + XPath xpath = XPathFactory.newInstance().newXPath(); + + // 查找 shadow 元素 + Node node = (Node) xpath.evaluate("//*[local-name()='shadow']", doc, XPathConstants.NODE); + if (node != null && node.getNodeType() == Node.ELEMENT_NODE) { + org.w3c.dom.Element ele = (org.w3c.dom.Element) node; + + String sxVal = ele.getAttribute("w14:sx"); + if (sxVal == null || sxVal.isEmpty()) { + sxVal = ele.getAttribute("sx"); + } + + if (sxVal != null && !sxVal.isEmpty()) { + try { + double sx = Double.parseDouble(sxVal); + double percent = sx / 1000.0; // 100000 -> 100% + return new BigDecimal(percent).setScale(1, RoundingMode.HALF_UP).toPlainString() + "%"; + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + return "无"; + } + + + + + + //字体 字号 public static String printFontSizesInTextbox(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { System.out.println("Anchor or GraphicData is null"); @@ -404,6 +1761,182 @@ public class TextInfo { return null; } + //字体 字体颜色 + public static String getFontAndColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws Exception { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) return "无"; + + Object graphicDataObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object graphicData = (graphicDataObj instanceof JAXBElement) ? ((JAXBElement) graphicDataObj).getValue() : graphicDataObj; + + if (!graphicData.getClass().getSimpleName().equals("CTWordprocessingShape")) return "无"; + + Field txbxField = graphicData.getClass().getDeclaredField("txbx"); + txbxField.setAccessible(true); + Object txbx = txbxField.get(graphicData); + if (txbx == null) return "无"; + + Field contentField = txbx.getClass().getDeclaredField("txbxContent"); + contentField.setAccessible(true); + List paras = (List) contentField.get(txbx); + + for (Object pObj : paras) { + if (pObj instanceof P) { + P p = (P) pObj; + List runs = p.getContent(); + for (Object rObj : runs) { + if (rObj instanceof R) { + R r = (R) rObj; + RPr rPr = r.getRPr(); + if (rPr != null) { + String font = rPr.getRFonts() != null ? rPr.getRFonts().getAscii() : "默认字体"; + String color = rPr.getColor() != null ? rPr.getColor().getVal() : "默认颜色"; + return color; + } + } + } + } + } + return "无"; + } + + + + // 获取字体颜色 + public static String getFontColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + if (rPr != null && rPr.getColor() != null) { + Color color = rPr.getColor(); + return ColorUtils.getChineseColorName(String.valueOf(color.getVal())); + } + return "默认颜色"; + } + + // 获取是否加粗 + public static String getBold(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + if (rPr != null && rPr.getB() != null && rPr.getB().isVal()) { + return "加粗"; + } + return "不加粗"; + } + + // 获取倾斜 + public static String getItalic(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + if (rPr != null && rPr.getI() != null && rPr.getI().isVal()) { + return "倾斜"; + } + return "不倾斜"; + } + + // 获取下划线线型 + + public static String getUnderlineType(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + if (rPr != null && rPr.getU() != null) { + U u = rPr.getU(); + if (u.getVal() != null) { + return u.getVal().toString(); // 返回下划线类型,如 "single", "double" 等 + } + } + return "无下划线"; + } + + + // 获取下划线颜色 + public static String getUnderlineColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + if (rPr != null && rPr.getU() != null && rPr.getU().getColor() != null) { + return ColorUtils.getChineseColorName(rPr.getU().getColor()); + } + return "无"; + } + + // 着重号 + public static String getEmphasis(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getEm() != null) ? "是" : "否"; + } + + // 删除线 + public static String getStrike(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getStrike() != null) ? "是" : "否"; + } + + // 双删除线 + public static String getDstrike(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getDstrike() != null) ? "是" : "否"; + } + + // 上标 + public static String getVertAlignSuperscript(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getVertAlign() != null && rPr.getVertAlign().getVal() == STVerticalAlignRun.SUPERSCRIPT) + ? "是" : "否"; + } + + // 下标 + public static String getVertAlignSubscript(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getVertAlign() != null && rPr.getVertAlign().getVal() == STVerticalAlignRun.SUBSCRIPT) + ? "是" : "否"; + } + + // 小型大写字母 + public static String getSmallCaps(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getSmallCaps() != null) ? "是" : "否"; + } + + // 全部大写字母 + public static String getCaps(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getCaps() != null) ? "是" : "否"; + } + + // 字符间距→缩放 + public static String getSpacingScale(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getW() != null) ? rPr.getW().getVal() + "%" : "无"; + } + + // 字符间距→间距 + public static String getSpacing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getSpacing() != null) + ? (rPr.getSpacing().getVal().doubleValue() / 20.0) + " 磅" + : "无"; + } + + // 字符间距→位置 + public static String getPosition(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getPosition() != null) + ? (rPr.getPosition().getVal().doubleValue() / 2.0) + " 磅" + : "无"; + } + // 字体→中文字体 + public static String getEastAsiaFont(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getRFonts() != null && rPr.getRFonts().getEastAsia() != null) + ? rPr.getRFonts().getEastAsia() + : "无"; + } + + // 字体→西文字体 + public static String getAsciiFont(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + RPr rPr = getRunProperties(anchor); + return (rPr != null && rPr.getRFonts() != null && rPr.getRFonts().getAscii() != null) + ? rPr.getRFonts().getAscii() + : "无"; + } + + + + + // 根据Run和wordMLPackage样式表,尝试获取字号名称(三号、四号) private static String getFontSizeNameByRunAndStyles(R run, WordprocessingMLPackage wordMLPackage) { if (run == null || wordMLPackage == null) return null; @@ -521,6 +2054,47 @@ public class TextInfo { default: return "无"; } } + + // 辅助方法:从Anchor中获取阴影效果对象 + private static CTOuterShadowEffect getOuterShadow(Anchor anchor) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + if (anyObj instanceof JAXBElement) { + anyObj = ((JAXBElement) anyObj).getValue(); + } + + if (!(anyObj instanceof CTWordprocessingShape)) { + return null; + } + + CTWordprocessingShape shape = (CTWordprocessingShape) anyObj; + CTShapeProperties spPr = shape.getSpPr(); + if (spPr != null && spPr.getEffectLst() != null) { + return spPr.getEffectLst().getOuterShdw(); + } + + return null; + } + + + + private static String getDashStyleName(String val) { + switch(val) { + case "solid": return "实线"; + case "dot": return "点线"; + case "dash": return "虚线"; + case "lgDash": return "长虚线"; + case "dashDot": return "点划线"; + case "lgDashDot": return "长点划线"; + case "sysDash": return "系统虚线"; + case "sysDot": return "系统点线"; + case "sysDashDot": return "系统点划线"; + default: return val; + } + } public static String getTextDirection(String vert) { if (vert == null) return "水平(默认)"; @@ -557,4 +2131,218 @@ public class TextInfo { return "水平(默认)"; } } + private static Integer tryGetVal(Object alphaObj) { + if (alphaObj == null) return null; + try { + Method getValMethod = alphaObj.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(alphaObj); + if (valObj instanceof Number) { + return ((Number) valObj).intValue(); + } + } catch (Exception e) { + // ignore + } + return null; + } + private static Object tryGetAlpha(Object prstClr) { + try { + Method getAlphaMethod = prstClr.getClass().getMethod("getAlpha"); + return getAlphaMethod.invoke(prstClr); + } catch (Exception e) { + // ignore + } + try { + Method getAlphaListMethod = prstClr.getClass().getMethod("getAlphaList"); + List list = (List) getAlphaListMethod.invoke(prstClr); + if (list != null && !list.isEmpty()) { + return list.get(0); + } + } catch (Exception e) { + // ignore + } + return null; + } + private static String formatAlphaValue(int val) { + double transparency = 100.0 - val / 1000.0; + if (transparency < 0) transparency = 0; + if (transparency > 100) transparency = 100; + return String.format("%.1f%%", transparency); + } + private static String getLineTypeName(String val) { + switch (val) { + case "sng": return "单线"; + case "dbl": return "双线"; + case "tri": return "三重线"; + default: return val; + } + } + // 获取倒影对象 + private static CTReflectionEffect getReflectionEffect(Anchor anchor) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0); + if (graphicData instanceof JAXBElement) { + graphicData = ((JAXBElement) graphicData).getValue(); + } + + CTShapeProperties spPr = null; + + try { + // 图片类型 + if (graphicData instanceof Pic) { + Pic pic = (Pic) graphicData; + spPr = pic.getSpPr(); + } + // 文本框类型 + else if (graphicData.getClass().getName().contains("CTWordprocessingShape")) { + Method getSpPrMethod = graphicData.getClass().getMethod("getSpPr"); + spPr = (CTShapeProperties) getSpPrMethod.invoke(graphicData); + } + + if (spPr == null || spPr.getEffectLst() == null) return null; + + // 获取倒影效果 + return spPr.getEffectLst().getReflection(); + + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + public static CTShapeProperties getShapeProperties(Anchor anchor) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null) { + System.out.println("Anchor 或 Graphic 为空"); + return null; + } + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + if (anyList == null || anyList.isEmpty()) { + System.out.println("GraphicData 中没有任何对象"); + return null; + } + + for (Object obj : anyList) { + Object value = obj instanceof JAXBElement ? ((JAXBElement) obj).getValue() : obj; + + // 判断是否为 org.docx4j.dml.picture.Pic 类型 + if (value instanceof org.docx4j.dml.picture.Pic) { + org.docx4j.dml.picture.Pic pic = (org.docx4j.dml.picture.Pic) value; + CTShapeProperties spPr = pic.getSpPr(); + + if (spPr == null) { + System.out.println("Pic 中 spPr 为 null"); + } else { + System.out.println("成功获取 spPr"); + } + + return spPr; + } else { + System.out.println("未识别的类型: " + value.getClass().getName()); + } + } + + return null; + } + public static String extractColor(CTColor ctColor) { + if (ctColor == null) return "无色"; + + if (ctColor.getSrgbClr() != null) { + CTSRgbColor srgb = ctColor.getSrgbClr(); + byte[] val = srgb.getVal().getBytes(); + if (val != null && val.length == 3) { + // 转成16进制字符串 + return String.format("#%02X%02X%02X", val[0], val[1], val[2]); + } + } + + if (ctColor.getSchemeClr() != null) { + // 主题色,直接返回名字或标识 + return ctColor.getSchemeClr().getVal().value() ; + } + + if (ctColor.getPrstClr() != null) { + // 预设颜色 + return ctColor.getPrstClr().getVal().value() ; + } + + return "未知色"; + } + + // 辅助方法:解析 alpha 百分比 + private static String parseAlphaFromColor(List transforms) { + if (transforms == null) return null; + for (Object t : transforms) { + Object valObj = t; + if (t instanceof JAXBElement) { + JAXBElement jaxb = (JAXBElement) t; + valObj = jaxb.getValue(); + if (!"alpha".equals(jaxb.getName().getLocalPart())) continue; + } + + try { + Method getValMethod = valObj.getClass().getMethod("getVal"); + Object val = getValMethod.invoke(valObj); + if (val instanceof Number) { + double alpha = ((Number) val).doubleValue(); + double transparency = 100.0 - alpha / 1000.0; // 转成百分比 + return String.format("%.1f%%", transparency); + } + } catch (Exception ignored) {} + } + return null; + } + + // 辅助方法:获取文本框第一个文字的 RPr(运行属性) + private static RPr getRunProperties(Anchor anchor) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null) { + return null; + } + + try { + Object graphicDataObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object graphicData = (graphicDataObj instanceof JAXBElement) + ? ((JAXBElement) graphicDataObj).getValue() + : graphicDataObj; + + if (!(graphicData instanceof CTWordprocessingShape)) { + return null; + } + + CTWordprocessingShape wsp = (CTWordprocessingShape) graphicData; + CTTextboxInfo txbx = wsp.getTxbx(); + if (txbx == null || txbx.getTxbxContent() == null) return null; + + CTTxbxContent content = txbx.getTxbxContent(); + List paragraphs = content.getEGBlockLevelElts(); + + for (Object obj : paragraphs) { + if (obj instanceof P) { + P p = (P) obj; + for (Object o : p.getContent()) { + if (o instanceof R) { + R r = (R) o; + return r.getRPr(); // 返回第一个运行属性 + } + } + } + } + + + + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/utils/ColorUtils.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/utils/ColorUtils.java index c6dbe51b..9e844661 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/utils/ColorUtils.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/utils/ColorUtils.java @@ -37,6 +37,7 @@ public class ColorUtils { COLOR_MAP.put("006600", "深绿色"); COLOR_MAP.put("99cc99", "浅绿色"); COLOR_MAP.put("00b0f0", "浅蓝色"); + COLOR_MAP.put("0070c0", "蓝色"); // 16进制颜色代码映射(统一小写) @@ -60,6 +61,7 @@ public class ColorUtils { COLOR_MAP.put("ffd700", "金色"); COLOR_MAP.put("e54c5e", "金色"); COLOR_MAP.put("00b050", "绿色"); + COLOR_MAP.put("ffc000", "橙色");