diff --git a/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/controller/admin/wps/ExamWpsXlsxController.java b/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/controller/admin/wps/ExamWpsXlsxController.java index 148b47f4..784dd8b3 100644 --- a/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/controller/admin/wps/ExamWpsXlsxController.java +++ b/exam-module-exam/exam-module-exam-biz/src/main/java/pc/exam/pp/module/exam/controller/admin/wps/ExamWpsXlsxController.java @@ -78,6 +78,9 @@ public class ExamWpsXlsxController { @GetMapping("/getByNameList") public CommonResult> getXlsxByNameList(@RequestParam("title") String title) { ExamWpsXlsx xlsx = examWpsXlsxService.getXlsxByTitle(title); + if (xlsx==null){ + return success(null ); + } return success(BeanUtils.toBean(examWpsXlsxService.getChildXlsxList(xlsx.getId()), XlsxRespVO.class)); } } diff --git a/exam-module-judgement/exam-module-judgement-biz/pom.xml b/exam-module-judgement/exam-module-judgement-biz/pom.xml index c11c1b7d..38d2b989 100644 --- a/exam-module-judgement/exam-module-judgement-biz/pom.xml +++ b/exam-module-judgement/exam-module-judgement-biz/pom.xml @@ -170,6 +170,11 @@ docx4j-JAXB-Internal 8.3.9 + + org.docx4j + docx4j-JAXB-ReferenceImpl + 11.4.9 + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/AutoWpsController.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/AutoWpsController.java index 200ed9c3..4a2dc3e1 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/AutoWpsController.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/AutoWpsController.java @@ -6,18 +6,18 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import pc.exam.pp.framework.common.pojo.CommonResult; import pc.exam.pp.module.infra.controller.admin.file.vo.file.FileUploadReqVO; import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsDocxInfoVo; import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsSlideInfoVo; +import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsXlsxInfoVo; import pc.exam.pp.module.judgement.service.wps_excel.JudgementWpsExcelService; import pc.exam.pp.module.judgement.service.wps_pptx.JudgementWpsPptxService; import pc.exam.pp.module.judgement.service.wps_word.JudgementWpsWordService; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.vo.JudgementXlsxVO; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.vo.XlsxDataInfoVO; import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.vo.JudgementSlidesVO; import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.vo.SlideDataInfoVO; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.DocxDataInfoVO; @@ -65,6 +65,16 @@ public class AutoWpsController { return CommonResult.success(judgementWpsPptxService.docxDataInfo(file.getFile())); } + /** + * wps xlsx + * + * @return 获取大类 + */ + @PostMapping("/xlsxDataInfo") + public CommonResult> runXlsxSlide(FileUploadReqVO file) throws Exception { + return CommonResult.success(judgementWpsExcelService.xlsxDataInfo(file.getFile())); + } + /** * 获取指定考点的数据 * @@ -99,5 +109,24 @@ public class AutoWpsController { ); return CommonResult.success(judgementWpsPptxService.slideMaster(wpsSlideInfoVos, file)); } + /** + * 获取指定考点的数据 + * + * @return + * @throws Exception + */ + @PostMapping(value = "/xlsxMaster", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public CommonResult> xlsxMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file,@RequestParam(value = "cell", required = false) List cell) throws Exception { + // 手动解析JSON数组 + ObjectMapper objectMapper = new ObjectMapper(); + List wpsXlsxInfoVos= objectMapper.readValue( + jsonData, + new TypeReference>() { + } + ); + return CommonResult.success(judgementWpsExcelService.xlsxMaster(wpsXlsxInfoVos, file,cell)); + } + + } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelService.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelService.java index ae985085..06d06042 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelService.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelService.java @@ -15,7 +15,7 @@ import java.util.List; */ public interface JudgementWpsExcelService { - List xlsxMaster(List wpsXlsxInfoVos, MultipartFile file) throws Exception; + List xlsxMaster(List wpsXlsxInfoVos, MultipartFile file,List cell) throws Exception; List xlsxDataInfo(MultipartFile file) throws Exception; } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelServiceImpl.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelServiceImpl.java index 704095ee..ddd7c56a 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelServiceImpl.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/service/wps_excel/JudgementWpsExcelServiceImpl.java @@ -17,8 +17,8 @@ public class JudgementWpsExcelServiceImpl implements JudgementWpsExcelService { @Override - public List xlsxMaster(List wpsXlsxInfoVos, MultipartFile file) throws Exception { - return XlsxMaster.xlsxMaster(wpsXlsxInfoVos, file); + public List xlsxMaster(List wpsXlsxInfoVos, MultipartFile file,List cell) throws Exception { + return XlsxMaster.xlsxMaster(wpsXlsxInfoVos, file,cell); } @Override diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxConversion.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxConversion.java index 844251ea..a4fcb24a 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxConversion.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/XlsxConversion.java @@ -1,9 +1,12 @@ package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j; import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.apache.poi.xssf.usermodel.*; import org.apache.xmlbeans.XmlCursor; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; import org.springframework.web.multipart.MultipartFile; import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.vo.XlsxDataInfoVO; @@ -13,43 +16,119 @@ import java.util.List; import java.util.Random; public class XlsxConversion { - // 大纲 - // 1、获取第一层目录 幻灯片 - 母版 - 设置 - 其他 - // 2、获取第二层目录 第x页 - 幻灯片母版/备注母版 - 幻灯片设置 - 文件操作 - // 3、获取第三层目录 幻灯片设置/形状 - 幻灯片母版 - 幻灯片设置 - 文件操作 + public static List XlsxDataInfos(MultipartFile file) throws Exception { List dataInfoVOS = new ArrayList<>(); - try (InputStream inputStream = file.getInputStream(); - OPCPackage pkg = OPCPackage.open(inputStream); + try (InputStream inputStream = file.getInputStream(); + OPCPackage pkg = OPCPackage.open(inputStream); XSSFWorkbook workbook = new XSSFWorkbook(pkg)) { + // 获取有多少个工作表 int sheetNumber = workbook.getNumberOfSheets(); String firstId = getStringRandom(); setXlsxDataInfo(firstId, "", "工作表", "sheet", "", false, dataInfoVOS); - for (int i = 0; i < sheetNumber; i++) { + + for (int sheetIdx = 0; sheetIdx < sheetNumber; sheetIdx++) { // 获取工作表内容 - XSSFSheet sheetXss = workbook.getSheetAt(i); + XSSFSheet sheetXss = workbook.getSheetAt(sheetIdx); String secondId = getStringRandom(); - setXlsxDataInfo(secondId, firstId, "sheet" + i + 1, "sheetInfo", "", true, dataInfoVOS); + setXlsxDataInfo(secondId, firstId, + sheetXss.getSheetName() + ":" + (sheetIdx + 1), + "sheetInfo", "", true, dataInfoVOS); + String thirdId = getStringRandom(); // 常用方法 - setXlsxDataInfo(thirdId, secondId, "单元格", "cell", "", true, dataInfoVOS); - setXlsxDataInfo(thirdId, secondId, "范围", "range", "", true, dataInfoVOS); - setXlsxDataInfo(thirdId, secondId, "行", "row", "", true, dataInfoVOS); - setXlsxDataInfo(thirdId, secondId, "列", "col", "", true, dataInfoVOS); - setXlsxDataInfo(thirdId, secondId, "页面", "page", "", true, dataInfoVOS); - setXlsxDataInfo(thirdId, secondId, "视图", "view", "", true, dataInfoVOS); - setXlsxDataInfo(thirdId, secondId, "属性", "style", "", true, dataInfoVOS); + setXlsxDataInfo(thirdId, secondId, "单元格", "cell", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + setXlsxDataInfo(thirdId, secondId, "范围", "range", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + setXlsxDataInfo(thirdId, secondId, "行", "row", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + setXlsxDataInfo(thirdId ,secondId, "列", "col", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + + // 表格需查询有多少个 + List tables = sheetXss.getTables(); + if (tables != null && !tables.isEmpty()) { + String tableId = getStringRandom(); + setXlsxDataInfo(tableId ,secondId, "表格", null, String.valueOf(sheetIdx + 1), true, dataInfoVOS); + for (int t = 0; t < tables.size(); t++) { + setXlsxDataInfo( + getStringRandom(), + tableId, + "表格:" +(t + 1), + "table", + String.valueOf(sheetIdx + 1), + true, + dataInfoVOS + ); + } + } + + setXlsxDataInfo(thirdId, secondId, "页面设置", "page", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + setXlsxDataInfo(thirdId, secondId, "视图", "view", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + setXlsxDataInfo(thirdId, secondId, "属性", "style", String.valueOf(sheetIdx + 1), true, dataInfoVOS); + //数据透视表 + // 数据透视表 + PackagePart sheetPart = sheetXss.getPackagePart(); + PackageRelationshipCollection rels = sheetPart.getRelationships(); + + List pivotRels = new ArrayList<>(); + for (PackageRelationship rel : rels) { + String relType = rel.getRelationshipType(); + if (relType != null && (relType.endsWith("/pivotCacheDefinition") || relType.endsWith("/pivotTable"))) { + pivotRels.add(rel); + } + } + + if (!pivotRels.isEmpty()) { + String pivotId = getStringRandom(); + setXlsxDataInfo(pivotId, secondId, "数据透视表", null, String.valueOf(sheetIdx + 1), true, dataInfoVOS); + for (int p = 0; p < pivotRels.size(); p++) { + setXlsxDataInfo( + getStringRandom(), + pivotId, + "数据透视表:" + (p + 1), + "pivotTable", + String.valueOf(sheetIdx + 1), + true, + dataInfoVOS + ); + } + } + + + // 图表 + XSSFDrawing drawing = sheetXss.getDrawingPatriarch(); + if (drawing != null) { + List charts = drawing.getCharts(); + if (charts != null && !charts.isEmpty()) { + String chartId = getStringRandom(); + setXlsxDataInfo(chartId, secondId, "图表", null, String.valueOf(sheetIdx + 1), true, dataInfoVOS); + for (int c = 0; c < charts.size(); c++) { + XSSFChart chart = charts.get(c); + String title = chart.getTitleText() != null ? chart.getTitleText().getString() : "无标题"; + setXlsxDataInfo( + getStringRandom(), + chartId, + "图表:" + (c + 1), + "chart", + String.valueOf(sheetIdx + 1), + true, + dataInfoVOS + ); + } + } + } + + //图表 + // 剩余需要查询xml,特定文件存在才会显示 } - } catch (Exception e) { throw new RuntimeException(e); } return dataInfoVOS; } + public static String getStringRandom() { String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); 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 e6e73860..7da36be3 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 @@ -1,25 +1,39 @@ package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFPivotTable; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFTable; import org.docx4j.openpackaging.exceptions.Docx4JException; -import org.docx4j.openpackaging.packages.OpcPackage; -import org.docx4j.openpackaging.packages.PresentationMLPackage; import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; import org.docx4j.openpackaging.parts.Part; -import org.docx4j.openpackaging.parts.PresentationML.SlidePart; import org.docx4j.openpackaging.parts.SpreadsheetML.WorkbookPart; import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; -import org.pptx4j.pml.GroupShape; -import org.pptx4j.pml.Shape; import org.springframework.web.multipart.MultipartFile; import org.xlsx4j.sml.*; import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsXlsxInfoVo; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.chart.ChartHandler; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.page.PagIng; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.pivotTable.PivotTabling; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.row.RowIng; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.col.ColIng; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.range.RangIng; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.cell.CellIng; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.style.Style; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.table.TableIng; +import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.view.Viewing; import pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.vo.JudgementXlsxVO; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class XlsxMaster { @@ -31,7 +45,7 @@ public class XlsxMaster { * @throws IOException IO * @throws Docx4JException 异常 */ - public static List xlsxMaster(List wpsXlsxInfoVos, MultipartFile file) throws IOException, Docx4JException { + public static List xlsxMaster(List wpsXlsxInfoVos, MultipartFile file,List cell) throws IOException, Docx4JException { List judgementXlsxVOS = new ArrayList<>(); // 1、获取想要判断的文件地址(文件流) try (InputStream inputStream = file.getInputStream()) { @@ -40,22 +54,335 @@ public class XlsxMaster { WorkbookPart wb = pkg.getWorkbookPart(); // 1) 先从 workbook 的 里拿到每个 sheet 的 r:id 和 name List sheets = wb.getJaxbElement().getSheets().getSheet(); - for (Sheet s : sheets) { - String rId = s.getId(); // 关系ID - String sheetName = s.getName();// 工作表名称 +// 先用 POI 打开 Workbook + InputStream is = file.getInputStream(); + org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(is); // 这里确保是POI的Workbook + System.out.println(cell); - // 2) 用 r:id 到关系表里取真正的 WorksheetPart - Part part = wb.getRelationshipsPart().getPart(rId); - WorksheetPart wsp = (WorksheetPart) part; - System.out.println("=== Sheet: " + sheetName); + for (WpsXlsxInfoVo wpsXlsxInfoVo : wpsXlsxInfoVos) { + // 参数实例化 + // 大类名称 + String firstName = wpsXlsxInfoVo.getFirstName(); + // 序号 + String indexParm = wpsXlsxInfoVo.getIndex(); + // 方法名称 + String function = wpsXlsxInfoVo.getFunction(); + // 考点名称 + String examName = wpsXlsxInfoVo.getExamName(); + // 考点代码 + String examCode = wpsXlsxInfoVo.getExamCode(); + String docxFunction = firstName + "@" + indexParm + "@" + function + "@" + examName + "@" + examCode; - // 3) 读行列 - for (Row row : wsp.getJaxbElement().getSheetData().getRow()) { - for (Cell c : row.getC()) { - System.out.print(readCellValue(c, wb) + "\t"); + + // 创建一个索引ID,跟参数中index进行匹配 + for (Sheet s : sheets) { + String sheetName = s.getName(); + String rId = s.getId(); + String number = rId.replaceAll("\\D+", ""); + if (firstName.contains("单元格")) { + if (indexParm.equals(number)) { + int sheetNum = Integer.parseInt(number); + + Part part = wb.getRelationshipsPart().getPart(rId); + WorksheetPart wsp = (WorksheetPart) part; + + CellIng excelFunctions = new CellIng(); + Class[] paramTypes = { + org.apache.poi.ss.usermodel.Cell.class, + org.apache.poi.ss.usermodel.Workbook.class, + }; + Method methodWithArgs = excelFunctions.getClass().getMethod(function, paramTypes); + + // **这里对每个传入的单元格循环查找处理** + if (cell != null && !cell.isEmpty()) { + for (String cellRef : cell) { + org.apache.poi.ss.usermodel.Cell poiCell = getPoiCellFromRef(workbook, sheetName, cellRef); + if (poiCell == null) continue; + String value = (String) methodWithArgs.invoke(excelFunctions, poiCell, workbook); + + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【"+sheetName+"】"+docxFunction +"【"+cellRef+"】"+ "@" + value, + "【"+sheetName+"】"+firstName +"【"+cellRef+"】"+ examName + value + ); + } + } + } + } } - System.out.println(); + if (firstName.contains("范围")) { + if (indexParm.equals(number)) { + RangIng rangingFunctions = new RangIng(); + Class[] paramTypes = { + String.class, + org.apache.poi.ss.usermodel.Workbook.class, + int.class + }; + Method methodWithArgs = rangingFunctions.getClass().getMethod(function, paramTypes); + int sheetNum = Integer.parseInt(number); + if (cell != null && !cell.isEmpty()) { + for (String rangeStr : cell) { + // 拆解范围,得到所有单元格地址列表 +// List cellRefs = getCellRefsInRange(rangeStr); +// for (String cellRef : cellRefs) { + String value = (String) methodWithArgs.invoke(rangingFunctions, rangeStr, workbook,sheetNum); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "【" + rangeStr + "】@" + value, + "【" + sheetName + "】" + firstName + "【" + rangeStr + "】" + examName + value + ); + } +// } + } + } + } + } + + if (firstName.contains("行")) { + if (indexParm.equals(number)) { + RowIng hangIng = new RowIng(); + Class[] paramTypes = { + String.class, + org.apache.poi.ss.usermodel.Workbook.class, + int.class + }; + Method methodWithArgs = hangIng.getClass().getMethod(function, paramTypes); + int sheetNum = Integer.parseInt(number); + // cell是前端传进的 ,1 ,2 ,3 + if (cell != null && !cell.isEmpty()) { + for (String rangeStr : cell) { + String value = (String) methodWithArgs.invoke(hangIng, rangeStr, workbook,sheetNum); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "【" + rangeStr + "】@" + value, + "【" + sheetName + "】" + firstName + "【" + rangeStr + "】" + examName + value + ); + } + } + } + + } + } + if (firstName.contains("列")) { + if (indexParm.equals(number)) { + ColIng hangIng = new ColIng(); + Class[] paramTypes = { + String.class, + org.apache.poi.ss.usermodel.Workbook.class, + int.class + }; + Method methodWithArgs = hangIng.getClass().getMethod(function, paramTypes); + int sheetNum = Integer.parseInt(number); + // cell是前端传进的 ,1 ,2 ,3 + if (cell != null && !cell.isEmpty()) { + for (String rangeStr : cell) { + String value = (String) methodWithArgs.invoke(hangIng, rangeStr, workbook,sheetNum); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "【" + rangeStr + "】@" + value, + "【" + sheetName + "】" + firstName + "【" + rangeStr + "】" + examName + value + ); + } + } + } + + } + + } + if (firstName.contains("页面")) { + if (indexParm.equals(number)) { + PagIng rangingFunctions = new PagIng(); + Class[] paramTypes = { + org.apache.poi.ss.usermodel.Workbook.class, + int.class + }; + Method methodWithArgs = rangingFunctions.getClass().getMethod(function, paramTypes); + int sheetNum = Integer.parseInt(number); + + String value = (String) methodWithArgs.invoke(rangingFunctions,workbook,sheetNum); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + firstName + examName + value + ); + } + } + } + if (firstName.contains("表格")) { + Pattern pattern = Pattern.compile("【表格:(\\d+)】"); + Matcher matcher = pattern.matcher(firstName); + if (!matcher.find()) continue; + + String tableIdStr = matcher.group(1); + long tableIndex = Long.parseLong(tableIdStr); + if (indexParm.equals(number)) { + int sheetNum = Integer.parseInt(number); // 页码索引,从1开始 + org.apache.poi.ss.usermodel.Sheet poiSheet = workbook.getSheetAt(sheetNum - 1); + + if (poiSheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) poiSheet; + List tables = xssfSheet.getTables(); + for (int i = 0; i < tables.size(); i++) { + + if ((i+1) == tableIndex) { + TableIng tableIng = new TableIng(); + + Class[] paramTypes = { XSSFTable.class, org.apache.poi.ss.usermodel.Workbook.class }; + Method methodWithArgs = tableIng.getClass().getMethod(function, paramTypes); + + String value = (String) methodWithArgs.invoke(tableIng, tables.get(i), workbook); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + firstName + examName + value + ); + } + } + } + } + } + } + if (firstName.contains("视图")) { + if (indexParm.equals(number)) { + Viewing rangingFunctions = new Viewing(); + Class[] paramTypes = { + org.apache.poi.ss.usermodel.Workbook.class, + int.class + }; + Method methodWithArgs = rangingFunctions.getClass().getMethod(function, paramTypes); + int sheetNum = Integer.parseInt(number); + + String value = (String) methodWithArgs.invoke(rangingFunctions,workbook,sheetNum); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + firstName + examName + value + ); + } + } + } + + + if (firstName.contains("数据透视表")) { + Pattern pattern = Pattern.compile("【数据透视表:(\\d+)】"); + Matcher matcher = pattern.matcher(firstName); + if (!matcher.find()) continue; + + String tableIdStr = matcher.group(1); + long tableIndex = Long.parseLong(tableIdStr); + if (indexParm.equals(number)) { + int sheetNum = Integer.parseInt(number); // 页码索引,从1开始 + org.apache.poi.ss.usermodel.Sheet poiSheet = workbook.getSheetAt(sheetNum - 1); + + if (poiSheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) poiSheet; + List pivotTables = xssfSheet.getPivotTables(); + for (int i = 0; i < pivotTables.size(); i++) { + + if ((i+1) == tableIndex) { + PivotTabling pivotTabling = new PivotTabling(); + + Class[] paramTypes = { XSSFPivotTable.class, org.apache.poi.ss.usermodel.Workbook.class }; + Method methodWithArgs = pivotTabling.getClass().getMethod(function, paramTypes); + + String value = null; + try { + value = (String) methodWithArgs.invoke(pivotTabling, pivotTables.get(i), workbook); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + cause.printStackTrace(); // 打印真实异常栈信息 + } catch (Exception e) { + e.printStackTrace(); + } + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + firstName + examName + value + ); + } + } + } + } + } + } + if (firstName.contains("图表")) { + Pattern pattern = Pattern.compile("【图表:(\\d+)】"); + Matcher matcher = pattern.matcher(firstName); + if (!matcher.find()) continue; + + String chartIdStr = matcher.group(1); + long chartIndex = Long.parseLong(chartIdStr); + if (indexParm.equals(number)) { + int sheetNum = Integer.parseInt(number); // 页码索引,从1开始 + org.apache.poi.ss.usermodel.Sheet poiSheet = workbook.getSheetAt(sheetNum - 1); + + if (poiSheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) poiSheet; + List charts = xssfSheet.getDrawingPatriarch() != null + ? xssfSheet.getDrawingPatriarch().getCharts() + : null; + + if (charts != null) { + for (int i = 0; i < charts.size(); i++) { + if ((i + 1) == chartIndex) { + ChartHandler chartHandler = new ChartHandler(); + + Class[] paramTypes = { XSSFChart.class, XSSFSheet.class }; + Method methodWithArgs = chartHandler.getClass().getMethod(function, paramTypes); + + String value = null; + try { + value = (String) methodWithArgs.invoke(chartHandler, charts.get(i), xssfSheet); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + cause.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + firstName + examName + value + ); + } + } + } + } + } + } + } + if (firstName.contains("属性")) { + if (indexParm.equals(number)) { + Style rangingFunctions = new Style(); + Class[] paramTypes = { + org.apache.poi.ss.usermodel.Workbook.class, + int.class + }; + Method methodWithArgs = rangingFunctions.getClass().getMethod(function, paramTypes); + int sheetNum = Integer.parseInt(number); + + String value = (String) methodWithArgs.invoke(rangingFunctions,workbook,sheetNum-1); + if (value != null) { + judgementXlsxVOS = setJudgementXlsx( + judgementXlsxVOS, + "【" + sheetName + "】" + docxFunction + "】@" + value, + "【" + sheetName + "】" + firstName + examName + value + ); + } + } + } + + } } @@ -66,6 +393,17 @@ public class XlsxMaster { return judgementXlsxVOS; } + + private static List setJudgementXlsx(List judgementXlsxVOList, String content, String contentIn) { + JudgementXlsxVO judgementXlsxVO = new JudgementXlsxVO(); + judgementXlsxVO.setContent(content); + judgementXlsxVO.setContentIn(contentIn); + judgementXlsxVOList.add(judgementXlsxVO); + return judgementXlsxVOList; + } + + + // 读取单元格值(含共享字符串) private static String readCellValue(Cell cell, WorkbookPart wb) { if (cell.getT() == STCellType.S) { // 共享字符串 @@ -82,4 +420,37 @@ public class XlsxMaster { // 其他类型:直接返回底层值(数字/日期序列号/布尔等) return cell.getV() == null ? "" : cell.getV(); } + private static org.apache.poi.ss.usermodel.Cell getPoiCellFromRef(org.apache.poi.ss.usermodel.Workbook workbook, String sheetName, String cellRef) { + org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheet(sheetName); + if (sheet == null) return null; + CellReference ref = new CellReference(cellRef); + org.apache.poi.ss.usermodel.Row row = sheet.getRow(ref.getRow()); + if (row == null) return null; + return row.getCell(ref.getCol()); + } + + public static List getCellRefsInRange(String range) { + // range 格式: "A1:C3" 或 "A1:A1" + List refs = new ArrayList<>(); + String[] parts = range.split(":"); + if (parts.length != 2) { + // 非范围,直接当单个单元格返回 + refs.add(range); + return refs; + } + CellReference start = new CellReference(parts[0]); + CellReference end = new CellReference(parts[1]); + + int startRow = Math.min(start.getRow(), end.getRow()); + int endRow = Math.max(start.getRow(), end.getRow()); + int startCol = Math.min(start.getCol(), end.getCol()); + int endCol = Math.max(start.getCol(), end.getCol()); + + for (int r = startRow; r <= endRow; r++) { + for (int c = startCol; c <= endCol; c++) { + refs.add(new CellReference(r, c).formatAsString()); + } + } + return refs; + } } 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 new file mode 100644 index 00000000..9f26ef41 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/cell/CellIng.java @@ -0,0 +1,241 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.cell; + +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 pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; + +public class CellIng { + + // 获取左框线样式 + public String getLeftBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + BorderStyle border = style.getBorderLeft(); + return border != null ? border.name() : "无"; + } + // 获取左框线颜色 + public String getLeftBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + if (style instanceof XSSFCellStyle) { + XSSFCellStyle xssfStyle = (XSSFCellStyle) style; + return getColorNameOrRGB(xssfStyle.getLeftBorderXSSFColor()); + } + return "无"; + } + + // 获取上框线样式 + public String getTopBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + BorderStyle border = style.getBorderTop(); + return border != null ? border.name() : "无"; + } + + // 获取上框线颜色 + public String getTopBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + if (style instanceof XSSFCellStyle) { + XSSFCellStyle xssfStyle = (XSSFCellStyle) style; + return getColorNameOrRGB(xssfStyle.getTopBorderXSSFColor()); + } + return "无"; + } + + // 获取右框线样式 + public String getRightBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + BorderStyle border = style.getBorderRight(); + return border != null ? border.name() : "无"; + } + + // 获取右框线颜色 + public String getRightBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + if (style instanceof XSSFCellStyle) { + XSSFCellStyle xssfStyle = (XSSFCellStyle) style; + return getColorNameOrRGB(xssfStyle.getRightBorderXSSFColor()); + } + return "无"; + } + + // 获取下框线样式 + public String getBottomBorderStyle(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + BorderStyle border = style.getBorderBottom(); + return border != null ? border.name() : "无"; + } + + // 获取下框线颜色 + public String getBottomBorderColor(Cell cell, org.apache.poi.ss.usermodel.Workbook wb) { + CellStyle style = cell.getCellStyle(); + if (style instanceof XSSFCellStyle) { + XSSFCellStyle xssfStyle = (XSSFCellStyle) style; + return getColorNameOrRGB(xssfStyle.getBottomBorderXSSFColor()); + } + return "无"; + } + // 获取单元格的公式表达式(字符串形式) + public String getFormulaExpression(Cell cell, Workbook wb) { + if (cell == null) return "无"; + if (cell.getCellType() == CellType.FORMULA) { + return cell.getCellFormula(); + } + return "无"; + } + + // 获取单元格的公式计算结果(已经计算出来的值,字符串形式) + public String getFormulaResult(Cell cell, Workbook wb) { + if (cell == null) return "无"; + if (cell.getCellType() != CellType.FORMULA) { + return null; // 不是公式单元格返回null + } + FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); + CellValue cellValue = evaluator.evaluate(cell); + if (cellValue == null) return "无"; + + switch (cellValue.getCellType()) { + case BOOLEAN: + return String.valueOf(cellValue.getBooleanValue()); + case NUMERIC: + return String.valueOf(cellValue.getNumberValue()); + case STRING: + return cellValue.getStringValue(); + case ERROR: + return "错误值"; + case BLANK: + return ""; + default: + return "无"; + } + } + + // 获取单元格字体名称 + public String getFontName(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + + int fontIndex = style.getFontIndexAsInt(); + Font font = wb.getFontAt(fontIndex); + if (font != null) { + return font.getFontName(); + } + return "无"; + } + + // 获取单元格字体字号 + public String getFontSize(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + + int fontIndex = style.getFontIndexAsInt(); + Font font = wb.getFontAt(fontIndex); + if (font != null) { + // getFontHeightInPoints() 返回的是磅值(pt),整数 + return String.valueOf(font.getFontHeightInPoints()); + } + return "无"; + } + + + + + // 获取单元格字形(加粗 / 斜体 ) + public String getFontStyle(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + + int fontIndex = style.getFontIndexAsInt(); + Font font = wb.getFontAt(fontIndex); + if (font == null) return "无"; + + boolean isBold = font.getBold(); + boolean isItalic = font.getItalic(); + + if (isBold && isItalic) return "粗体 + 斜体"; + if (isBold) return "粗体"; + if (isItalic) return "斜体"; + return "正常"; + } + + // 获取单元格下划线 +// 获取单元格下划线类型 + public String getUnderline(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + + int fontIndex = style.getFontIndexAsInt(); + Font font = wb.getFontAt(fontIndex); + if (font == null) return "无"; + + byte underline = font.getUnderline(); + switch (underline) { + case Font.U_NONE: + return "无"; + case Font.U_SINGLE: + return "单下划线"; + case Font.U_DOUBLE: + return "双下划线"; + case Font.U_SINGLE_ACCOUNTING: + return "会计用单下划线"; + case Font.U_DOUBLE_ACCOUNTING: + return "会计用双下划线"; + default: + return "未知类型(" + underline + ")"; + } + } + + // 获取单元格颜色 + public String getFontColor(Cell cell, Workbook wb) { + if (cell == null) return "无"; + CellStyle style = cell.getCellStyle(); + if (style == null) return "无"; + + int fontIndex = style.getFontIndexAsInt(); + Font font = wb.getFontAt(fontIndex); + if (font == null) return "无"; + + if (font instanceof XSSFFont) { + // 处理 XLSX + XSSFColor color = ((XSSFFont) font).getXSSFColor(); + return ColorUtils.getChineseColorName(getColorNameOrRGB(color)); + + } else if (font instanceof HSSFFont) { + // 处理 XLS + short colorIndex = font.getColor(); + HSSFPalette palette = ((HSSFWorkbook) wb).getCustomPalette(); + HSSFColor hssfColor = palette.getColor(colorIndex); + return ColorUtils.getChineseColorName(getHSSFColorNameOrRGB(hssfColor)); + } + + return "无"; + } + + + + private String getHSSFColorNameOrRGB(HSSFColor color) { + if (color == null) return "无"; + short[] rgb = color.getTriplet(); + if (rgb == null) return "无"; + return String.format("%02X%02X%02X", rgb[0], rgb[1], rgb[2]); + } + + // 工具方法:颜色转换为 RGB 代码 + private String getColorNameOrRGB(org.apache.poi.xssf.usermodel.XSSFColor color) { + if (color == null) { + return "无"; + } + byte[] rgb = color.getRGB(); + if (rgb == null) { + return "无"; + } + return String.format("%02X%02X%02X", rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF); + } +} 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 new file mode 100644 index 00000000..83cb212c --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java @@ -0,0 +1,475 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.chart; + +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.drawingml.x2006.chart.*; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphicalObjectFrame; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotTableDefinition; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +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 ChartHandler { + + /** 图表常规 - 是否存在图表 */ + public String isChartPresent(XSSFChart chart, XSSFSheet sheet) { + return chart != null ? "存在" : "不存在"; + } + + /** 图表常规 - 图表类型 */ + public String getChartType(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无图表"; + + CTChart ctChart = chart.getCTChart(); + if (ctChart == 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 "散点图"; + // 可根据需求继续扩展 + } + return "未知图表类型"; + } + + + + + /** 图表-数据-数据系列 - 产生方式 */ + public String getDataSeriesGeneration(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无图表"; + + CTChart ctChart = chart.getCTChart(); + if (ctChart == null || ctChart.getPlotArea() == null) return "无数据系列"; + + CTPlotArea plotArea = ctChart.getPlotArea(); + StringBuilder sb = new StringBuilder(); + + // 遍历柱状图 + for (CTBarChart barChart : plotArea.getBarChartList()) { + for (CTBarSer ser : barChart.getSerList()) { + String gen = getSeriesGeneration(ser.getTx()); + sb.append(gen).append("; "); + } + } + + // 遍历折线图 + for (CTLineChart lineChart : plotArea.getLineChartList()) { + for (CTLineSer ser : lineChart.getSerList()) { + String gen = getSeriesGeneration(ser.getTx()); + sb.append(gen).append("; "); + } + } + + // 遍历饼图 + for (CTPieChart pieChart : plotArea.getPieChartList()) { + for (CTPieSer ser : pieChart.getSerList()) { + String gen = getSeriesGeneration(ser.getTx()); + sb.append(gen).append("; "); + } + } + + return sb.length() > 0 ? sb.substring(0, sb.length() - 2) : "无数据系列"; + } + + /** 图表-数据-数据系列 - 名称 */ + public String getDataSeriesNames(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无图表"; + + CTChart ctChart = chart.getCTChart(); + if (ctChart == null || ctChart.getPlotArea() == null) return "无数据系列"; + + CTPlotArea plotArea = ctChart.getPlotArea(); + StringBuilder sb = new StringBuilder(); + + // 遍历柱状图 + for (CTBarChart barChart : plotArea.getBarChartList()) { + for (CTBarSer ser : barChart.getSerList()) { + String name = extractSeriesName(ser.getTx()); + sb.append(name).append("; "); + } + } + + // 遍历折线图 + for (CTLineChart lineChart : plotArea.getLineChartList()) { + for (CTLineSer ser : lineChart.getSerList()) { + String name = extractSeriesName(ser.getTx()); + sb.append(name).append("; "); + } + } + + // 遍历饼图 + for (CTPieChart pieChart : plotArea.getPieChartList()) { + for (CTPieSer ser : pieChart.getSerList()) { + String name = extractSeriesName(ser.getTx()); + sb.append(name).append("; "); + } + } + + // 遍历其他图表类型,可按需补充(区域图、散点图等) + + return sb.length() > 0 ? sb.substring(0, sb.length() - 2) : "无数据系列"; + + + } + + /** 图表-轴标签-名称 */ + public String getAxisLabels(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无"; + + StringBuilder sb = new StringBuilder(); + String sheetName = sheet.getSheetName(); + + List chartDataList = chart.getChartSeries(); + if (chartDataList == null || chartDataList.isEmpty()) return "无"; + + int chartIndex = 1; // 可根据实际情况设置 + for (XDDFChartData chartData : chartDataList) { + List seriesList = chartData.getSeries(); + for (int i = 0; i < seriesList.size(); i++) { + XDDFChartData.Series series = seriesList.get(i); + + // 获取分类轴 XValue + StringBuilder xValuesStr = new StringBuilder(); + XDDFCategoryDataSource xValues = (XDDFCategoryDataSource) series.getCategoryData(); + if (xValues != null) { + int count = xValues.getPointCount(); + for (int j = 0; j < count; j++) { + xValuesStr.append(xValues.getPointAt(j)).append(","); + } + if (xValuesStr.length() > 0) { + xValuesStr.setLength(xValuesStr.length() - 1); // 去掉最后逗号 + } + } else { + xValuesStr.append("未知XValue"); + } + + return xValuesStr.toString(); + } + } + + return sb.length() > 0 ? sb.toString() : "无"; + } + + + /** 图表 -数据- 图表数据区域 */ + public String getChartPosition(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无图表"; + if (sheet == null) return "无工作表"; + + StringBuilder sb = new StringBuilder(); + String sheetName = sheet.getSheetName(); + + // 遍历图表中的所有数据系列 + List chartDataList = chart.getChartSeries(); + if (chartDataList == null || chartDataList.isEmpty()) return "无数据系列"; + + int chartIndex = 1; // 可按实际需要调整 + for (XDDFChartData chartData : chartDataList) { + List seriesList = chartData.getSeries(); + for (int i = 0; i < seriesList.size(); i++) { + XDDFChartData.Series series = seriesList.get(i); + + String xRef = "未知X区域"; + String yRef = "未知Y区域"; + + // 获取X轴引用 + if (series.getCategoryData() != null && series.getCategoryData() instanceof XDDFDataSource) { + XDDFDataSource xData = series.getCategoryData(); + if (xData.getDataRangeReference() != null) { + xRef = xData.getDataRangeReference(); + } + } + + // 获取Y轴引用 + if (series.getValuesData() != null) { + XDDFNumericalDataSource yData = series.getValuesData(); + if (yData.getDataRangeReference() != null) { + yRef = yData.getDataRangeReference(); + } + } + + sb.append(String.format("【%s】【第%d个图表】【数据系列第%d】【X区域:%s】【Y区域:%s】\n", + sheetName, chartIndex, i + 1, xRef, yRef)); + } + } + + return sb.length() > 0 ? sb.toString() : "无数据系列区域"; + + } + + + /** 图表 -位置- 图表数据区域 */ + public String getChartRelPosition(XSSFChart chart, XSSFSheet sheet) { + if (chart == null || sheet == null) return "无图表"; + + XSSFDrawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) return "无图表"; + + List anchors = drawing.getCTDrawing().getTwoCellAnchorList(); + for (CTTwoCellAnchor anchor : anchors) { + CTGraphicalObjectFrame graphicFrame = anchor.getGraphicFrame(); + if (graphicFrame != null && graphicFrame.getGraphic() != null) { + CTGraphicalObjectData graphicData = graphicFrame.getGraphic().getGraphicData(); + if (graphicData != null && + "http://schemas.openxmlformats.org/drawingml/2006/chart".equals(graphicData.getUri())) { + // 不用 getAnyArray(),直接假设匹配第一个 chart + int row1 = anchor.getFrom().getRow(); + int col1 = anchor.getFrom().getCol(); + int row2 = anchor.getTo().getRow(); + int col2 = anchor.getTo().getCol(); + String startCell = new CellReference(row1, col1).formatAsString(); + String endCell = new CellReference(row2, col2).formatAsString(); + return startCell + ":" + endCell; + } + } + } + + return "未找到图表位置"; + } + + + /** 图表-设计 - 图表样式 */ + public String getChartStyle(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无图表"; +// +// CTChart ctChart = chart.getCTChart(); +// if (ctChart == null) return "无样式"; +// +// CTPlotArea plotArea = ctChart.getPlotArea(); +// if (plotArea != null && plotArea.getExtLst() != null) { +// // 可以进一步解析 extLst 获取自定义样式信息 +// } +// +// // 尝试直接从 plotArea 或 chart XML 属性里获取 style +// if (ctChart.getDomNode().getAttributes().getNamedItem("style") != null) { +// String style = ctChart.getDomNode().getAttributes().getNamedItem("style").getNodeValue(); +// return "样式" + style; +// } + + return "默认样式"; + } + + + /** 获取图表高度,返回"磅(厘米)" */ + public static String getChartHeight(XSSFChart chart, XSSFSheet sheet) { + if (chart == null || sheet == null) return "无"; + + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + if (drawing == null) return "无"; + + for (XSSFShape shape : drawing.getShapes()) { + if (!(shape instanceof XSSFGraphicFrame)) continue; + XSSFGraphicFrame frame = (XSSFGraphicFrame) shape; + CTGraphicalObjectFrame ctFrame = frame.getCTGraphicalObjectFrame(); + CTGraphicalObjectData graphicData = ctFrame.getGraphic().getGraphicData(); + if (graphicData.getUri().equals("http://schemas.openxmlformats.org/drawingml/2006/chart")) { + ClientAnchor anchor = frame.getAnchor(); + double heightPt = 0; + for (int r = anchor.getRow1(); r < anchor.getRow2(); r++) { + Row row = sheet.getRow(r); + float rowHeightPt = row != null ? row.getHeightInPoints() : sheet.getDefaultRowHeightInPoints(); + heightPt += rowHeightPt; + } + double heightCm = heightPt * 0.0352778; + return String.format("%.1f磅(%.2f厘米)", heightPt, heightCm); + } + } + return "无"; + } + + + /** 获取图表宽度,返回"磅(厘米)" */ + //todo + public static String getChartWidth(XSSFChart chart, XSSFSheet sheet) { + if (chart == null || sheet == null) return "无"; + +// XSSFDrawing drawing = sheet.createDrawingPatriarch(); +// if (drawing == null) return "无"; +// +// for (XSSFShape shape : drawing.getShapes()) { +// if (!(shape instanceof XSSFGraphicFrame)) continue; +// XSSFGraphicFrame frame = (XSSFGraphicFrame) shape; +// CTGraphicalObjectFrame ctFrame = frame.getCTGraphicalObjectFrame(); +// CTGraphicalObjectData graphicData = ctFrame.getGraphic().getGraphicData(); +// +// // 确保是 chart 类型 +// if (!"http://schemas.openxmlformats.org/drawingml/2006/chart".equals(graphicData.getUri())) +// continue; +// +// // 判断是否是目标 chart +// List charts = drawing.getCharts(); +// if (!charts.contains(chart)) continue; +// +// if (ctFrame.getXfrm() == null || ctFrame.getXfrm().getExt() == null) continue; +// +// long cxEmu = ctFrame.getXfrm().getExt().getCx(); // EMU +// double points = cxEmu * 72.0 / 914400.0; // EMU -> pt +// double cm = points * 2.54 / 72.0; // pt -> cm +// +// return String.format("%.1f磅(%.2f厘米)", points, cm); +// } + + return "无"; + } + + + + + + + + + private int[] cellRefToRowCol(String ref) { + int col = 0, row = 0; + int i = 0; + while (i < ref.length() && Character.isLetter(ref.charAt(i))) { + col = col * 26 + (Character.toUpperCase(ref.charAt(i)) - 'A' + 1); + i++; + } + while (i < ref.length() && Character.isDigit(ref.charAt(i))) { + row = row * 10 + (ref.charAt(i) - '0'); + i++; + } + return new int[]{row - 1, col - 1}; // 转为0起始 + } + + private String cellPosition(int row, int col) { + StringBuilder sb = new StringBuilder(); + col++; // 转为1起始 + while (col > 0) { + int rem = (col - 1) % 26; + sb.insert(0, (char) ('A' + rem)); + col = (col - 1) / 26; + } + return sb.toString() + (row + 1); + } + + /** 图表标题 - 是否显示 */ + public String isChartTitleVisible(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无"; + + CTChart ctChart = chart.getCTChart(); + if (ctChart == null) return "无图表定义"; + + if (ctChart.isSetTitle()) { + return "是"; + } + return "否"; + } + + /** 图表标题 - 文本内容 */ + public String getChartTitleText(XSSFChart chart,XSSFSheet sheet) { + if (chart == null) return "无图表"; + + CTChart ctChart = chart.getCTChart(); + if (ctChart == null || !ctChart.isSetTitle()) return "无标题"; + + // 标题文本一般在 rich 或 strRef + if (ctChart.getTitle().getTx() != null) { + if (ctChart.getTitle().getTx().isSetRich()) { + try { + return ctChart.getTitle().getTx().getRich().getPArray(0).getRArray(0).getT(); + } catch (Exception e) { + return "标题解析异常"; + } + } else if (ctChart.getTitle().getTx().isSetStrRef()) { + return ctChart.getTitle().getTx().getStrRef().getF(); + } + } + return "无标题文本"; + } + + /** 图表标题 - 位置 */ + public String getChartTitlePosition(XSSFChart chart, XSSFSheet sheet) { + if (chart == null) return "无图表"; + + CTChart ctChart = chart.getCTChart(); + if (ctChart == null || !ctChart.isSetTitle()) return "无标题"; + + if (ctChart.getTitle().isSetOverlay()) { + CTBoolean overlay = ctChart.getTitle().getOverlay(); + return overlay.isSetVal()? "覆盖" : "不覆盖"; + } + return "无标题位置属性"; + } + + + + + // 辅助方法:从CTSer的CTSerTx获取名称 + private String extractSeriesName(CTSerTx tx) { + if (tx != null) { + if (tx.getStrRef() != null && tx.getStrRef().getStrCache() != null + && tx.getStrRef().getStrCache().getPtList() != null + && !tx.getStrRef().getStrCache().getPtList().isEmpty()) { + return tx.getStrRef().getStrCache().getPtList().get(0).getV(); + } else if (tx.getV() != null) { + return tx.getV(); + } + } + return "未知名称"; + } + // 辅助方法:根据CTSerTx判断产生方式 + private String getSeriesGeneration(CTSerTx tx) { + if (tx != null) { + if (tx.getStrRef() != null) { + return "系列产生于列"; + } else if (tx.getV() != null) { + return "手动输入"; + } + } + return "未知生成方式"; + } + + + public void printChartXml(XSSFChart chart) { + if (chart == null) { + System.out.println("无图表"); + return; + } + + try { + PackagePart part = chart.getPackagePart(); + try (InputStream is = part.getInputStream()) { + String xml = new String(is.readAllBytes(), StandardCharsets.UTF_8); + System.out.println("===== chart.xml 内容 ====="); + System.out.println(xml); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + + +} + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/col/ColIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/col/ColIng.java new file mode 100644 index 00000000..2c2e863a --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/col/ColIng.java @@ -0,0 +1,39 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.col; + +public class ColIng { + /** + * 列 - 列宽 + * @param colStr 列字母(例如 "B")或列号(从1开始,例如 "2" 表示第2列) + * @param workbook Apache POI Workbook + * @param sheetIndex sheet序号(从1开始) + * @return 列宽(Excel单位),保留1位小数 + */ + public String getColumnWidth(String colStr, org.apache.poi.ss.usermodel.Workbook workbook, int sheetIndex) { + try { + int colIndex; + if (colStr.matches("\\d+")) { + colIndex = Integer.parseInt(colStr) - 1; // 数字列号转0基 + } else { + colIndex = colLetterToIndex(colStr); // 字母转索引 + } + org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + int widthUnits = sheet.getColumnWidth(colIndex); // 单位是 1/256 字符宽度 + double width = widthUnits / 256.0; + return String.format("%.1f", width); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + // 列字母转索引(A=0, B=1, ..., AA=26, ...) + private int colLetterToIndex(String col) { + col = col.toUpperCase(); + int result = 0; + for (int i = 0; i < col.length(); i++) { + result *= 26; + result += col.charAt(i) - 'A' + 1; + } + return result - 1; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/page/PagIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/page/PagIng.java new file mode 100644 index 00000000..e4ba406f --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/page/PagIng.java @@ -0,0 +1,457 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.page; + +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHeaderFooter; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageMargins; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageSetup; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; + +public class PagIng { + + /** + * 获取页面方向 + * @param workbook 工作簿 + * @param sheetIndex sheet页索引,从1开始 + * @return 页面方向,"纵向"或"横向" + */ + public String getPageOrientation(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet instanceof XSSFSheet) { + PrintSetup printSetup = sheet.getPrintSetup(); + if (printSetup.getLandscape()) { + return "横向"; + } else { + return "纵向"; + } + } + return "无"; + } + + /** + * 获取页面缩放比例 + * @param workbook 工作簿 + * @param sheetIndex sheet页索引,从1开始 + * @return 缩放比例,如 "100%" + */ + public String getPageScale(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet instanceof XSSFSheet) { + PrintSetup printSetup = sheet.getPrintSetup(); + int scale = printSetup.getScale(); + if (scale > 0) { + return scale + "%"; + } + } + return "无"; + } + + /** + * 获取纸张大小(根据纸张编号返回常用名称) + * @param workbook 工作簿 + * @param sheetIndex sheet页索引,从1开始 + * @return 纸张大小描述 + */ + public String getPaperSize(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet instanceof XSSFSheet) { + PrintSetup printSetup = sheet.getPrintSetup(); + short paperSize = printSetup.getPaperSize(); + return paperSizeToName(paperSize); + } + return "无"; + } + + /** + * 获取起始页码 + * @param workbook 工作簿 + * @param sheetIndex sheet页索引,从1开始 + * @return 起始页码 + */ + public String getFirstPageNumber(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTWorksheet ctWorksheet = xssfSheet.getCTWorksheet(); + if (ctWorksheet.isSetPageSetup()) { + CTPageSetup pageSetup = ctWorksheet.getPageSetup(); + if (pageSetup.isSetFirstPageNumber()) { + return String.valueOf(pageSetup.getFirstPageNumber()); + } + } + } + return "无"; + } + + + + //页边距 + //上(磅(cm)) + public String getMarginTop(Workbook workbook, int sheetIndex) { + CTPageMargins margins = getMargins(workbook, sheetIndex); + if (margins == null) return "无"; + return formatMarginWithPtAndCm(margins.getTop()); + } + + //下(磅(cm)) + public String getMarginBottom(Workbook workbook, int sheetIndex) { + CTPageMargins margins = getMargins(workbook, sheetIndex); + if (margins == null) return "无"; + return formatMarginWithPtAndCm(margins.getBottom()); + } + + //左(磅(cm)) + public String getMarginLeft(Workbook workbook, int sheetIndex) { + CTPageMargins margins = getMargins(workbook, sheetIndex); + if (margins == null) return "无"; + return formatMarginWithPtAndCm(margins.getLeft()); + } + + //右(磅(cm)) + public String getMarginRight(Workbook workbook, int sheetIndex) { + CTPageMargins margins = getMargins(workbook, sheetIndex); + if (margins == null) return "无"; + return formatMarginWithPtAndCm(margins.getRight()); + } + + + //页边距 + //页眉 + public String getHeaderMargin(Workbook workbook, int sheetIndex) { + CTPageMargins margins = getMargins(workbook, sheetIndex); + if (margins == null) return "无"; + return formatMarginWithPtAndCm(margins.getHeader()); + } + //页脚 + + public String getFooterMargin(Workbook workbook, int sheetIndex) { + CTPageMargins margins = getMargins(workbook, sheetIndex); + if (margins == null) return "无"; + return formatMarginWithPtAndCm(margins.getFooter()); + } + + + // --- 页眉 --- + + // 首页-左部页眉文字 + public String getHeaderFirstLeft(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "first_left"); + } + + // 首页-中部页眉文字 + public String getHeaderFirstCenter(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "first_center"); + } + + // 首页-右部页眉文字 + public String getHeaderFirstRight(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "first_right"); + } + + // --- 奇数页 --- + + // 奇数页-左部页眉文字 + public String getHeaderOddLeft(Workbook workbook, int sheetIndex) { + // Excel没有区分奇偶页眉,暂用统一获取 + return getHeaderPart(workbook, sheetIndex, "odd_left"); + } + + // 奇数页-中部页眉文字 + public String getHeaderOddCenter(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "odd_center"); + } + + // 奇数页-右部页眉文字 + public String getHeaderOddRight(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "odd_right"); + } + + // --- 偶数页 --- + + // 偶数页-左部页眉文字 + public String getHeaderEvenLeft(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "even_left"); + } + + // 偶数页-中部页眉文字 + public String getHeaderEvenCenter(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "even_center"); + } + + // 偶数页-右部页眉文字 + public String getHeaderEvenRight(Workbook workbook, int sheetIndex) { + return getHeaderPart(workbook, sheetIndex, "even_right"); + } + + + // --- 页脚同理 --- + + // 页脚首页左部 + public String getFooterFirstLeft(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "first_left"); + } + + // 页脚首页中部 + public String getFooterFirstCenter(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "first_center"); + } + + // 页脚首页右部 + public String getFooterFirstRight(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "first_right"); + } + + // 奇数页页脚左部 + public String getFooterOddLeft(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "odd_left"); + } + + // 奇数页页脚中部 + public String getFooterOddCenter(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "odd_center"); + } + + // 奇数页页脚右部 + public String getFooterOddRight(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "odd_right"); + } + + // 偶数页页脚左部 + public String getFooterEvenLeft(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "even_left"); + } + + // 偶数页页脚中部 + public String getFooterEvenCenter(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "even_center"); + } + + // 偶数页页脚右部 + public String getFooterEvenRight(Workbook workbook, int sheetIndex) { + return getFooterPart(workbook, sheetIndex, "even_right"); + } + + + // 页眉/页脚选项 + // 奇偶页不同 + public String isDifferentOddEven(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex-1); + if (!(sheet instanceof XSSFSheet)) { + return "只支持XSSF格式的Sheet"; + } + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTHeaderFooter headerFooter = xssfSheet.getCTWorksheet().getHeaderFooter(); + if (headerFooter != null && headerFooter.isSetDifferentOddEven() && headerFooter.getDifferentOddEven()) { + return "是"; + } + return "否"; + } + + // 首页不同 + public String isDifferentFirstPage(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex-1); + if (!(sheet instanceof XSSFSheet)) { + return "只支持XSSF格式的Sheet"; + } + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTHeaderFooter headerFooter = xssfSheet.getCTWorksheet().getHeaderFooter(); + if (headerFooter != null && headerFooter.isSetDifferentFirst() && headerFooter.getDifferentFirst()) { + return "是"; + } + return "否"; + } + + + + + + + //==========辅助方法========== + + private String getHeaderPart(Workbook workbook, int sheetIndex, String part) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (!(sheet instanceof XSSFSheet)) { + return "只支持XSSF格式的Sheet"; + } + + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTHeaderFooter hf = xssfSheet.getCTWorksheet().getHeaderFooter(); + if (hf == null) { + return "无"; + } + + boolean diffFirst = hf.getDifferentFirst(); + boolean diffOddEven = hf.getDifferentOddEven(); + + String target = null; + + switch (part.toLowerCase()) { + case "first_left": + case "first_center": + case "first_right": + if (diffFirst && hf.isSetFirstHeader()) { + target = getHeaderAndFooterSection(hf.getFirstHeader(), part); + } + break; + + case "even_left": + case "even_center": + case "even_right": + if (diffOddEven && hf.isSetEvenHeader()) { + target = getHeaderAndFooterSection(hf.getEvenHeader(), part); + } + break; + + case "odd_left": + case "odd_center": + case "odd_right": + if (hf.isSetOddHeader()) { + target = getHeaderAndFooterSection(hf.getOddHeader(), part); + } + break; + + case "left": + case "center": + case "right": + // 默认使用 OddHeader + if (hf.isSetOddHeader()) { + target = getHeaderAndFooterSection(hf.getOddHeader(), part); + } + break; + } + + return (target == null || target.isEmpty()) ? "无" : target; + } + + + + + + + private String getFooterPart(Workbook workbook, int sheetIndex, String part) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (!(sheet instanceof XSSFSheet)) { + return "只支持XSSF格式的Sheet"; + } + + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTHeaderFooter hf = xssfSheet.getCTWorksheet().getHeaderFooter(); + if (hf == null) { + return "无"; + } + + boolean diffFirst = hf.getDifferentFirst(); + boolean diffOddEven = hf.getDifferentOddEven(); + + String target = null; + + switch (part.toLowerCase()) { + case "first_left": + case "first_center": + case "first_right": + if (diffFirst && hf.isSetFirstFooter()) { + target = getHeaderAndFooterSection(hf.getFirstFooter(), part); + } + break; + + case "even_left": + case "even_center": + case "even_right": + if (diffOddEven && hf.isSetEvenFooter()) { + target = getHeaderAndFooterSection(hf.getEvenFooter(), part); + } + break; + + case "odd_left": + case "odd_center": + case "odd_right": + if (hf.isSetOddFooter()) { + target = getHeaderAndFooterSection(hf.getOddFooter(), part); + } + break; + + case "left": + case "center": + case "right": + // 默认使用 OddFooter + if (hf.isSetOddFooter()) { + target = getHeaderAndFooterSection(hf.getOddFooter(), part); + } + break; + } + + return (target == null || target.isEmpty()) ? "无" : target; + + + + } + + // 从页眉文本里取左/中/右 + private String getHeaderAndFooterSection(String headerText, String part) { + if (headerText == null) return null; + String left = ""; + String center = ""; + String right = ""; + + // Excel 页眉分隔符: &L(左) &C(中) &R(右) + String[] sections = headerText.split("(?=&[LCR])"); + for (String sec : sections) { + if (sec.startsWith("&L")) { + left = sec.substring(2); + } else if (sec.startsWith("&C")) { + center = sec.substring(2); + } else if (sec.startsWith("&R")) { + right = sec.substring(2); + } + } + + switch (part.toLowerCase()) { + case "first_left": + case "even_left": + case "odd_left": + case "left": + return left; + case "first_center": + case "even_center": + case "odd_center": + case "center": + return center; + case "first_right": + case "even_right": + case "odd_right": + case "right": + return right; + } + return null; + } + + + + + + private String formatMarginWithPtAndCm(double inchVal) { + double pt = inchVal * 72; // 1 inch = 72 points + double cm = inchVal * 2.54; // 1 inch = 2.54 cm + return String.format("%.2f 磅 (%.2f 厘米)", pt, cm); + } + + private CTPageMargins getMargins(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (!(sheet instanceof XSSFSheet)) return null; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + return xssfSheet.getCTWorksheet().getPageMargins(); + } + // 纸张大小编号转名称,常用部分 + private String paperSizeToName(short paperSize) { + switch (paperSize) { + case PrintSetup.A4_PAPERSIZE: return "A4"; + case PrintSetup.LETTER_PAPERSIZE: return "信纸"; + case PrintSetup.LEGAL_PAPERSIZE: return "法律"; + case PrintSetup.EXECUTIVE_PAPERSIZE: return "行政"; + case PrintSetup.A3_PAPERSIZE: return "A3"; + case PrintSetup.B4_PAPERSIZE: return "B4"; + case PrintSetup.B5_PAPERSIZE: return "B5"; + default: return "其他(" + paperSize + ")"; + } + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/pivotTable/PivotTabling.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/pivotTable/PivotTabling.java new file mode 100644 index 00000000..325b11b5 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/pivotTable/PivotTabling.java @@ -0,0 +1,539 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.pivotTable; + +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.xssf.usermodel.*; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.model.StylesTable; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xlsx4j.sml.STShowDataAs; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.List; + +public class PivotTabling { + + /** + * 常规 - 是否存在 + */ + public String isExist(XSSFPivotTable pivotTable, Workbook workbook) { + return pivotTable != null ? "是" : "否"; + } + + /** + * 常规 - 数据来源 + */ + public String getDataSource(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + try { + long cacheId = pivotTable.getCTPivotTableDefinition().getCacheId(); + + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + if (!xssfWorkbook.getCTWorkbook().isSetPivotCaches()) { + return "无数据透视缓存"; + } + + List pivotCaches = xssfWorkbook.getCTWorkbook().getPivotCaches().getPivotCacheList(); + + for (CTPivotCache cache : pivotCaches) { + if (cache.getCacheId() == cacheId) { + String relId = cache.getId(); // r:id + + PackageRelationship rel = xssfWorkbook.getPackagePart().getRelationship(relId); + PackagePart part = xssfWorkbook.getPackagePart().getRelatedPart(rel); + + try (InputStream is = part.getInputStream()) { + CTPivotCacheDefinition def = CTPivotCacheDefinition.Factory.parse(is); + + // 直接用XPath选出cacheSource节点 + XmlObject[] cacheSources = def.selectPath("declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//ns:cacheSource"); + if (cacheSources != null && cacheSources.length > 0) { + XmlObject cacheSource = cacheSources[0]; + + XmlObject[] worksheetSources = cacheSource.selectPath("declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//ns:worksheetSource"); + if (worksheetSources != null && worksheetSources.length > 0) { + XmlObject worksheetSource = worksheetSources[0]; + // 通过属性获取sheet和ref + String sheetName = worksheetSource.getDomNode().getAttributes().getNamedItem("sheet").getNodeValue(); + String ref = worksheetSource.getDomNode().getAttributes().getNamedItem("ref").getNodeValue(); + + return String.format("%s!%s", sheetName, ref); + } + } + return "未知数据源"; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + return "异常:" + e.getMessage(); + } + + return "未找到匹配缓存"; + } + + + + + + + /** + * 常规 - 放置位置 + */ + public String getPlacement(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + CTPivotTableDefinition def = pivotTable.getCTPivotTableDefinition(); + if (def != null && def.getLocation() != null) { + return def.getLocation().getRef(); + } + return "未知"; + } + + /** + * 字段分布 - 报表筛选区域 + */ + public String getReportFilterFields(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + try { + CTPivotTableDefinition def = pivotTable.getCTPivotTableDefinition(); + if (def == null) return "无"; + + CTPageFields pageFields = def.getPageFields(); + if (pageFields == null) return "无"; + + CTPageField[] ctFields = pageFields.getPageFieldArray(); + if (ctFields == null || ctFields.length == 0) return "无"; + + long cacheId = def.getCacheId(); + + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + if (!xssfWorkbook.getCTWorkbook().isSetPivotCaches()) { + return "无数据透视缓存"; + } + + List pivotCaches = xssfWorkbook.getCTWorkbook().getPivotCaches().getPivotCacheList(); + + for (CTPivotCache cache : pivotCaches) { + if (cache.getCacheId() == cacheId) { + String relId = cache.getId(); // r:id + PackageRelationship rel = xssfWorkbook.getPackagePart().getRelationship(relId); + PackagePart part = xssfWorkbook.getPackagePart().getRelatedPart(rel); + + try (InputStream is = part.getInputStream()) { + CTPivotCacheDefinition cacheDef = CTPivotCacheDefinition.Factory.parse(is); + + XmlObject[] cacheFieldsArr = cacheDef.selectPath( + "declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//ns:cacheField" + ); + if (cacheFieldsArr == null || cacheFieldsArr.length == 0) { + return "无缓存字段"; + } + + StringBuilder sb = new StringBuilder(); + + for (CTPageField pageField : ctFields) { + int idx = (int) pageField.getFld(); + if (idx >= 0 && idx < cacheFieldsArr.length) { + XmlObject cacheFieldObj = cacheFieldsArr[idx]; + String name = cacheFieldObj.getDomNode().getAttributes().getNamedItem("name").getNodeValue(); + sb.append(name).append("; "); + } else { + sb.append("索引超出范围; "); + } + } + + if (sb.length() > 2) sb.setLength(sb.length() - 2); + return sb.toString(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + return "异常:" + e.getMessage(); + } + + return "未找到匹配缓存"; + } + + /** + * 字段分布 - 列标签区域 + */ + public String getColumnLabelFields(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + try { + CTPivotTableDefinition def = pivotTable.getCTPivotTableDefinition(); + if (def == null) return "无"; + + CTColFields colFields = def.getColFields(); + if (colFields == null) return "无"; + + CTField[] ctFields = colFields.getFieldArray(); + if (ctFields == null || ctFields.length == 0) return "无"; + + long cacheId = def.getCacheId(); + + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + if (!xssfWorkbook.getCTWorkbook().isSetPivotCaches()) { + return "无数据透视缓存"; + } + + List pivotCaches = xssfWorkbook.getCTWorkbook().getPivotCaches().getPivotCacheList(); + + for (CTPivotCache cache : pivotCaches) { + if (cache.getCacheId() == cacheId) { + String relId = cache.getId(); // r:id + PackageRelationship rel = xssfWorkbook.getPackagePart().getRelationship(relId); + PackagePart part = xssfWorkbook.getPackagePart().getRelatedPart(rel); + + try (InputStream is = part.getInputStream()) { + CTPivotCacheDefinition cacheDef = CTPivotCacheDefinition.Factory.parse(is); + + XmlObject[] cacheFieldsArr = cacheDef.selectPath( + "declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//ns:cacheField" + ); + if (cacheFieldsArr == null || cacheFieldsArr.length == 0) { + return "无缓存字段"; + } + + StringBuilder sb = new StringBuilder(); + + for (CTField field : ctFields) { + int idx = (int) field.getX(); + if (idx >= 0 && idx < cacheFieldsArr.length) { + XmlObject cacheFieldObj = cacheFieldsArr[idx]; + String name = cacheFieldObj.getDomNode().getAttributes().getNamedItem("name").getNodeValue(); + sb.append(name).append("; "); + } else { + sb.append("索引超出范围; "); + } + } + + if (sb.length() > 2) sb.setLength(sb.length() - 2); + return sb.toString(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + return "异常:" + e.getMessage(); + } + + return "未找到匹配缓存"; + } + + + + /** + * 字段分布 - 行标签区域 + */ + public String getRowLabelFields(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + try { + CTPivotTableDefinition def = pivotTable.getCTPivotTableDefinition(); + if (def == null) return "无"; + + CTRowFields rowFields = def.getRowFields(); + if (rowFields == null) return "无"; + + CTField[] ctFields = rowFields.getFieldArray(); + if (ctFields == null || ctFields.length == 0) return "无"; + + long cacheId = def.getCacheId(); + + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + if (!xssfWorkbook.getCTWorkbook().isSetPivotCaches()) { + return "无数据透视缓存"; + } + + List pivotCaches = xssfWorkbook.getCTWorkbook().getPivotCaches().getPivotCacheList(); + + for (CTPivotCache cache : pivotCaches) { + if (cache.getCacheId() == cacheId) { + String relId = cache.getId(); // r:id + PackageRelationship rel = xssfWorkbook.getPackagePart().getRelationship(relId); + PackagePart part = xssfWorkbook.getPackagePart().getRelatedPart(rel); + + try (InputStream is = part.getInputStream()) { + CTPivotCacheDefinition cacheDef = CTPivotCacheDefinition.Factory.parse(is); + + // 直接XPath拿cacheFields + XmlObject[] cacheFieldsArr = cacheDef.selectPath( + "declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//ns:cacheField" + ); + if (cacheFieldsArr == null || cacheFieldsArr.length == 0) { + return "无缓存字段"; + } + + StringBuilder sb = new StringBuilder(); + + for (CTField field : ctFields) { + int idx = (int) field.getX(); + if (idx >= 0 && idx < cacheFieldsArr.length) { + XmlObject cacheFieldObj = cacheFieldsArr[idx]; + // 通过属性拿name + String name = cacheFieldObj.getDomNode().getAttributes().getNamedItem("name").getNodeValue(); + sb.append(name).append("; "); + } else { + sb.append("索引超出范围; "); + } + } + + if (sb.length() > 2) sb.setLength(sb.length() - 2); // 去掉最后分号和空格 + return sb.toString(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + return "异常:" + e.getMessage(); + } + + return "未找到匹配缓存"; + } + + + /** + * 字段分布 - 值区域 + */ + public String getValueFields(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + try { + CTPivotTableDefinition def = pivotTable.getCTPivotTableDefinition(); + if (def == null) return "无"; + + CTDataFields dataFields = def.getDataFields(); + if (dataFields == null) return "无"; + + CTDataField[] ctDataFields = dataFields.getDataFieldArray(); + if (ctDataFields == null || ctDataFields.length == 0) return "无"; + + long cacheId = def.getCacheId(); + + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + if (!xssfWorkbook.getCTWorkbook().isSetPivotCaches()) { + return "无数据透视缓存"; + } + + List pivotCaches = xssfWorkbook.getCTWorkbook().getPivotCaches().getPivotCacheList(); + + for (CTPivotCache cache : pivotCaches) { + if (cache.getCacheId() == cacheId) { + String relId = cache.getId(); // r:id + PackageRelationship rel = xssfWorkbook.getPackagePart().getRelationship(relId); + PackagePart part = xssfWorkbook.getPackagePart().getRelatedPart(rel); + + try (InputStream is = part.getInputStream()) { + CTPivotCacheDefinition cacheDef = CTPivotCacheDefinition.Factory.parse(is); + + XmlObject[] cacheFieldsArr = cacheDef.selectPath( + "declare namespace ns='http://schemas.openxmlformats.org/spreadsheetml/2006/main' .//ns:cacheField" + ); + if (cacheFieldsArr == null || cacheFieldsArr.length == 0) { + return "无缓存字段"; + } + + StringBuilder sb = new StringBuilder(); + + for (CTDataField dataField : ctDataFields) { + int idx = (int) dataField.getFld(); + if (idx >= 0 && idx < cacheFieldsArr.length) { + XmlObject cacheFieldObj = cacheFieldsArr[idx]; + String name = cacheFieldObj.getDomNode().getAttributes().getNamedItem("name").getNodeValue(); + sb.append(name).append("; "); + } else { + sb.append("索引超出范围; "); + } + } + + if (sb.length() > 2) sb.setLength(sb.length() - 2); + return sb.toString(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + return "异常:" + e.getMessage(); + } + + return "未找到匹配缓存"; + } + + + /** + * 值字段 - 汇总方式 + */ + public String getValueFieldSummarization(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + CTDataFields dataFields = pivotTable.getCTPivotTableDefinition().getDataFields(); + if (dataFields != null && dataFields.sizeOfDataFieldArray() > 0) { + StringBuilder sb = new StringBuilder(); + for (CTDataField df : dataFields.getDataFieldList()) { + sb.append(df.getName()) + .append(":") + .append(translateSubtotal(df.getSubtotal())) + .append(";"); + } + return sb.toString(); + } + return "无"; + } + + /** + * 显示方式 (Show Values As) + */ + public String getDisplayFormat(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + CTDataFields dataFields = pivotTable.getCTPivotTableDefinition().getDataFields(); + if (dataFields == null || dataFields.sizeOfDataFieldArray() == 0) return "无"; + + StringBuilder sb = new StringBuilder(); + + for (CTDataField df : dataFields.getDataFieldList()) { + String fieldName = df.getName(); + // ShowValuesAs对应的属性是showDataAs,XmlBeans没有直接封装,需用XmlCursor遍历拿 + String showAs = "普通"; // 默认显示方式 + + XmlObject xmlObj = df; + XmlCursor cursor = xmlObj.newCursor(); + if (cursor.toFirstChild()) { + do { + if ("showDataAs".equals(cursor.getName().getLocalPart())) { + showAs = cursor.getTextValue(); + break; + } + } while (cursor.toNextSibling()); + } + cursor.dispose(); + + sb.append(fieldName) + .append(":") + .append(showAs) + .append("; "); + } + + return sb.toString().trim(); + } + + /** + * 数字方式 (Number Format) + */ + public String getNumberFormat(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无"; + + CTPivotTableDefinition def = pivotTable.getCTPivotTableDefinition(); + if (def == null) return "无"; + + CTDataFields dataFields = def.getDataFields(); + if (dataFields == null || dataFields.sizeOfDataFieldArray() == 0) return "无"; + + if (!(workbook instanceof XSSFWorkbook)) return "仅支持XSSFWorkbook"; + + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + + StringBuilder sb = new StringBuilder(); + + for (CTDataField df : dataFields.getDataFieldList()) { + String fieldName = df.getName(); + String formatString = null; + long numFmtIdLong = df.getNumFmtId(); + short numFmtId = (short) numFmtIdLong; + try { + formatString = xssfWorkbook.getStylesSource() + .getNumberFormatAt(numFmtId) ; + } catch (Exception e) { + formatString = null; + } + + + if (formatString == null) { + // 如果没有找到格式,则根据内置numFmtId给个默认名称 + formatString = builtinNumFmtName((int) numFmtId); + } + + sb.append(fieldName) + .append(":") + .append(formatString == null ? "默认格式" : formatString) + .append("; "); + } + + return sb.toString().trim(); + } + + + + //字段数据-数据筛选 + public String getFieldDataFilters(XSSFPivotTable pivotTable, Workbook workbook) { + if (pivotTable == null) return "无数据透视表"; + return null; + } + + //字段数据-数据排序 + public String getFieldDataSorting(XSSFPivotTable pivotTable, Workbook workbook) { + + return null; + } + + + /** + * 内置数字格式名称(简易版,常见的几个) + */ + private String builtinNumFmtName(int numFmtId) { + switch (numFmtId) { + case 0: return "常规"; + case 1: return "0"; + case 2: return "0.00"; + case 3: return "#,##0"; + case 4: return "#,##0.00"; + case 9: return "0%"; + case 10: return "0.00%"; + case 14: return "yyyy/m/d"; + default: return "内置格式ID " + numFmtId; + } + } + + + private String translateSubtotal(STDataConsolidateFunction.Enum subtotal) { + if (subtotal == null) return "无"; + + switch (subtotal.intValue()) { + case STDataConsolidateFunction.INT_SUM: + return "求和"; + case STDataConsolidateFunction.INT_COUNT: + return "计数"; + case STDataConsolidateFunction.INT_AVERAGE: + return "平均值"; + case STDataConsolidateFunction.INT_MAX: + return "最大值"; + case STDataConsolidateFunction.INT_MIN: + return "最小值"; + case STDataConsolidateFunction.INT_PRODUCT: + return "乘积"; + case STDataConsolidateFunction.INT_COUNT_NUMS: + return "数值计数"; + case STDataConsolidateFunction.INT_STD_DEV: + return "标准偏差"; + case STDataConsolidateFunction.INT_VAR: + return "方差"; + case STDataConsolidateFunction.INT_STD_DEVP: + return "总体标准偏差"; + case STDataConsolidateFunction.INT_VARP: + return "总体方差"; + default: + return subtotal.toString(); + } + } + + +} 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 new file mode 100644 index 00000000..fd63b8e0 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/range/RangIng.java @@ -0,0 +1,543 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.range; +//范围 +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.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.usermodel.*; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + + +public class RangIng { + + // 获取单元格的常规值(字符串形式) + public String getValue(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + StringBuilder cellValueBuilder = new StringBuilder(); + for (String cellRefa : cellRefs) { + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellRefa); + if (cell == null) continue; // 这里不直接return,继续循环 + String cellValue1 = getCellValue(cell); + if (cellValue1 != null && !cellValue1.isEmpty()) { + if (cellValueBuilder.length() > 0) cellValueBuilder.append("|"); + cellValueBuilder.append(cellValue1); + } + } + return cellValueBuilder.toString(); + } + + + + // 获取单元格的文本值 + public String getText(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + StringBuilder cellValueBuilder = new StringBuilder(); + DataFormatter formatter = new DataFormatter(); + FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); + + for (int i = 0; i < cellRefs.size(); i++) { + String ref = cellRefs.get(i); + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, ref); + if (cell == null) continue; + + // 显示值(应该是 Excel 中看到的格式化结果,比如 "JD1001") + String displayValue = formatter.formatCellValue(cell, evaluator); + // 原始值(便于调试/比较) + String rawValue = getRawCellValue(cell, evaluator); + + // 把显示值加入返回,之间用 | 分隔 + if (displayValue != null && !displayValue.isEmpty()) { + if (cellValueBuilder.length() > 0) cellValueBuilder.append("|"); + cellValueBuilder.append(displayValue); + } else if (rawValue != null && !rawValue.isEmpty()) { + // 若显示值为空,退回到原始值 + if (cellValueBuilder.length() > 0) cellValueBuilder.append("|"); + cellValueBuilder.append(rawValue); + } + + // 可选:如果你希望同时看到 raw 值用于调试,可以改成: + // cellValueBuilder.append(displayValue).append("(").append(rawValue).append(")"); + } + + return cellValueBuilder.toString(); + } + + + + // 这里是字体名称示例,先简单返回空或默认 + public String getFontName(String cellRef, Workbook workbook, int sheetIndex) { + + List cellRefs = getCellRefsInRange(cellRef); + String cellref= cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return null; + + CellStyle style = cell.getCellStyle(); + if (style == null) return null; + + Font font = workbook.getFontAt(style.getFontIndex()); + return font != null ? font.getFontName() : null; + } + // 字形(粗体、斜体) + public String getFontType(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return null; + + CellStyle style = cell.getCellStyle(); + if (style == null) return null; + + Font font = workbook.getFontAt(style.getFontIndex()); + if (font == null) return null; + + boolean isBold = font.getBold(); + boolean isItalic = font.getItalic(); + + if (isBold && isItalic) return "粗体 + 斜体"; + if (isBold) return "粗体"; + if (isItalic) return "斜体"; + return "正常"; + } + + // 字号 + public String getFontSize(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return null; + + CellStyle style = cell.getCellStyle(); + if (style == null) return null; + + Font font = workbook.getFontAt(style.getFontIndex()); + return font != null ? String.valueOf(font.getFontHeightInPoints()) : null; + } + + // 字体颜色 + public String getFontColor(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return null; + + CellStyle style = cell.getCellStyle(); + if (style == null) return null; + + Font font = workbook.getFontAt(style.getFontIndex()); + if (font == null) return null; + + if (workbook instanceof XSSFWorkbook) { + XSSFColor color = ((XSSFFont) font).getXSSFColor(); + if (color != null && color.getRGB() != null) { + byte[] rgb = color.getRGB(); + String format = String.format("%02X%02X%02X", rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF); + System.out.println(format); + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(format); +// return String.format("#%02X%02X%02X", rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF); + } + } else if (workbook instanceof HSSFWorkbook) { + short colorIndex = font.getColor(); + HSSFPalette palette = ((HSSFWorkbook) workbook).getCustomPalette(); + HSSFColor hssfColor = palette.getColor(colorIndex); + if (hssfColor != null) { + short[] triplet = hssfColor.getTriplet(); + String format = String.format("%02X%02X%02X", triplet[0], triplet[1], triplet[2]); + System.out.println(format+" System.out.println(format);"); + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(format); +// return String.format("#%02X%02X%02X", triplet[0], triplet[1], triplet[2]); + } + } + return null; + } + + + + + + //格式 + //数字格式 + public String printCellFormatNumStyle(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return "无"; + + // ===== 数字格式 ===== + CellStyle style = cell.getCellStyle(); + short dataFormatIndex = style.getDataFormat(); + String dataFormatString = style.getDataFormatString(); + System.out.println("数字格式索引: " + dataFormatIndex + ", 格式: " + dataFormatString); + return dataFormatString; + } + + + //格式-合并单元格 (返回是否) + 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(); + + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet == null) return "否"; + + int mergedRegionsCount = sheet.getNumMergedRegions(); + for (int i = 0; i < mergedRegionsCount; i++) { + CellRangeAddress range = sheet.getMergedRegion(i); + if (range.isInRange(rowIndex, colIndex)) { + return "是"; + } + } + return "否"; + } + + + + + + //条件格式 + //格式 + //填充颜色 + public String printCellFormats(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return "无"; + + Sheet sheet = cell.getSheet(); + if (sheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) sheet; + XSSFSheetConditionalFormatting condFmt = xssfSheet.getSheetConditionalFormatting(); + + int numCF = condFmt.getNumConditionalFormattings(); + for (int i = 0; i < numCF; i++) { + XSSFConditionalFormatting cf = condFmt.getConditionalFormattingAt(i); + int numRules = cf.getNumberOfRules(); + + for (int j = 0; j < numRules; j++) { + XSSFConditionalFormattingRule rule = cf.getRule(j); + System.out.println("条件格式类型: " + rule.getConditionType()); + + // ===== 填充颜色 ===== + 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 "无"; + } + + + + } + } + } + } + return "无"; + } + //字体颜色 + public String printCellFormatFontColor(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return "无"; + + Sheet sheet = cell.getSheet(); + if (sheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) sheet; + XSSFSheetConditionalFormatting condFmt = xssfSheet.getSheetConditionalFormatting(); + + int numCF = condFmt.getNumConditionalFormattings(); + for (int i = 0; i < numCF; i++) { + XSSFConditionalFormatting cf = condFmt.getConditionalFormattingAt(i); + int numRules = cf.getNumberOfRules(); + + for (int j = 0; j < numRules; j++) { + XSSFConditionalFormattingRule rule = cf.getRule(j); + System.out.println("条件格式类型: " + rule.getConditionType()); + + // ===== 字体颜色 ===== + 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 "无"; + } + + + } + } + } + } + return "无"; + } + + + + // 边框颜色 + public String printCellTableColor(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return "无"; + + CellStyle style = cell.getCellStyle(); + + + // ===== 边框颜色 ===== + if (style instanceof XSSFCellStyle) { + XSSFCellStyle xssfStyle = (XSSFCellStyle) style; + XSSFColor borderColorTop = xssfStyle.getTopBorderXSSFColor(); + XSSFColor borderColorBottom = xssfStyle.getBottomBorderXSSFColor(); + XSSFColor borderColorLeft = xssfStyle.getLeftBorderXSSFColor(); + XSSFColor borderColorRight = xssfStyle.getRightBorderXSSFColor(); + System.out.println("上边框颜色: " + (borderColorTop != null ? borderColorTop.getARGBHex() : "无")); + 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() ); + + } else { + System.out.println("边框颜色索引(旧版HSSF): top=" + style.getTopBorderColor() + + ", bottom=" + style.getBottomBorderColor() + + ", left=" + style.getLeftBorderColor() + + ", right=" + style.getRightBorderColor()); + return style!=null?String.valueOf(style.getTopBorderColor()):"无"; + } + + + } + + + + /** + * 获取单元格的公式表达式(范围 + */ + public String getFormulaExpression(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + String cellref = cellRefs.get(0); + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellref); + if (cell == null) return null; + if (cell.getCellType() == CellType.FORMULA) { + return cell.getCellFormula(); + } + return null; + } + + /** + * 获取单元格的公式计算结果(范围 + */ + public String getFormulaResult(String cellRef, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRef); + StringBuilder sb = new StringBuilder(); + FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); + + for (String ref : cellRefs) { + Cell cell = getPoiCellFromRef(workbook, sheetIndex - 1, ref); + if (cell == null) continue; + + if (cell.getCellType() == CellType.FORMULA) { + CellType evaluatedType = evaluator.evaluateFormulaCell(cell); + + String val; + switch (evaluatedType) { + case NUMERIC: + val = String.valueOf(cell.getNumericCellValue()); + break; + case STRING: + val = cell.getStringCellValue(); + break; + case BOOLEAN: + val = String.valueOf(cell.getBooleanCellValue()); + break; + case ERROR: + val = "错误值"; + break; + default: + val = null; + } + + if (val != null) { + if (sb.length() > 0) sb.append("|"); + sb.append(val); + } + } + } + + return sb.toString(); + } + + + + //范围 - 填充 背景色 + public String getFillBgColors(String cellRefRange, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRefRange); + for (String cellRef : cellRefs) { + Cell cell = getPoiCellFromRef(workbook, sheetIndex-1, cellRef); + if (cell == null) continue; + + CellStyle style = cell.getCellStyle(); + if (style instanceof XSSFCellStyle) { + 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() ); + } + } + } + return null; // 没有背景色返回null + } + + //范围 -行 -行高 + public String getRowHeight(String cellRefRange, Workbook workbook, int sheetIndex) { + List cellRefs = getCellRefsInRange(cellRefRange); + if (cellRefs.isEmpty()) return "无"; + + // 取范围里的第一个单元格 + String firstCellRef = cellRefs.get(0); + CellReference cr = new CellReference(firstCellRef); + int rowIndex = cr.getRow(); + + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + Row row = sheet.getRow(rowIndex); + if (row == null) return "无"; + + // getHeightInPoints 是行高的“磅”值 + return String.valueOf(row.getHeightInPoints()); + } + + + + + // ---------- 辅助方法 ---------- + + private static Cell getPoiCellFromRef(Workbook workbook, int sheetIndex, String cellRef) { + Sheet sheet = workbook.getSheetAt(sheetIndex); + if (sheet == null) return null; + + org.apache.poi.ss.util.CellReference ref = new org.apache.poi.ss.util.CellReference(cellRef); + Row row = sheet.getRow(ref.getRow()); + if (row == null) return null; + + return row.getCell(ref.getCol()); + } + + private static String getCellValue(Cell cell) { + if (cell == null) return null; + + switch (cell.getCellType()) { + case STRING: + return cell.getStringCellValue(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue().toString(); + } else { + return String.valueOf(cell.getNumericCellValue()); + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case FORMULA: + try { + FormulaEvaluator evaluator = cell.getSheet().getWorkbook().getCreationHelper().createFormulaEvaluator(); + CellValue cellValue = evaluator.evaluate(cell); + switch (cellValue.getCellType()) { + case STRING: + return cellValue.getStringValue(); + case NUMERIC: + return String.valueOf(cellValue.getNumberValue()); + case BOOLEAN: + return String.valueOf(cellValue.getBooleanValue()); + default: + return ""; + } + } catch (Exception e) { + return ""; + } + default: + return ""; + } + } + + public static List getCellRefsInRange(String range) { + // range 格式: "A1:C3" 或 "A1:A1" + List refs = new ArrayList<>(); + String[] parts = range.split(":"); + if (parts.length != 2) { + // 非范围,直接当单个单元格返回 + refs.add(range); + return refs; + } + CellReference start = new CellReference(parts[0]); + CellReference end = new CellReference(parts[1]); + + int startRow = Math.min(start.getRow(), end.getRow()); + int endRow = Math.max(start.getRow(), end.getRow()); + int startCol = Math.min(start.getCol(), end.getCol()); + int endCol = Math.max(start.getCol(), end.getCol()); + + for (int r = startRow; r <= endRow; r++) { + for (int c = startCol; c <= endCol; c++) { + refs.add(new CellReference(r, c).formatAsString()); + } + } + return refs; + } + private String getCellTextValue(Cell cell) { + if (cell == null) return null; + + DataFormatter formatter = new DataFormatter(); + // 直接用 DataFormatter 会自动把数字、日期、文本等格式化成字符串 + return formatter.formatCellValue(cell); + } + + private String getRawCellValue(Cell cell, FormulaEvaluator evaluator) { + if (cell == null) return ""; + CellType type = cell.getCellType(); + if (type == CellType.FORMULA) { + CellValue cv = evaluator.evaluate(cell); + if (cv == null) return ""; + switch (cv.getCellType()) { + case STRING: return cv.getStringValue(); + case NUMERIC: + // 避免科学计数法 + return BigDecimal.valueOf(cv.getNumberValue()).stripTrailingZeros().toPlainString(); + case BOOLEAN: return String.valueOf(cv.getBooleanValue()); + default: return ""; + } + } + switch (type) { + case STRING: return cell.getStringCellValue(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) return cell.getDateCellValue().toString(); + return BigDecimal.valueOf(cell.getNumericCellValue()).stripTrailingZeros().toPlainString(); + case BOOLEAN: return String.valueOf(cell.getBooleanCellValue()); + default: return ""; + } + } + +} + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/row/RowIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/row/RowIng.java new file mode 100644 index 00000000..518aa7c6 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/row/RowIng.java @@ -0,0 +1,27 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.row; + +public class RowIng { + /** + * 行 - 行高 + * @param rowStr 行号(从1开始,例如 "3" 表示第3行) + * @param workbook Apache POI Workbook + * @param sheetIndex sheet序号(从1开始) + * @return 行高(单位:磅),保留1位小数 + */ + public String getRowHeight(String rowStr, org.apache.poi.ss.usermodel.Workbook workbook, int sheetIndex) { + try { + int rowNum = Integer.parseInt(rowStr) - 1; // 转成0基索引 + org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + org.apache.poi.ss.usermodel.Row row = sheet.getRow(rowNum); + if (row != null) { + float height = row.getHeightInPoints(); // 行高(单位:磅) + return String.format("%.1f", height); + } + } 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_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 new file mode 100644 index 00000000..4eaa8427 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/style/Style.java @@ -0,0 +1,306 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.style; + +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 org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphicalObjectFrame; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPageBreak; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetProtection; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; + +public class Style { + + /** 获取Sheet名称 */ + public String getSheetName(Workbook workbook, int sheetNum) { + if (workbook == null || sheetNum < 0 || sheetNum >= workbook.getNumberOfSheets()) return "无"; + return workbook.getSheetAt(sheetNum).getSheetName(); + } + + /** 获取Sheet类型(工作表 / 图表等) */ + public String getSheetType(Workbook workbook, int sheetNum) { + if (workbook == null || sheetNum < 0 || sheetNum >= workbook.getNumberOfSheets()) return "无"; + Sheet sheet = workbook.getSheetAt(sheetNum); + if (sheet instanceof XSSFSheet) return "工作表"; + // 可扩展判断图表或其他类型 + return "未知类型"; + } + + /** 获取Sheet标签颜色 */ + public String getSheetTabColor(Workbook workbook, int sheetNum) { + if (workbook == null || sheetNum < 0 || sheetNum >= workbook.getNumberOfSheets()) return "无"; + Sheet sheet = workbook.getSheetAt(sheetNum); + if (sheet instanceof XSSFSheet) { + XSSFColor color = ((XSSFSheet) sheet).getTabColor(); + if (color != null) return color.getARGBHex(); + } + return "无"; + } + + /** 判断Sheet是否隐藏 */ + public String isSheetHidden(Workbook workbook, int sheetNum) { + if (workbook == null || sheetNum < 0 || sheetNum >= workbook.getNumberOfSheets()) return "无"; + SheetVisibility visibility = workbook.getSheetVisibility(sheetNum); + switch (visibility) { + case VISIBLE: return "可见"; + case HIDDEN: return "隐藏"; + case VERY_HIDDEN: return "非常隐藏"; + default: return "未知"; + } + } + + /** 获取Sheet数据范围,如"A1:C12" */ + public String getSheetDataRange(Workbook workbook, int sheetNum) { + if (workbook == null || sheetNum < 0 || sheetNum >= workbook.getNumberOfSheets()) return "无"; + Sheet sheet = workbook.getSheetAt(sheetNum); + if (sheet.getPhysicalNumberOfRows() == 0) return "无数据"; + + int firstRow = sheet.getFirstRowNum(); + int lastRow = sheet.getLastRowNum(); + int firstCol = Integer.MAX_VALUE; + int lastCol = Integer.MIN_VALUE; + + for (int r = firstRow; r <= lastRow; r++) { + Row row = sheet.getRow(r); + if (row == null) continue; + short rowFirstCol = row.getFirstCellNum(); + short rowLastCol = row.getLastCellNum(); + if (rowFirstCol >= 0) firstCol = Math.min(firstCol, rowFirstCol); + if (rowLastCol >= 0) lastCol = Math.max(lastCol, rowLastCol - 1); + } + + if (firstCol == Integer.MAX_VALUE || lastCol == Integer.MIN_VALUE) return "无数据"; + + String startCell = CellReference.convertNumToColString(firstCol) + (firstRow + 1); + String endCell = CellReference.convertNumToColString(lastCol) + (lastRow + 1); + return startCell + ":" + endCell; + } + + + + + /** 获取Sheet页数 - 只针对整个工作簿无效,返回1作为单页占用 */ + public String getSheetPages(Workbook workbook, int sheetNum) { + // Excel 没有直接页数概念,只能假定单Sheet为1页 + return "1"; + } + + /** 获取图表数量 */ + public String getChartCount(Workbook workbook, int sheetNum) { + if (!(workbook instanceof XSSFWorkbook)) return "0"; + XSSFSheet sheet = ((XSSFWorkbook) workbook).getSheetAt(sheetNum); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + if (drawing == null) return "0"; + + int count = 0; + for (XSSFShape shape : drawing.getShapes()) { + if (!(shape instanceof XSSFGraphicFrame)) continue; + XSSFGraphicFrame frame = (XSSFGraphicFrame) shape; + try { + CTGraphicalObjectFrame ctFrame = frame.getCTGraphicalObjectFrame(); + CTGraphicalObjectData graphicData = ctFrame.getGraphic().getGraphicData(); + if ("http://schemas.openxmlformats.org/drawingml/2006/chart".equals(graphicData.getUri())) { + count++; + } + } catch (Exception e) { + // 忽略不是图表的 frame + } + } + return String.valueOf(count); + } + + + /** 获取形状数量(包含图表、图片、控件等) */ + public String getShapeCount(Workbook workbook, int sheetNum) { + if (!(workbook instanceof XSSFWorkbook)) return "0"; + XSSFSheet sheet = ((XSSFWorkbook) workbook).getSheetAt(sheetNum); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + if (drawing == null) return "0"; + return String.valueOf(drawing.getShapes().size()); + } + + /** 获取透视表数量 */ + public String getPivotTableCount(Workbook workbook, int sheetNum) { + if (!(workbook instanceof XSSFWorkbook)) return "0"; + XSSFSheet sheet = ((XSSFWorkbook) workbook).getSheetAt(sheetNum); + return String.valueOf(sheet.getPivotTables().size()); + } + + /** 获取批注数量 */ + public String getCommentsCount(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + int count = 0; + for (Row row : sheet) { + for (Cell cell : row) { + if (cell.getCellComment() != null) count++; + } + } + return String.valueOf(count); + } + + + // 水平分页符 + public String getHPageBreakCount(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "0"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTWorksheet ctSheet = xssfSheet.getCTWorksheet(); + CTPageBreak rowBreaks = ctSheet.getRowBreaks(); + if (rowBreaks == null || rowBreaks.sizeOfBrkArray() == 0) return "0"; + return String.valueOf(rowBreaks.getBrkList().size()); + } + // 垂直分页符 + public String getVPageBreakCount(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "0"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTWorksheet ctSheet = xssfSheet.getCTWorksheet(); + CTPageBreak colBreaks = ctSheet.getColBreaks(); + if (colBreaks == null || colBreaks.sizeOfBrkArray() == 0) return "0"; + return String.valueOf(colBreaks.getBrkList().size()); + } + + + //保护 + + private CTSheetProtection getProtection(XSSFSheet sheet) { + CTWorksheet ctSheet = sheet.getCTWorksheet(); + return ctSheet.isSetSheetProtection() ? ctSheet.getSheetProtection() : null; + } + + /** 允许→选定单元格方式 */ + public String allowSelectCells(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; // 没保护默认允许 + return protection.getSelectLockedCells() ? "是" : "否"; + } + + /** 允许→设置单元格格式 */ + public String allowFormatCells(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getFormatCells() ? "是" : "否"; + } + + /** 允许→设置列格式 */ + public String allowFormatColumns(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getFormatColumns() ? "是" : "否"; + } + + /** 允许→设置行格式 */ + public String allowFormatRows(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getFormatRows() ? "是" : "否"; + } + + /** 允许→插入列 */ + public String allowInsertColumns(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getInsertColumns() ? "是" : "否"; + } + + /** 允许→插入行 */ + public String allowInsertRows(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getInsertRows() ? "是" : "否"; + } + + /** 允许→插入超链接 */ + public String allowInsertHyperlinks(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getInsertHyperlinks() ? "是" : "否"; + } + + /** 允许→删除行 */ + public String allowDeleteRows(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getDeleteRows() ? "是" : "否"; + } + + /** 允许→删除列 */ + public String allowDeleteColumns(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getDeleteColumns() ? "是" : "否"; + } + + /** 允许→排序 */ + public String allowSort(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getSort() ? "是" : "否"; + } + + /** 允许→使用自动筛选 */ + public String allowAutoFilter(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getAutoFilter() ? "是" : "否"; + } + + /** 允许→使用数据透视表 */ + public String allowPivotTables(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getPivotTables() ? "是" : "否"; + } + + /** 允许→编辑对象 */ + public String allowEditObjects(Workbook workbook, int sheetNum) { + Sheet sheet = workbook.getSheetAt(sheetNum); + if (!(sheet instanceof XSSFSheet)) return "否"; + XSSFSheet xssfSheet = (XSSFSheet) sheet; + CTSheetProtection protection = getProtection(xssfSheet); + if (protection == null) return "否"; + return protection.getObjects() ? "是" : "否"; + } + + + +} + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java new file mode 100644 index 00000000..38336eab --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java @@ -0,0 +1,196 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.table; + +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.usermodel.XSSFTable; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSortCondition; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSortState; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.lang.reflect.Method; + +public class TableIng { + + // 获取表格名称 + public String getName(XSSFTable table, Workbook wb) { + return table.getName(); + } + + // 获取表格地址(引用区域) + public String getAddress(XSSFTable table, Workbook wb) { + return table.getCTTable().getRef(); + } + + // 获取表格行数 + public String getRowCount(XSSFTable table, Workbook wb) { + CellReference start = new CellReference(table.getStartCellReference().formatAsString()); + CellReference end = new CellReference(table.getEndCellReference().formatAsString()); + int rowCount = end.getRow() - start.getRow() + 1; + return String.valueOf(rowCount); + } + + // 获取表格列数 + public String getColCount(XSSFTable table, Workbook wb) { + CellReference start = new CellReference(table.getStartCellReference().formatAsString()); + CellReference end = new CellReference(table.getEndCellReference().formatAsString()); + int colCount = end.getCol() - start.getCol() + 1; + return String.valueOf(colCount); + } + + // 是否启用了自动筛选 + public String hasAutoFilter(XSSFTable table, Workbook wb) { + return table.getCTTable().isSetAutoFilter() ? "是" : "否"; + } + + + //表样式 + //名称 + public String getTableStyleName(XSSFTable table, Workbook wb) { + if (!table.getCTTable().isSetTableStyleInfo()) { + return "无"; + } + XmlObject styleInfo = table.getCTTable().getTableStyleInfo(); + Node node = styleInfo.getDomNode(); + if (node == null) { + return "无"; + } + if (node.getAttributes() == null) { + return "无"; + } + for (int i = 0; i < node.getAttributes().getLength(); i++) { + Node attr = node.getAttributes().item(i); + System.out.println(attr.getNodeName() + " = " + attr.getNodeValue()); + } + Node attr = node.getAttributes().getNamedItem("name"); + if (attr != null) { + // 适应不同格式 + String val = attr.getNodeValue().toLowerCase(); + return val; + } + return "无"; + } + + //选项→标题行 + public String hasHeaderRow(XSSFTable table, Workbook wb) { + try { + int headerRowCount = table.getHeaderRowCount(); // 1表示有标题行,0表示无 + return headerRowCount > 0 ? "是" : "否"; + } catch (Exception e) { + // 该方法不存在或异常,返回否 + return "否"; + } + } + //选项→汇总行 + public String hasTotalsRow(XSSFTable table, Workbook wb) { + try { + // 直接用CTTable对应的xml节点判断totalsRowCount属性 + XmlObject xmlObject = table.getCTTable(); + Node node = xmlObject.getDomNode(); + printNodeDetails(node); + if (node != null && node.getAttributes() != null) { + Node totalsAttr = node.getAttributes().getNamedItem("totalsRowCount"); + if (totalsAttr != null) { + return "1".equals(totalsAttr.getNodeValue()) ? "是" : "否"; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "否"; + } + + //排序 -排序条件 + public String getSortConditions(XSSFTable table, Workbook wb) { + if (!table.getCTTable().isSetSortState()) { + return "无排序条件"; + } + CTSortState sortState = table.getCTTable().getSortState(); + if (sortState == null) { + return "无排序条件"; + } + + CTSortCondition[] conditions = sortState.getSortConditionArray(); + if (conditions == null || conditions.length == 0) { + return "无排序条件"; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < conditions.length; i++) { + CTSortCondition cond = conditions[i]; + String ref = (cond.getRef() != null) ? cond.getRef() : "未知区域"; + String order = cond.getDescending() ? "降序" : "升序"; + sb.append("排序条件").append(i + 1).append(": 区域[").append(ref).append("], 顺序[").append(order).append("]\n"); + } + + return sb.toString().trim(); + } + + + + + public void printNodeDetails(Node node) { + Node tableStyleInfoNode = null; + NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + if ("tableStyleInfo".equals(child.getNodeName())) { + tableStyleInfoNode = child; + break; + } + } + + if (tableStyleInfoNode != null) { + System.out.println("tableStyleInfo attributes:"); + NamedNodeMap attrs = tableStyleInfoNode.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node attr = attrs.item(i); + System.out.println(" - " + attr.getNodeName() + " = " + attr.getNodeValue()); + } + } else { + System.out.println("No tableStyleInfo node found"); + } + + } + // 选项→第一列 + public String hasFirstColumnStyle(XSSFTable table, Workbook wb) { + if (table.getCTTable().isSetTableStyleInfo()) { + return table.getCTTable().getTableStyleInfo().getShowFirstColumn() ? "是" : "否"; + } + return "否"; + } + // 选项→最后一列 + public String hasLastColumnStyle(XSSFTable table, Workbook wb) { + if (table.getCTTable().isSetTableStyleInfo()) { + return table.getCTTable().getTableStyleInfo().getShowLastColumn() ? "是" : "否"; + } + return "否"; + } + //选项→镶边行 + public String hasBandedRows(XSSFTable table, Workbook wb) { + if (table.getCTTable().isSetTableStyleInfo()) { + return table.getCTTable().getTableStyleInfo().getShowRowStripes() ? "是" : "否"; + } + return "否"; + } + //选项→镶边列 + public String hasBandedColumns(XSSFTable table, Workbook wb) { + if (table.getCTTable().isSetTableStyleInfo()) { + return table.getCTTable().getTableStyleInfo().getShowColumnStripes() ? "是" : "否"; + } + return "否"; + } + //排序→排序条件 + public String hasSortConditions(XSSFTable table, Workbook wb) { + if (table.getCTTable().isSetSortState()) { + return "有排序条件"; + } + return "无排序条件"; + } + + + +} + diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/view/Viewing.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/view/Viewing.java new file mode 100644 index 00000000..61cf4146 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/view/Viewing.java @@ -0,0 +1,154 @@ +package pc.exam.pp.module.judgement.utils.wps_excel.xlsx4j.view; + +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.PaneInformation; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetView; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetViews; + +public class Viewing { + + // 常规 视图名称 + public String getViewName(Workbook workbook, int sheetIndex) { + if (!(workbook instanceof XSSFWorkbook)) { + return "只支持XSSF格式的工作簿"; + } + XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook; + if (sheetIndex < 1 || sheetIndex > xssfWorkbook.getNumberOfSheets()) { + return "页码超出范围"; + } + XSSFSheet sheet = xssfWorkbook.getSheetAt(sheetIndex - 1); + if (sheet == null) return "无"; + + CTSheetViews sheetViews = sheet.getCTWorksheet().getSheetViews(); + if (sheetViews == null || sheetViews.getSheetViewList().isEmpty()) { + return "无视图信息"; + } + + CTSheetView sheetView = sheetViews.getSheetViewList().get(0); + String viewType = String.valueOf(sheetView.getView()); + + switch (viewType) { + case "normal": + return "普通视图"; + case "pageBreakPreview": + return "分页预览"; + case "pageLayout": + return "页面布局"; + default: + return "未知视图类型:" + viewType; + } + } + + // 公式审核 显示公式(true/false) + public String isShowFormulas(Workbook workbook, int sheetIndex) { + if (!(workbook instanceof XSSFWorkbook)) return "只支持XSSF格式"; + XSSFSheet xssfSheet = ((XSSFWorkbook) workbook).getSheetAt(sheetIndex - 1); + CTSheetViews sheetViews = xssfSheet.getCTWorksheet().getSheetViews(); + if(sheetViews != null && !sheetViews.getSheetViewList().isEmpty()) { + CTSheetView sheetView = sheetViews.getSheetViewList().get(0); + boolean showFormulas = sheetView.getShowFormulas(); + return showFormulas ? "是" : "否"; + } + return "否"; + } + + // 显示/隐藏 标尺(Ruler) - Apache POI没有直接接口,一般视图中没有标尺属性,返回未知 + public String isShowRuler(Workbook workbook, int sheetIndex) { + if (!(workbook instanceof XSSFWorkbook)) return "只支持XSSF格式"; + XSSFSheet xssfSheet = ((XSSFWorkbook) workbook).getSheetAt(sheetIndex - 1); + CTSheetViews sheetViews = xssfSheet.getCTWorksheet().getSheetViews(); + if(sheetViews != null && !sheetViews.getSheetViewList().isEmpty()) { + CTSheetView sheetView = sheetViews.getSheetViewList().get(0); + boolean showRuler = sheetView.getShowRuler(); + return showRuler ? "是" : "否"; + } + return "否"; + } + + // 显示/隐藏 网格线 + public String isShowGridLines(Workbook workbook, int sheetIndex) { + + if (!(workbook instanceof XSSFWorkbook)) return "只支持XSSF格式"; + XSSFSheet xssfSheet = ((XSSFWorkbook) workbook).getSheetAt(sheetIndex - 1); + CTSheetViews sheetViews = xssfSheet.getCTWorksheet().getSheetViews(); + if(sheetViews != null && !sheetViews.getSheetViewList().isEmpty()) { + CTSheetView sheetView = sheetViews.getSheetViewList().get(0); + boolean showGridLines = sheetView.getShowGridLines(); + return showGridLines ? "是" : "否"; + } + return "否"; + } + + // 显示/隐藏 显示行号列标 + public String isShowRowColHeaders(Workbook workbook, int sheetIndex) { + if (!(workbook instanceof XSSFWorkbook)) return "只支持XSSF格式"; + XSSFSheet xssfSheet = ((XSSFWorkbook) workbook).getSheetAt(sheetIndex - 1); + CTSheetViews sheetViews = xssfSheet.getCTWorksheet().getSheetViews(); + if(sheetViews != null && !sheetViews.getSheetViewList().isEmpty()) { + CTSheetView sheetView = sheetViews.getSheetViewList().get(0); + boolean showRowColHeaders = sheetView.getShowRowColHeaders(); + return showRowColHeaders ? "是" : "否"; + } + return "否"; + } + + // 缩放 缩放比例(百分比) + public String getZoomScale(Workbook workbook, int sheetIndex) { + if (!(workbook instanceof XSSFWorkbook)) return "只支持XSSF格式"; + XSSFSheet xssfSheet = ((XSSFWorkbook) workbook).getSheetAt(sheetIndex - 1); + CTSheetViews sheetViews = xssfSheet.getCTWorksheet().getSheetViews(); + if(sheetViews != null && !sheetViews.getSheetViewList().isEmpty()) { + CTSheetView sheetView = sheetViews.getSheetViewList().get(0); + long sheetViewZoomScale = sheetView.getZoomScale(); + return String.valueOf(sheetViewZoomScale); + } + return "无"; + } + + // 冻结 冻结窗格 + public String isPaneFrozen(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet == null) return "无"; + + PaneInformation paneInfo = sheet.getPaneInformation(); + if (paneInfo == null || !paneInfo.isFreezePane()) { + return "无"; + } + boolean freezeFirstRow = paneInfo.getHorizontalSplitPosition() == 1; + boolean freezeFirstColumn = paneInfo.getVerticalSplitPosition() == 1; + + if (freezeFirstRow && freezeFirstColumn) { + return "冻结首行+冻结首列"; + } else if (freezeFirstRow) { + return "冻结首行"; + } else if (freezeFirstColumn) { + return "冻结首列"; + } else { + return "冻结窗格(非首行首列)"; + } + } + + + // 拆分 拆分→行数 + public String getSplitRow(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet == null) return "无"; + PaneInformation paneInfo = sheet.getPaneInformation(); + if (paneInfo == null) return "无"; + int ySplit = paneInfo.getVerticalSplitPosition(); + return ySplit > 0 ? String.valueOf(ySplit) : "无"; + } + + // 拆分→列数 + public String getSplitColumn(Workbook workbook, int sheetIndex) { + Sheet sheet = workbook.getSheetAt(sheetIndex - 1); + if (sheet == null) return "无"; + PaneInformation paneInfo = sheet.getPaneInformation(); + if (paneInfo == null) return "无"; + int xSplit = paneInfo.getHorizontalSplitPosition(); + return xSplit > 0 ? String.valueOf(xSplit) : "无"; + } +} 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 be235b99..37bda365 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 @@ -3,17 +3,23 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; +import org.docx4j.openpackaging.parts.relationships.Namespaces; import org.springframework.web.multipart.MultipartFile; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.DocxDataInfoVO; import pc.exam.pp.module.judgement.utils.wps_word.utils.WpsWordNameSpaces; import pc.exam.pp.module.judgement.utils.wps_word.utils.XmlUtil; +import javax.xml.namespace.QName; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class DocxConversion { + public static final String W_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + public static List DocxDataInfos(MultipartFile file) throws Exception { List dataInfoVOS = new ArrayList<>(); try (InputStream inputStream = file.getInputStream()) { @@ -46,80 +52,200 @@ public class DocxConversion { // 获取文本 XmlCursor wpTextXml = wpCursor.newCursor(); wpTextXml.selectPath(namespace + ".//w:r/w:t"); - if (wpTextXml.toNextSelection()) { - String secondIdWp = getStringRandom(); - String text = wpTextXml.getTextValue(); - text = shorten(text); - setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + text, "w:p", String.valueOf(wpIndex), true, dataInfoVOS); - // 使用 。 判断句子 - String[] texts = text.split("。"); - int textsIndex = 0; - for (String s : texts) { - String thirdIdWp = getStringRandom(); - textsIndex ++; - s = shorten(s); - setWordDataInfo(thirdIdWp, secondIdWp, "句子" + textsIndex + ":" + s, "w:t", String.valueOf(wpIndex), true, dataInfoVOS); + StringBuilder fullTextBuilder = new StringBuilder(); + while (wpTextXml.toNextSelection()) { + String part = wpTextXml.getTextValue(); + if (part != null) { + fullTextBuilder.append(part); } } + String fullText = fullTextBuilder.toString().trim(); + if (!fullText.isEmpty()) { + String secondIdWp = getStringRandom(); + String shortText = shorten(fullText); // 截断展示 + setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + shortText, "w:p", String.valueOf(wpIndex), true, dataInfoVOS); + + String[] sentences = fullText.split("。"); + int sentenceIndex = 0; + for (String sentence : sentences) { + sentence = sentence.trim(); + if (!sentence.isEmpty()) { + sentenceIndex++; + String thirdIdWp = getStringRandom(); + setWordDataInfo(thirdIdWp, secondIdWp, "句子" + sentenceIndex + ":" + shorten(sentence), "w:t", String.valueOf(wpIndex), true, dataInfoVOS); + } + } + } + } } // 2、页面 XmlCursor wSectPrCursor = docXml.newCursor(); - String wSectPrPath = namespace + "//w:sectPr"; + String wSectPrPath = namespace + "//w:sectPr"; // 查找所有节 wSectPrCursor.selectPath(wSectPrPath); + int wSectPrIndex = 0; + // 一级节点:页面 String firstIdSectPr = getStringRandom(); setWordDataInfo(firstIdSectPr, "", "页面", "w:sectPr", "", false, dataInfoVOS); + + // 遍历所有节 while (wSectPrCursor.toNextSelection()) { - wSectPrIndex ++; + wSectPrIndex++; String secondIdSectPr = getStringRandom(); - setWordDataInfo(secondIdSectPr, firstIdSectPr, "节" + wSectPrIndex, "w:sectPr", String.valueOf(wSectPrIndex), true, dataInfoVOS); + setWordDataInfo( + secondIdSectPr, + firstIdSectPr, + "节" + wSectPrIndex, + "w:sectPr", + String.valueOf(wSectPrIndex), + true, + dataInfoVOS + ); } -// // 3、图形 -// XmlCursor wDrawingCursor = docXml.newCursor(); -// String wDrawingPath = namespace + "//w:drawing"; -// wDrawingCursor.selectPath(wDrawingPath); -// int wDrawingIndex = 0; -// String firstIdDrawing = getStringRandom(); -// setWordDataInfo(firstIdDrawing, "", "图形", "w:drawing", "", false, dataInfoVOS); -// while (wDrawingCursor.toNextSelection()) { -// wDrawingIndex ++; -// String secondIdDrawing = getStringRandom(); -// XmlCursor wDrawingXml = wDrawingCursor.newCursor(); -// wDrawingXml.selectPath(namespace + ".//wp:anchor/wp:docPr/@name"); -// if (wDrawingXml.toNextSelection()) { -// setWordDataInfo(firstIdSectPr, firstIdDrawing, "图形" + wDrawingIndex + ":" + wDrawingXml.getTextValue(), "w:drawing", String.valueOf(wDrawingIndex), true, dataInfoVOS); -// setWordInfo("图形" + wDrawingIndex + ":" + wDrawingXml.getTextValue(), "(//w:drawing)[" + wDrawingIndex + "]", "w:drawing", filePath, secondIdDrawing, firstIdDrawing, wordInfoReqVos); -// } -// } + + // 3、图形 + XmlCursor wDrawingCursor = docXml.newCursor(); + String wDrawingPath = namespace + "//w:drawing"; + wDrawingCursor.selectPath(wDrawingPath); + + // 用于记录图片和文本框序号 + int pictureIndex = 0; + int textBoxIndex = 0; + + String firstIdDrawing = getStringRandom(); + String firstIdSectTr = getStringRandom(); + setWordDataInfo(firstIdDrawing, "", "图形", "w:drawing", "", false, dataInfoVOS); + + while (wDrawingCursor.toNextSelection()) { + String secondIdDrawing = getStringRandom(); + + // 先取这个 drawing 的名称 + XmlCursor wDrawingXml = wDrawingCursor.newCursor(); + wDrawingXml.selectPath(namespace + ".//wp:anchor/wp:docPr/@name"); + + String shapeName = ""; + if (wDrawingXml.toNextSelection()) { + shapeName = wDrawingXml.getTextValue(); + System.out.println("名称: " + shapeName); + } + + XmlCursor innerCursor = wDrawingCursor.newCursor(); + + boolean isPicture = false; + boolean isTextBox = false; + + // 判断是否含有 + innerCursor.selectPath(namespace + ".//pic:pic"); + if (innerCursor.toNextSelection()) { + isPicture = true; + } else { + // 判断是否含有 + innerCursor = wDrawingCursor.newCursor(); + innerCursor.selectPath(namespace + ".//wps:txbx"); + if (innerCursor.toNextSelection()) { + isTextBox = true; + } + } + + String label = ""; + String seq = ""; // 具体类型序号 + + if (isPicture) { + pictureIndex++; + seq = String.valueOf(pictureIndex); + label = "图形1:图片 " + seq; + } else if (isTextBox) { + textBoxIndex++; + seq = String.valueOf(textBoxIndex); + label = "图形2:文本框 " + seq; + } else { + label = "图形未知类型"; + } + + setWordDataInfo(firstIdSectTr, firstIdDrawing, label, "w:drawing", seq, true, dataInfoVOS); + } + //域 + XmlCursor instrTextCursor = docXml.newCursor(); + String instrTextPath = namespace + "//w:instrText"; + instrTextCursor.selectPath(instrTextPath); + + int instrTextIndex = 0; + String firstIdInstrText = getStringRandom(); + setWordDataInfo(firstIdInstrText, "", "域", "w:instrText", "", false, dataInfoVOS); + + while (instrTextCursor.toNextSelection()) { + instrTextIndex++; + String secondIdInstrText = getStringRandom(); + String instrTextContent = instrTextCursor.getTextValue(); + + String shortText = extractQuotedContent(instrTextContent); // 如果有这个方法截断文本 + setWordDataInfo(secondIdInstrText, firstIdInstrText, + "域" + instrTextIndex + ":" + shortText, + "w:instrText", + String.valueOf(instrTextIndex), + true, + dataInfoVOS); + } + + // // 4、尾注 -// XmlCursor endnoteReferenceCursor = docXml.newCursor(); -// String endnoteReferencePath = namespace + "//w:endnoteReference"; -// endnoteReferenceCursor.selectPath(endnoteReferencePath); -// int endnoteReferenceIndex = 0; -// String firstIdEndnoteReference = getStringRandom(); -// while (endnoteReferenceCursor.toNextSelection()) { -// if (endnoteReferenceIndex == 0) { -// setWordInfo("引用", "w:endnoteReference", "w:endnoteReference", filePath, firstIdEndnoteReference, "", wordInfoReqVos); -// } -// endnoteReferenceIndex ++; -// String secondIdEndnoteReference = getStringRandom(); -// setWordInfo("尾注", "(//w:endnoteReference)[" + endnoteReferenceIndex + "]", "w:endnoteReference", filePath, secondIdEndnoteReference, firstIdEndnoteReference, wordInfoReqVos); -// } -// // 5、表格 -// XmlCursor tblCursor = docXml.newCursor(); -// String tblCursorPath = namespace + "//w:tbl"; -// endnoteReferenceCursor.selectPath(tblCursorPath); -// int tblIndex = 0; -// String firstIdTbl = getStringRandom(); -// while (endnoteReferenceCursor.toNextSelection()) { -// if (tblIndex == 0) { -// setWordInfo("表格", "w:endnoteReference", "w:endnoteReference", filePath, firstIdTbl, "", wordInfoReqVos); -// } -// tblIndex ++; -// String secondIdTbl = getStringRandom(); -// setWordInfo("表格:"+tblIndex, "(//w:tbl)[" + tblIndex + "]", "w:tbl", filePath, secondIdTbl, firstIdTbl, wordInfoReqVos); -// } + + XmlCursor endnoteReferenceCursor = docXml.newCursor(); + String endnoteReferencePath = namespace + "//w:endnoteReference"; + endnoteReferenceCursor.selectPath(endnoteReferencePath); + + int endnoteIndex = 0; + String firstIdEndnote = getStringRandom(); + setWordDataInfo(firstIdEndnote, "", "尾注", "w:endnoteReference", "", false, dataInfoVOS); + + while (endnoteReferenceCursor.toNextSelection()) { + // 获取尾注的 id 属性(w:id) + String idValue = endnoteReferenceCursor.getAttributeText(new QName(W_NAMESPACE, "id")); + + + endnoteIndex++; + String secondIdEndnote = getStringRandom(); + + setWordDataInfo( + secondIdEndnote, + firstIdEndnote, + "尾注" + endnoteIndex + (idValue == null || idValue.isEmpty() ? "" : (":" + idValue)), + "w:endnoteReference", + String.valueOf(endnoteIndex), + true, + dataInfoVOS + ); + } + + + // 5、表格 + XmlCursor tblCursor = docXml.newCursor(); + String tblCursorPath = namespace + "//w:tbl"; + tblCursor.selectPath(tblCursorPath); + + int tblIndex = 0; + String firstIdTbl = getStringRandom(); + // 记录父节点信息 + setWordDataInfo(firstIdTbl, "", "表格", "w:tbl", "", false, dataInfoVOS); + + while (tblCursor.toNextSelection()) { + tblIndex++; + String secondIdTbl = getStringRandom(); + + setWordDataInfo( + secondIdTbl, + firstIdTbl, + "表格" + tblIndex, + "w:tbl", + String.valueOf(tblIndex), + true, + dataInfoVOS + ); + } + + + // // 5、域 // XmlCursor yuCursor = docXml.newCursor(); // String yuCursorPath = namespace + "//w:instrText"; @@ -176,4 +302,22 @@ public class DocxConversion { String suffix = input.substring(input.length() - 2); return prefix + "..." + suffix; } + public static String extractQuotedContent(String text) { + if (text == null) return ""; + Pattern pattern = Pattern.compile("\"([^\"]+)\""); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + String content = matcher.group(1); + if (content.length() > 20) { + String prefix = content.substring(0, 6); + String suffix = content.substring(content.length() - 2); + return prefix + "..." + suffix; + } else { + return content; + } + } + return text; // 如果没匹配到,就返回原字符串 + } + + } 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 8752d02e..92f48b4b 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 @@ -2,18 +2,23 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j; import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.JAXBException; +import org.apache.commons.io.IOUtils; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.ZipPackage; +import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; import org.docx4j.XmlUtils; import org.docx4j.com.microsoft.schemas.office.word.x2010.wordprocessingShape.CTWordprocessingShape; import org.docx4j.dml.CTBlipFillProperties; import org.docx4j.dml.CTStretchInfoProperties; import org.docx4j.mce.AlternateContent; +import org.docx4j.model.structure.HeaderFooterPolicy; import org.docx4j.model.structure.SectionWrapper; import org.docx4j.dml.wordprocessingDrawing.Anchor; +import org.docx4j.model.structure.jaxb.Sections; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.*; @@ -22,13 +27,20 @@ import org.docx4j.wml.*; import org.springframework.web.multipart.MultipartFile; import org.w3c.dom.Node; import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsDocxInfoVo; +import pc.exam.pp.module.judgement.utils.wps_word.docx4j.endNote.EndNoteing; +import pc.exam.pp.module.judgement.utils.wps_word.docx4j.insert.InsertIng; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Paragraphs; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.RunText; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.section.SectionPage; +import pc.exam.pp.module.judgement.utils.wps_word.docx4j.table.TableIng; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.text.TextInfo; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.JudgementWordsVO; +import pc.exam.pp.module.judgement.utils.wps_word.utils.WpsWordNameSpaces; +import pc.exam.pp.module.judgement.utils.wps_word.utils.XmlUtil; +import javax.xml.namespace.QName; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -37,11 +49,14 @@ import java.lang.reflect.Method; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author REN */ public class DocxMaster { + private static final String W_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; /** * @@ -57,17 +72,20 @@ public class DocxMaster { * @throws InvocationTargetException * @throws IllegalAccessException */ - public static List docxMaster(List wpsDocxInfoVos, MultipartFile file) throws Docx4JException, InvalidFormatException, IOException, JAXBException, XmlException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + public static List docxMaster(List wpsDocxInfoVos, MultipartFile file) throws Exception { // 一共分为 段落, 页面,图形 String firstDuanLuoId = Convert.getStringRandom(); String firstYeMianId = Convert.getStringRandom(); String firstTuXingId = Convert.getStringRandom(); List judgementWordsVOS = new ArrayList<>(); + byte[] fileBytes = file.getBytes(); // 直接读全部字节,Commons IO的toByteArray可能出问题,MultipartFile自带getBytes() // 1、获取想要判断的文件地址(文件流) + InputStream docx4jStream = new ByteArrayInputStream(fileBytes); + WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(docx4jStream); + InputStream inputStream = file.getInputStream(); // 2、Docx转换对象 - WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream); List paragraphs = wordMLPackage.getMainDocumentPart().getContent(); // 2-1、样式对象 StyleDefinitionsPart stylePart = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart(); @@ -80,6 +98,8 @@ public class DocxMaster { // 2-4、文档设置 DocumentSettingsPart settingsPart = wordMLPackage.getMainDocumentPart().getDocumentSettingsPart(); wordMLPackage.getHeaderFooterPolicy().getDefaultFooter(); + // 3. 获取尾注部分XmlObject(endnotes.xml) + EndnotesPart endnotesPart = wordMLPackage.getMainDocumentPart().getEndNotesPart(); for (WpsDocxInfoVo wpsDocxInfoVo : wpsDocxInfoVos) { // 参数实例化 // 大类名称 @@ -93,6 +113,11 @@ public class DocxMaster { // 考点代码 String examCode = wpsDocxInfoVo.getExamCode(); String docxFunction = firstName + "@" + indexParm + "@" + function + "@" + examName + "@" + examCode; + InputStream poiStream = new ByteArrayInputStream(fileBytes); + XWPFDocument document = new XWPFDocument(poiStream); + String xmlString = XmlUtil.getDocumentXml(document); + String namespace = WpsWordNameSpaces.getNameSpace(xmlString); + XmlObject docXml = document.getDocument(); // 创建一个索引ID,跟参数中index进行匹配 int index = 0; for (Object obj : paragraphs) { @@ -101,7 +126,7 @@ public class DocxMaster { if (realObj instanceof P) { P paragraph = (P) realObj; PPr pPr = paragraph.getPPr(); - if (pPr == null || pPr.getSectPr() == null) { + if (pPr == null || pPr.getSectPr() == null&& firstName.contains("段落")) { // 开始查询段落的属性,非句子的属性 if (index == Integer.parseInt(indexParm)) { // 查询出具体想要查询哪个段落的数据 @@ -114,11 +139,449 @@ public class DocxMaster { // 实际参数值 Object[] arguments = {paragraph, stylePart, wordMLPackage, ndp}; String value = (String) methodWithArgs.invoke(obj, arguments); - judgementWordsVOS = setJudgementWord(judgementWordsVOS, docxFunction + "@" + value, firstName + examName + value); + if (value!=null){ + // 写入结果 + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } } } } } + int i = 0; + for (SectionWrapper section : sections) { + SectionWrapper sectionWrapper = section; + ++i; + SectPr sectPr = section.getSectPr(); + + if (sectPr != null && firstName.contains("节")) { + if (i == Integer.parseInt(indexParm)) { + // 构造一个伪段落,包裹 sectPr 传入方法 + P dummyP = new P(); + PPr pPr = new PPr(); + pPr.setSectPr(sectPr); + dummyP.setPPr(pPr); + + SectionPage sectionsFunction = new SectionPage(); + Class[] paramTypes = {P.class, HeaderFooterPolicy.class, WordprocessingMLPackage.class,List.class}; + Method methodWithArgs = sectionsFunction.getClass().getMethod(function,paramTypes); + Object[] arguments = {dummyP, sectionWrapper.getHeaderFooterPolicy(), wordMLPackage,sections}; + String value = null; + try { + value = (String) methodWithArgs.invoke(sectionsFunction, arguments); + } catch (InvocationTargetException e) { + e.getCause().printStackTrace(); // 打印真正的异常 + } + + if (value!=null){ + // 写入结果 + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + } + } + } + if (firstName.contains("句子")) { + // 修改的正则,支持非贪婪匹配到】结束,包括中间包含换行、点号等 + Pattern pattern = Pattern.compile("【(句子\\d+):(.+?)】"); + Matcher matcher = pattern.matcher(firstName); + if (!matcher.find()) continue; + + String sentenceLabel = matcher.group(1); // 句子1 + String expectedText = matcher.group(2).trim(); // 期望内容 + + int paragraphIndex = 0; + + for (Object obj : paragraphs) { + paragraphIndex++; + Object realObj = XmlUtils.unwrap(obj); + + if (realObj instanceof P) { + P paragraph = (P) realObj; + + List paragraphContent = paragraph.getContent(); + + for (Object part : paragraphContent) { + Object contentObj = XmlUtils.unwrap(part); + if (contentObj instanceof R) { + + 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(); + } + } + + } + } + } + } + } + + } + if (firstName.contains("图片")) { + // 从 firstName 中提取图形编号,例如 【图形1:图片2】 + Pattern pattern = Pattern.compile("【图形(\\d+):(.+?)】"); + Matcher matcher = pattern.matcher(firstName); + if (!matcher.find()) continue; + int targetIndex = Integer.parseInt(indexParm); // 要找的图片序号 + int currentImageIndex = 0; + // 使用 XmlCursor 查找 + try (XmlCursor drawingCursor = docXml.newCursor()) { + drawingCursor.selectPath(namespace + "//w:drawing"); + + while (drawingCursor.toNextSelection()) { + // 判断是否含有 + boolean hasPicture = false; + try (XmlCursor picCursor = drawingCursor.newCursor()) { + picCursor.selectPath(namespace + ".//pic:pic"); + if (picCursor.toNextSelection()) { + hasPicture = true; + } + } + + if (hasPicture) { + currentImageIndex++; + if (currentImageIndex == targetIndex) { + try { + // 将当前选中的 转换成 Drawing 对象 + Object drawingObj = XmlUtils.unmarshal(drawingCursor.getDomNode()); + if (drawingObj instanceof org.docx4j.wml.Drawing) { + org.docx4j.wml.Drawing drawingElement = (org.docx4j.wml.Drawing) drawingObj; + // 找到 Anchor + org.docx4j.dml.wordprocessingDrawing.Anchor anchor = null; + org.docx4j.dml.wordprocessingDrawing.Inline inline = null; + for (Object dChild : drawingElement.getAnchorOrInline()) { + System.out.println("======= 原始对象类型: " + dChild.getClass().getName() + " ======="); + Object value = dChild; + if (dChild instanceof JAXBElement) { + value = ((JAXBElement) dChild).getValue(); + } + if (value instanceof org.docx4j.dml.wordprocessingDrawing.Anchor) { + anchor = (org.docx4j.dml.wordprocessingDrawing.Anchor) value; + break; + } else if (value instanceof org.docx4j.dml.wordprocessingDrawing.Inline) { + inline = (org.docx4j.dml.wordprocessingDrawing.Inline) value; + break; + } + } + + if (anchor != null) { + // 反射调用目标方法 + Class drawingClass = pc.exam.pp.module.judgement.utils.wps_word.docx4j.drawing.Drawing.class; + Method method = drawingClass.getMethod( + function, + List.class, + org.docx4j.dml.wordprocessingDrawing.Anchor.class, + int.class, + WordprocessingMLPackage.class + ); + + String value = (String) method.invoke( + null, + judgementWordsVOS, + anchor, + currentImageIndex, + wordMLPackage + ); + + if (value!=null){ + // 写入结果 + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + } + + if (inline!=null){ + // 反射调用目标方法 + Class drawingClass = pc.exam.pp.module.judgement.utils.wps_word.docx4j.drawing.DrawingInline.class; + Method method = drawingClass.getMethod( + function, + List.class, + org.docx4j.dml.wordprocessingDrawing.Inline.class, + int.class, + WordprocessingMLPackage.class + ); + + String value = (String) method.invoke( + null, + judgementWordsVOS, + inline, + currentImageIndex, + wordMLPackage + ); + if (value!=null){ + // 写入结果 + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + + } + } + } catch (InvocationTargetException e) { + e.getCause().printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + break; // 找到目标退出循环 + } + } + } + } + } + + + if (firstName.contains("文本框")) { + // 从 firstName 中提取图形编号,例如【图形1:文本框2】 + Pattern pattern = Pattern.compile("【图形(\\d+):(.+?)】"); + Matcher matcher = pattern.matcher(firstName); + if (!matcher.find()) continue; + + int targetIndex = Integer.parseInt(indexParm); // 目标文本框序号 + int currentTextBoxIndex = 0; + + try (XmlCursor drawingCursor = docXml.newCursor()) { + drawingCursor.selectPath(namespace + "//w:drawing"); + + while (drawingCursor.toNextSelection()) { + boolean hasTextBox = false; + try (XmlCursor txbxCursor = drawingCursor.newCursor()) { + txbxCursor.selectPath(namespace + ".//wps:txbx"); + if (txbxCursor.toNextSelection()) { + hasTextBox = true; + } + } + + if (hasTextBox) { + currentTextBoxIndex++; + if (currentTextBoxIndex == targetIndex) { + try { + // 将当前 转成 Drawing 对象 + Object drawingObj = XmlUtils.unmarshal(drawingCursor.getDomNode()); + if (drawingObj instanceof org.docx4j.wml.Drawing) { + org.docx4j.wml.Drawing drawingElement = (org.docx4j.wml.Drawing) drawingObj; + + // 打印 XML 方便调试 + String drawingXml = XmlUtils.marshaltoString(drawingElement, true, true); + System.out.println("Drawing XML:\n" + drawingXml); + + // 找 Anchor 或 Inline + org.docx4j.dml.wordprocessingDrawing.Anchor anchor = null; + org.docx4j.dml.wordprocessingDrawing.Inline inline = null; + for (Object dChild : drawingElement.getAnchorOrInline()) { + if (dChild instanceof JAXBElement) { + Object value = ((JAXBElement) dChild).getValue(); + if (value instanceof org.docx4j.dml.wordprocessingDrawing.Anchor) { + anchor = (org.docx4j.dml.wordprocessingDrawing.Anchor) value; + System.out.println(1); + break; + } + } else if (dChild instanceof org.docx4j.dml.wordprocessingDrawing.Anchor) { + System.out.println(2); + // 直接是Anchor对象 + anchor = (org.docx4j.dml.wordprocessingDrawing.Anchor) dChild; + break; + } else if (dChild instanceof org.docx4j.dml.wordprocessingDrawing.Inline) { + System.out.println(3); + inline = (org.docx4j.dml.wordprocessingDrawing.Inline) dChild; + break; + } + } + + + if (anchor != null) { + // 反射调用文本框处理方法(传 Anchor) + Class textClass = pc.exam.pp.module.judgement.utils.wps_word.docx4j.text.TextInfo.class; + String value = null; + try { + Method method = textClass.getMethod(function, List.class, + org.docx4j.dml.wordprocessingDrawing.Anchor.class, + int.class, WordprocessingMLPackage.class); + value = (String) method.invoke(null, judgementWordsVOS, anchor, currentTextBoxIndex, wordMLPackage); + } catch (NoSuchMethodException e) { + // 方法不存在,跳过 + System.out.println("方法不存在,跳过: " + function); + } catch (IllegalAccessException | InvocationTargetException e) { + // 方法无法调用或内部异常,也跳过或打印日志 + e.printStackTrace(); + } + if (value != null) { + judgementWordsVOS = setJudgementWord(judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value); + } + } else { + System.out.println("文本框中没有 Anchor 或 Inline 对象"); + } + } + }catch (Exception e) { + e.printStackTrace(); + } + break; // 找到目标退出循环 + } + } + } + } + } + + if (firstName.contains("域")) { + XmlCursor instrTextCursor = docXml.newCursor(); + String instrTextPath = namespace + "//w:instrText"; + instrTextCursor.selectPath(instrTextPath); + + int instrIndex = 0; + while (instrTextCursor.toNextSelection()) { + instrIndex++; + if (instrIndex != Integer.parseInt(indexParm)) continue; + + // 传当前cursor(指向w:instrText节点)给 InsertIng + try { + Class insertIngClass = InsertIng.class; + Method method = insertIngClass.getMethod(function, XmlCursor.class); + String value = (String) method.invoke(null, instrTextCursor); + if (value != null) { + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + } catch (Exception e) { + e.printStackTrace(); + } + + break; + } + } + String namespaces = "declare namespace w='" + W_NAMESPACE + "' "; + + // 5. 尾注 + if (firstName.contains("尾注")) { + XmlCursor endnoteRefCursor = docXml.newCursor(); + String endnoteRefPath = namespaces + "//w:endnoteReference"; + endnoteRefCursor.selectPath(endnoteRefPath); + + if (endnotesPart == null) { + System.out.println("[提示] 该文档无尾注部分(endnotes.xml)"); + continue; + } + CTEndnotes endnotes = endnotesPart.getContents(); + + int noteIndex = 0; + while (endnoteRefCursor.toNextSelection()) { + String id = endnoteRefCursor.getAttributeText(new QName(W_NAMESPACE, "id")); + + + noteIndex++; + if (noteIndex != Integer.parseInt(indexParm)) continue; + + try { + Class clazz = EndNoteing.class; + Method method = clazz.getMethod(function, XmlCursor.class, + CTEndnotes.class ); + + + // 传入CTEndnotes对象 + String value = (String) method.invoke(null, endnoteRefCursor, endnotes); + + if (value != null) { + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + } catch (Exception e) { + e.printStackTrace(); + } + + break; + } + } + + if (firstName.contains("表格")) { + XmlCursor tblCursor = docXml.newCursor(); + String tblCursorPath = namespace + "//w:tbl"; + tblCursor.selectPath(tblCursorPath); + + int tblIndex = 0; + while (tblCursor.toNextSelection()) { + tblIndex++; + if (tblIndex != Integer.parseInt(indexParm)) continue; + + try { + // 如果方法需要尾注对象 + CTEndnotes endnotes = null; + if (endnotesPart != null) { + endnotes = endnotesPart.getContents(); + } + + Class clazz = TableIng.class; + Method method = clazz.getMethod(function, XmlCursor.class, CTEndnotes.class); + String value = (String) method.invoke(null, tblCursor, endnotes); + + if (value != null) { + judgementWordsVOS = setJudgementWord( + judgementWordsVOS, + docxFunction + "@" + value, + firstName + examName + value + ); + } + } catch (NoSuchMethodException e) { + System.err.println("方法不存在: " + function); + } catch (Exception e) { + e.printStackTrace(); + } + + break; // 找到指定表格就退出 + } + } + + + + + + + } @@ -272,4 +735,47 @@ public class DocxMaster { } return sb.toString(); } + /** + * 将 expectedText 中的 ... 处理为正则表达式的模糊匹配,并判断 actualText 是否匹配 + */ + private static boolean isFuzzyMatch(String expectedText, String actualText) { + if (expectedText == null || actualText == null) return false; + + // 将中文省略号或英文省略号都替换成正则表达式的模糊匹配 + String regex = expectedText + .replace("...", ".*") + .replace("…", ".*") + .replaceAll("\\s+", "") // 去除空格 + .replaceAll("[,。!?]", ".*"); // 常见标点也处理成模糊 + + // 去除 actualText 的空格、标点影响匹配 + String cleanActual = actualText.replaceAll("\\s+", ""); + + return cleanActual.matches(".*" + regex + ".*"); + } + private static boolean isTextBoxDrawing(Drawing drawing) { + if (drawing.getAnchorOrInline() != null) { + for (Object obj : drawing.getAnchorOrInline()) { + if (obj instanceof org.docx4j.dml.wordprocessingDrawing.Anchor) { + org.docx4j.dml.wordprocessingDrawing.Anchor anchor = (org.docx4j.dml.wordprocessingDrawing.Anchor) obj; + if (anchor.getGraphic() != null && anchor.getGraphic().getGraphicData() != null) { + List anyList = anchor.getGraphic().getGraphicData().getAny(); + if (anyList != null && !anyList.isEmpty()) { + Object graphicData = anyList.get(0); + // 你可以打印这个对象看看内容或类型,下面示例是用toString判断 + String gdStr = graphicData.toString().toLowerCase(); + + // 常见文本框里面会有 "txBody" 标记,docx4j的文本框一般是wps的txBody节点 + if (gdStr.contains("txbody") || gdStr.contains("textbox")) { + return true; + } + } + } + } + } + } + return false; + } + + } 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 0c931d71..d79344a0 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 @@ -1,22 +1,43 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.drawing; -import org.docx4j.dml.CTLineProperties; -import org.docx4j.dml.CTOuterShadowEffect; -import org.docx4j.dml.CTShapeProperties; -import org.docx4j.dml.STShapeType; +import jakarta.xml.bind.*; +import lombok.val; +import org.docx4j.XmlUtils; +import org.docx4j.dml.*; import org.docx4j.dml.chartDrawing.CTPicture; +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; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.DocxSetInfo; import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert; 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.NamespaceContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.List; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.*; public class Drawing { // 布局 - public static List getLayout(List judgementWordsVOS, Anchor anchor, int betoLong, String firstId){ + public static List getLayout(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { String name = "【第" + betoLong + "个图形】【图片】"; String parentId = Convert.getStringRandom(); String parName = "【布局】【锁定横纵比】"; @@ -24,28 +45,56 @@ public class Drawing { // 环绕方式 String layoutHuanRaoValue = anchor.getWrapSquare().getWrapText().value(); layoutHuanRaoValue = getLayoutHuanRaoName(layoutHuanRaoValue); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + layoutHuanRaoName + layoutHuanRaoValue, name + layoutHuanRaoName + layoutHuanRaoValue, name); // 锁定横纵比 boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + parName + lock, name + parName + lock, name); // 高度 String heightName = "【布局】【高度】"; - String height = anchor.getExtent().getCy() / 360000 + "cm"; - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + heightName + height, name + heightName + height, name); + String height = anchor.getExtent().getCy() / 360000 + "cm"; // 宽度 String weightName = "【布局】【宽度】"; - String weight = anchor.getExtent().getCx() / 360000 + "cm"; - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + weightName + weight, name + weightName + weight, name); + String weight = anchor.getExtent().getCx() / 360000 + "cm"; return judgementWordsVOS; } + //布局 高度 + public static String getLayoutHeight(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + double heightCm = anchor.getExtent().getCy() / 360000.0; + String height = String.format("%.2fcm", heightCm); + return height; + } + + //布局 宽度 + public static String getLayoutWeight(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + double widthCm = anchor.getExtent().getCx() / 360000.0; + String width = String.format("%.2fcm", widthCm); + return width; + } + + //布局 锁定横纵比 + public static String getLayoutLock(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect(); + if (lock) { + return "是"; + } else { + return "否"; + } + } + + //布局 环绕方式 + public static String getLayoutHuanRao(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + String layoutHuanRaoValue = anchor.getWrapSquare().getWrapText().value(); + layoutHuanRaoValue = getLayoutHuanRaoName(layoutHuanRaoValue); + return layoutHuanRaoValue; + } + + // 形状和样式 - public static List getStyles(List judgementWordsVOS, Anchor anchor, int betoLong, String firstId){ + public static List getStyles(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { String name = "【第" + betoLong + "个图形】【图片】"; String parName = "【形状和样式】【自选图形->形状】"; CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); @@ -57,14 +106,40 @@ public class Drawing { if (shapeProps.getPrstGeom() != null) { String prst = shapeProps.getPrstGeom().getPrst().value(); prst = getShapeName(prst); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + parName + prst, name + parName + prst, name); } } return judgementWordsVOS; } + public static String getStylesAndType(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + + // 判断是否是 Picture 类型 + if (value instanceof Pic) { + Pic pic = (Pic) value; + + // 获取形状属性 spPr + CTShapeProperties spPr = pic.getSpPr(); + if (spPr != null) { + // 获取预设几何图形 prstGeom + CTPresetGeometry2D prstGeom = spPr.getPrstGeom(); + if (prstGeom != null) { + String prst = prstGeom.getPrst().value(); + prst = getShapeName(prst); + return prst; + } + } + } + + return null; + } + // 形状轮廓 - public static List getShapeProfile(List judgementWordsVOS, Anchor anchor, int betoLong, String firstId){ + public static List getShapeProfile(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { String name = "【第" + betoLong + "个图形】【图片】"; String firstName = "【形状轮廓】"; String huiZhiName = firstName + "【绘制】"; @@ -74,44 +149,311 @@ public class Drawing { String xuXianName = firstName + "【虚线(短划线类型)】"; String touMingName = firstName + "【透明度】"; String parentId = Convert.getStringRandom(); + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); // 绘制 - if (picture.getSpPr() == null || picture.getSpPr().getLn() == null) { - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + huiZhiName + "是", name + huiZhiName + "是", name); + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { // 宽度 CTLineProperties outline = picture.getSpPr().getLn(); // 轮廓宽度(以EMU为单位,1厘米=360000 EMU) long widthEMU = outline.getW(); - String widthValue =widthEMU / 360000.0 +" 厘米"; - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + kuanDuName + widthValue, name + kuanDuName + widthValue, name); + String widthValue = widthEMU / 360000.0 + " 厘米"; // 颜色 if (outline.getSolidFill() != null) { if (outline.getSolidFill().getSrgbClr() != null) { // RGB颜色 String colorValue = outline.getSolidFill().getSrgbClr().getVal(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + yanSeName + colorValue, name + yanSeName + colorValue, name); } else if (outline.getSolidFill().getSchemeClr() != null) { // 主题颜色 String colorValue = outline.getSolidFill().getSchemeClr().getVal().value(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + yanSeName + colorValue, name + yanSeName + colorValue, name); } } // 轮廓样式 if (outline.getPrstDash() != null) { String val = outline.getPrstDash().getVal().value(); val = getDashStyleName(val); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + xianXingName + val, name + xianXingName + val, name); } } else { - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + huiZhiName + "否", name + huiZhiName + "否", name); } return judgementWordsVOS; } + //绘制 + public static String getShapeProfileHuiZhi(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + org.docx4j.dml.picture.Pic pic = null; + + if (anyObj instanceof JAXBElement) { + Object value = ((JAXBElement) anyObj).getValue(); + if (value instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) value; + } + } else if (anyObj instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) anyObj; + } + + if (pic == null) { + return "否"; + } + + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + return "是"; + } else { + return "否"; + } + } + + + // 形状轮廓(RGB颜色) + public static String getShapeProfileColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + org.docx4j.dml.picture.Pic pic = null; + + if (anyObj instanceof JAXBElement) { + Object value = ((JAXBElement) anyObj).getValue(); + if (value instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) value; + } + } else if (anyObj instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) anyObj; + } + + if (pic == null) { + // 处理未找到图片的情况 + return null; + } + + // 继续处理 pic,获取轮廓颜色 + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties outline = pic.getSpPr().getLn(); + if (outline.getSolidFill() != null) { + if (outline.getSolidFill().getSrgbClr() != null) { + return outline.getSolidFill().getSrgbClr().getVal(); + } else if (outline.getSolidFill().getSchemeClr() != null) { + return outline.getSolidFill().getSchemeClr().getVal().value(); + } + } + } + + return null; + + } + + + // 形状轮廓 宽度 + public static String getShapeProfileKuanDu(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + org.docx4j.dml.picture.Pic pic = null; + + if (anyObj instanceof JAXBElement) { + Object value = ((JAXBElement) anyObj).getValue(); + if (value instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) value; + } + } else if (anyObj instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) anyObj; + } + + if (pic == null) { + // 处理找不到图片的情况 + return null; + } + + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties outline = pic.getSpPr().getLn(); + long widthEMU = outline.getW(); + double widthPoints = widthEMU / 12700.0; + String widthValue = String.format("%.2f 磅", widthPoints); + return widthValue; + } + + + return null; + } + + + // 形状轮廓 线型(复合类型) + public static String getShapeProfileXianXing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + org.docx4j.dml.picture.Pic pic = null; + + if (anyObj instanceof JAXBElement) { + Object value = ((JAXBElement) anyObj).getValue(); + if (value instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) value; + } + } else if (anyObj instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) anyObj; + } + + if (pic == null) { + // 找不到图片对象,返回 null + return null; + } + + 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); // 你自己的方法,转成中文描述 + } + } + + return null; + } + + // 形状轮廓 虚线(短划线类型) + public static String getShapeProfileDashVal(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + org.docx4j.dml.picture.Pic pic = null; + + if (anyObj instanceof JAXBElement) { + Object value = ((JAXBElement) anyObj).getValue(); + if (value instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) value; + } + } else if (anyObj instanceof org.docx4j.dml.picture.Pic) { + pic = (org.docx4j.dml.picture.Pic) anyObj; + } + + if (pic == null) { + return null; + } + + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties outline = pic.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) { + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + + // 输出 XML(调试用) + try { + 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()); + } catch (Exception e) { + e.printStackTrace(); + } + + if (!(value instanceof Pic)) { + System.out.println("value不是Pic类型,实际类型:" + value.getClass().getName()); + return null; + } + + Pic pic = (Pic) value; + + if (pic.getSpPr() == null || pic.getSpPr().getLn() == null) { + System.out.println("ln为空,无法获取轮廓透明度"); + return null; + } + + CTLineProperties ln = pic.getSpPr().getLn(); + CTSolidColorFillProperties solidFill = ln.getSolidFill(); + + if (solidFill == null) { + System.out.println("solidFill为空"); + return null; + } + + // 优先处理 srgbClr + CTSRgbColor srgbColor = solidFill.getSrgbClr(); + if (srgbColor != null) { + List egColorTransformList = srgbColor.getEGColorTransform(); + if (egColorTransformList != null && !egColorTransformList.isEmpty()) { + Object first = egColorTransformList.get(0); + Object alphaObj = null; + if (first instanceof JAXBElement) { + alphaObj = ((JAXBElement) first).getValue(); + } else { + alphaObj = first; + } + + if (alphaObj != null) { + try { + Method getValMethod = alphaObj.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(alphaObj); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + // 透明度计算,val一般0~1000或者0~100000,根据实际调整 + double opaquePercent = val / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + String transparency = String.format("%.1f%%", transparencyPercent); + return transparency; + } else { + System.out.println("透明度值不是数字: " + valObj); + } + } catch (Exception e) { + System.out.println("反射调用 getVal() 失败:" + e.getMessage()); + } + } else { + System.out.println("egColorTransform第一个元素的值为空"); + } + } else { + System.out.println("egColorTransform为空或无元素"); + } + } + // 如果 srgbClr 为空,尝试 schemeClr + else { + CTSchemeColor schemeColor = solidFill.getSchemeClr(); + if (schemeColor != null) { + List transforms = schemeColor.getEGColorTransform(); + if (transforms != null && !transforms.isEmpty()) { + for (Object transform : transforms) { + Object val = transform; + if (transform instanceof JAXBElement) { + val = ((JAXBElement) transform).getValue(); + } + if (val != null) { + try { + Method getValMethod = val.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(val); + if (valObj instanceof Number) { + int alphaVal = ((Number) valObj).intValue(); // 0~100000透明度范围 + double transparencyPercent = (100000 - alphaVal) / 1000.0; + return String.format("%.1f%%", transparencyPercent); + } + } catch (Exception e) { + // 反射调用失败,继续下一个 + } + } + } + System.out.println("schemeClr中没有透明度val"); + } else { + System.out.println("schemeClr中颜色变换为空或无元素"); + } + } else { + System.out.println("solidFill中既没有srgbClr也没有schemeClr"); + } + } + + return null; + } + + // 形状效果(阴影) - public static List getShapeYinYing(List judgementWordsVOS, Anchor anchor, int betoLong, String firstId){ + public static List getShapeYinYing(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws JAXBException { String name = "【第" + betoLong + "个图形】【图片】"; String firstName = "【形状效果(阴影)】"; String huiZhiName = firstName + "【绘制】"; @@ -120,68 +462,939 @@ public class Drawing { String daXiaoName = firstName + "【大小】"; String moHuName = firstName + "【模糊】"; String parentId = Convert.getStringRandom(); - CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); - if (picture.getSpPr() == null || picture.getSpPr().getEffectLst() == null) { - // 没有设置效果 - CTOuterShadowEffect shadow = picture.getSpPr().getEffectLst().getOuterShdw(); - if (shadow == null) { - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + huiZhiName + "否", name + huiZhiName + "否", name); - } - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + huiZhiName + "是", name + huiZhiName + "是", name); - if (shadow.getPrstClr() != null) { - String colorVal = shadow.getPrstClr().getVal().value(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + yanSeName + colorVal, name + yanSeName + colorVal, name); - // 尝试通过颜色变换获取透明度 - try { - Object egColorTransform = shadow.getPrstClr().getEGColorTransform(); - if (egColorTransform != null) { - // 使用反射检查是否存在alpha属性 - Method getAlphaMethod = egColorTransform.getClass().getMethod("getAlpha"); - if (getAlphaMethod != null) { - Object alpha = getAlphaMethod.invoke(egColorTransform); - if (alpha != null) { - String value = alpha.toString(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + touMingDuName + value, name + touMingDuName + value, name); - } - } - } - } catch (Exception e) { - System.out.println("无法获取透明度信息"); - } - } else if (shadow.getSrgbClr() != null) { - String colorVal = shadow.getSrgbClr().getVal(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + yanSeName + colorVal, name + yanSeName + colorVal, name); - // 尝试通过颜色变换获取透明度 - try { - Object egColorTransform = shadow.getPrstClr().getEGColorTransform(); - if (egColorTransform != null) { - // 使用反射检查是否存在alpha属性 - Method getAlphaMethod = egColorTransform.getClass().getMethod("getAlpha"); - if (getAlphaMethod != null) { - Object alpha = getAlphaMethod.invoke(egColorTransform); - if (alpha != null) { - String value = alpha.toString(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + touMingDuName + value, name + touMingDuName + value, name); - } - } - } - } catch (Exception e) { - System.out.println("无法获取透明度信息"); - } - } - System.out.printf("模糊半径: %.2f cm\n", shadow.getBlurRad()/360000.0); - System.out.printf("阴影距离: %.2f cm\n", shadow.getDist()/360000.0); - System.out.printf("阴影方向: %.1f°\n", shadow.getDir()/60000.0); - System.out.printf("水平缩放: %.1f%%\n", shadow.getSx()/1000.0); - System.out.printf("垂直缩放: %.1f%%\n", shadow.getSy()/1000.0); - System.out.printf("水平倾斜: %.1f°\n", shadow.getKx()/60000.0); - System.out.printf("垂直倾斜: %.1f°\n", shadow.getKy()/60000.0); + // 先取 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.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 { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object graphicData = anyObj; + if (anyObj instanceof JAXBElement) { + graphicData = ((JAXBElement) anyObj).getValue(); + } + if (!(graphicData instanceof Pic)) { + return "无"; + } + Pic pic = (Pic) graphicData; + + if (pic.getSpPr() == null || pic.getSpPr().getEffectLst() == null) { + return "无"; + } + + CTEffectList effectLst = pic.getSpPr().getEffectLst(); + CTOuterShadowEffect shadow = effectLst.getOuterShdw(); + if (shadow == null) { + return "无"; + } + + CTPresetColor prstClr = shadow.getPrstClr(); + if (prstClr == null) { + return "无"; + } + + // 反射尝试获取 alpha 对象并调用 getVal() + Object alphaObj = tryGetAlpha(prstClr); + if (alphaObj != null) { + Integer val = tryGetVal(alphaObj); + if (val != null) { + return formatAlphaValue(val); + } + } + + // 尝试遍历 getEGColorTransform() + List transforms = prstClr.getEGColorTransform(); + if (transforms != null) { + for (Object t : transforms) { + Object valObj = t; + if (t instanceof JAXBElement) { + valObj = ((JAXBElement) t).getValue(); + } + Integer val = tryGetVal(valObj); + if (val != null) { + return formatAlphaValue(val); + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + return "无"; + } + + 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 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 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); + } + + // 获取“预设” + public static String getShadowPresetType(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + + return "无预设"; + } + + + + + //形状填充 + + //填充方式 + public static String getFillType(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(); + } + 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()); + + + + CTShapeProperties spPr = getShapeProperties(anchor); + if (spPr == null) return null; + + if (spPr.getSolidFill() != null) return "纯色填充"; + if (spPr.getBlipFill() != null) return "图片填充"; + if (spPr.getPattFill() != null) return "图案填充"; + return null; + } +// marshalShapeProperties(spPr); +public static String getSolidFillColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTShapeProperties spPr = getShapeProperties(anchor); + if (spPr == null) { + return null; + } + + CTSolidColorFillProperties solidFill = spPr.getSolidFill(); + if (solidFill == null) { + return null; + } + + if (solidFill.getSrgbClr() != null) { + System.out.println("srgbClr = " + solidFill.getSrgbClr().getVal()); + return ColorUtils.getChineseColorName(String.valueOf(solidFill.getSrgbClr().getVal())); + } + + if (solidFill.getSchemeClr() != null) { + System.out.println("schemeClr = " + solidFill.getSchemeClr().getVal().value()); + return ColorUtils.getChineseColorName(solidFill.getSchemeClr().getVal().value()); + + } + + System.out.println("全部颜色属性均为空!"); + return null; +} + + // 纯色填充→透明度 + public static String getPictureFillTransparency(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTShapeProperties spPr = getShapeProperties(anchor); + 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 + || anchor.getGraphic().getGraphicData().getPic() == null) { + return "否"; + } + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr == null) return "否"; + + CTEffectList effectList = spPr.getEffectLst(); + if (effectList == null) return "否"; + + CTReflectionEffect reflection = effectList.getReflection(); + if (reflection == null) return "否"; + + 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() : "无"); + } + + + // 3. 透明度(单位%) + public static String getShadowTransparency( + List judgementWordsVOS, + Anchor anchor, + int betoLong, + WordprocessingMLPackage wordMLPackage) { + + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getPic() == null) { + return "无倒影"; + } + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr == null) return "无倒影"; + + CTEffectList effectList = spPr.getEffectLst(); + if (effectList == null) return "无倒影"; + + CTReflectionEffect reflection = effectList.getReflection(); + if (reflection == null) return "无倒影"; + + // 读取起始透明度 stA 和结束透明度 endA,类型是 Integer + Integer stA = reflection.getStA(); // 起始透明度 + Integer endA = reflection.getEndA(); // 结束透明度 + + // 计算平均透明度(百分比) + if (stA == null && endA == null) { + return "0%"; + } + + int stAVal = stA != null ? stA : 0; + + // 透明度范围是0~100000,值越大越不透明 + // 我们转成百分比透明度(100%完全透明) + double stTransparencyPercent = 100.0 - (stAVal / 1000.0); + + // 返回平均透明度,保留一位小数 + double avgTransparency = stTransparencyPercent; + + return String.format("%.1f%%", avgTransparency); + } + + // 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(); + } + 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()); + 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); + } + + + + // ----- 辅助方法 ----- + private static BigInteger getReflectionSize(CTReflectionEffect reflection) { + if (reflection == null) return null; + try { + Method m = reflection.getClass().getMethod("getSz"); + Object val = m.invoke(reflection); + if (val instanceof BigInteger) { + return (BigInteger) val; + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + // 获取倒影对象 + private static CTReflectionEffect getReflectionEffect(Anchor anchor) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getPic() == null) { + return null; + } + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr == null) return null; + + CTEffectList effectLst = spPr.getEffectLst(); + if (effectLst == null) return null; + + // 通过 getReflection() 获取倒影对象 + CTReflectionEffect reflection = effectLst.getReflection(); + return reflection; + } + + + + + // 从shadow中获取透明度alpha,返回整数(0~100000) + private static Integer getAlphaVal(CTReflectionEffect reflection) { + if (reflection == null) return null; + + try { + // 先尝试用反射取所有get开头的方法,找透明度相关的 + for (Method method : reflection.getClass().getMethods()) { + String name = method.getName(); + if (name.startsWith("get") && + (name.toLowerCase().contains("alpha") || name.toLowerCase().contains("amt"))) { + Object val = method.invoke(reflection); + if (val != null) { + // 如果是BigInteger或Integer,返回 + if (val instanceof BigInteger) { + return ((BigInteger) val).intValue(); + } else if (val instanceof Integer) { + return (Integer) val; + } else if (val.getClass().getName().contains("CTPositiveFixedAngle") || + val.getClass().getName().contains("CTAlpha")) { + // 进一步递归找val属性 + Method getValMethod = val.getClass().getMethod("getVal"); + Object v = getValMethod.invoke(val); + if (v instanceof BigInteger) return ((BigInteger) v).intValue(); + if (v instanceof Integer) return (Integer) v; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + // 找不到就返回null + 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 "未知色"; + } + + + + + + private static String mapPresetShadowValueToLabel(String prstVal) { + if (prstVal == null) return "未知"; + + switch (prstVal) { + case "shdw1": return "外部偏移下"; + case "shdw2": return "内部上"; + case "shdw3": return "内部下"; + case "shdw4": return "透视左上"; + case "shdw5": return "透视左下"; + default: return prstVal; + } + } + + // 辅助方法:从Anchor中获取阴影效果对象 + private static CTOuterShadowEffect getOuterShadow(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().getOuterShdw() != null) { + return spPr.getEffectLst().getOuterShdw(); + } + } + return null; + } + + + + + private static String getShapeName(String shapeType) { switch (shapeType) { case "rect": return "矩形"; @@ -222,4 +1435,18 @@ public class Drawing { default: return "无"; } } + + private static String getLineTypeName(String val) { + switch (val) { + case "sng": return "单线"; + case "dbl": return "双线"; + case "tri": return "三重线"; + default: return val; + } + } + + + + + } 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 new file mode 100644 index 00000000..39c4e68b --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/DrawingInline.java @@ -0,0 +1,1187 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.drawing; + +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.JAXBException; +import jakarta.xml.bind.Marshaller; +import org.docx4j.dml.*; +import org.docx4j.dml.chartDrawing.CTPicture; +import org.docx4j.dml.picture.Pic; +import org.docx4j.dml.wordprocessingDrawing.CTWrapSquare; +import org.docx4j.dml.wordprocessingDrawing.Inline; +import org.docx4j.dml.wordprocessingDrawing.Inline; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import pc.exam.pp.module.judgement.utils.wps_word.docx4j.DocxSetInfo; +import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert; +import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.JudgementWordsVO; +import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; + +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; + +public class DrawingInline { + + // 布局 +// public static List getLayout(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { +// String name = "【第" + betoLong + "个图形】【图片】"; +// String parentId = Convert.getStringRandom(); +// String parName = "【布局】【锁定横纵比】"; +// String layoutHuanRaoName = "【布局】【环绕方式】"; +// // 环绕方式 +// String layoutHuanRaoValue = anchor.getWrapSquare().getWrapText().value(); +// layoutHuanRaoValue = getLayoutHuanRaoName(layoutHuanRaoValue); +// judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + layoutHuanRaoName + layoutHuanRaoValue, name + layoutHuanRaoName + layoutHuanRaoValue, name); +// +// // 锁定横纵比 +// boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect(); +// +// judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + parName + lock, name + parName + lock, name); +// +// // 高度 +// String heightName = "【布局】【高度】"; +// String height = anchor.getExtent().getCy() / 360000 + "cm"; +// judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + heightName + height, name + heightName + height, name); +// +// // 宽度 +// String weightName = "【布局】【宽度】"; +// String weight = anchor.getExtent().getCx() / 360000 + "cm"; +// judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + weightName + weight, name + weightName + weight, name); +// +// return judgementWordsVOS; +// } + + // 布局 高度 + public static String getLayoutHeight(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + double heightCm = anchor.getExtent().getCy() / 360000.0; + String height = String.format("%.2fcm", heightCm); + return height; + } + + // 布局 宽度 + public static String getLayoutWeight(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + double widthCm = anchor.getExtent().getCx() / 360000.0; + String width = String.format("%.2fcm", widthCm); + return width; + } + + + //布局 锁定横纵比 + public static String getLayoutLock(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect(); + if (lock) { + return "是"; + } else { + return "否"; + } + } + + //布局 环绕方式 + public static String getLayoutHuanRao(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + // Inline + return null; + } + + + + // 形状和样式 + public static List getStyles(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + String name = "【第" + betoLong + "个图形】【图片】"; + String parName = "【形状和样式】【自选图形->形状】"; + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + // 获取形状属性 + CTShapeProperties shapeProps = picture.getSpPr(); + String parentId = Convert.getStringRandom(); + if (shapeProps != null) { + // 获取形状几何属性 + if (shapeProps.getPrstGeom() != null) { + String prst = shapeProps.getPrstGeom().getPrst().value(); + prst = getShapeName(prst); + } + } + return judgementWordsVOS; + } + + public static String getStylesAndType(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + + // 判断是否是 Picture 类型 + if (value instanceof Pic) { + Pic pic = (Pic) value; + + // 获取形状属性 spPr + CTShapeProperties spPr = pic.getSpPr(); + if (spPr != null) { + // 获取预设几何图形 prstGeom + CTPresetGeometry2D prstGeom = spPr.getPrstGeom(); + if (prstGeom != null) { + String prst = prstGeom.getPrst().value(); + prst = getShapeName(prst); + return prst; + } + } + } + + return null; + } + + // 形状轮廓 + public static List getShapeProfile(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + String name = "【第" + betoLong + "个图形】【图片】"; + String firstName = "【形状轮廓】"; + String huiZhiName = firstName + "【绘制】"; + String yanSeName = firstName + "【颜色】"; + String kuanDuName = firstName + "【宽度(粗细)】"; + String xianXingName = firstName + "【线型(复合类型)】"; + String xuXianName = firstName + "【虚线(短划线类型)】"; + String touMingName = firstName + "【透明度】"; + String parentId = Convert.getStringRandom(); + + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + + // 绘制 + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { + + // 宽度 + CTLineProperties outline = picture.getSpPr().getLn(); + // 轮廓宽度(以EMU为单位,1厘米=360000 EMU) + long widthEMU = outline.getW(); + String widthValue = widthEMU / 360000.0 + " 厘米"; + // 颜色 + if (outline.getSolidFill() != null) { + if (outline.getSolidFill().getSrgbClr() != null) { + // RGB颜色 + String colorValue = outline.getSolidFill().getSrgbClr().getVal(); + } else if (outline.getSolidFill().getSchemeClr() != null) { + // 主题颜色 + String colorValue = outline.getSolidFill().getSchemeClr().getVal().value(); + } + } + // 轮廓样式 + if (outline.getPrstDash() != null) { + String val = outline.getPrstDash().getVal().value(); + val = getDashStyleName(val); + } + } else { + } + return judgementWordsVOS; + } + + //绘制 + public static String getShapeProfileHuiZhi(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + // 绘制 + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { + return "是"; + } else { + return "否"; + } + } + + // 形状轮廓(RGB颜色) + public static String getShapeProfileColor(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + String colorValue = null; + // 绘制 + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { + // 宽度 + CTLineProperties outline = picture.getSpPr().getLn(); + // 颜色 + if (outline.getSolidFill() != null) { + if (outline.getSolidFill().getSrgbClr() != null) { + // RGB颜色 + colorValue = outline.getSolidFill().getSrgbClr().getVal(); + } else if (outline.getSolidFill().getSchemeClr() != null) { + // 主题颜色 + colorValue = outline.getSolidFill().getSchemeClr().getVal().value(); + } + } + } + return colorValue; + } + + + // 形状轮廓 绘制 + public static String getShapeProfileKuanDu(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + // 绘制 + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { + CTLineProperties outline = picture.getSpPr().getLn(); + // 轮廓宽度(以EMU为单位,1厘米=360000 EMU) + long widthEMU = outline.getW(); + String widthValue = widthEMU / 360000.0 + " 厘米"; + return widthValue; + } + return null; + } + + // 形状轮廓 线型(复合类型) + public static String getShapeProfileXianXing(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { + CTLineProperties outline = picture.getSpPr().getLn(); + if (outline.getCmpd() != null) { + String val = outline.getCmpd().value(); // 例如:sng、dbl、tri + return getLineTypeName(val); // 可选:自己转换成中文描述 + } + } + + return null; + } + + // 形状轮廓 虚线(短划线类型) + public static String getShapeProfileDashVal(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTPicture picture = (CTPicture) anchor.getGraphic().getGraphicData().getAny().get(0); + + if (picture.getSpPr() != null && picture.getSpPr().getLn() != null) { + CTLineProperties outline = picture.getSpPr().getLn(); + if (outline.getPrstDash() != null) { + return getDashStyleName(outline.getPrstDash().getVal().value()); // 返回转义后的中文名称 + } + } + + return null; + } + + + //形状轮廓 透明度 + public static List getShapeProfileTouMing(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + String name = "【第" + betoLong + "个图形】【图片】"; + String firstName = "【形状轮廓】"; + String touMingName = firstName + "【透明度】"; + String parentId = Convert.getStringRandom(); + + Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); + Object value = anyObj; + if (anyObj instanceof JAXBElement) { + value = ((JAXBElement) anyObj).getValue(); + } + + // 输出 XML(调试用) + try { + 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()); + } catch (Exception e) { + e.printStackTrace(); + } + + + if (value instanceof Pic) { + Pic pic = (Pic) value; + + if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { + CTLineProperties ln = pic.getSpPr().getLn(); + CTSolidColorFillProperties solidFill = ln.getSolidFill(); + + if (solidFill.getSrgbClr() != null) { + CTSRgbColor srgbColor = solidFill.getSrgbClr(); + + List egColorTransformList = srgbColor.getEGColorTransform(); + if (egColorTransformList != null && !egColorTransformList.isEmpty()) { + Object first = egColorTransformList.get(0); + if (first instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) first; + Object alphaObj = jaxbElement.getValue(); + // alphaObj 应该是一个有 getVal() 方法的对象,表示透明度 + + 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; + String transparency = String.format("%.1f%%", transparencyPercent); + + } else { + System.out.println("透明度值不是数字: " + valObj); + } + } catch (Exception e) { + System.out.println("反射调用 getVal() 失败:" + e.getMessage()); + } + } else { + System.out.println("egColorTransform 第一个元素不是 JAXBElement,实际类型:" + first.getClass().getName()); + } + } else { + System.out.println("egColorTransform 为空或无元素"); + } + } else { + System.out.println("solidFill.getSrgbClr()为空"); + } + } else { + System.out.println("ln为空,无法获取轮廓透明度"); + } + } else { + System.out.println("value不是Pic类型,实际类型:" + value.getClass().getName()); + } + + return judgementWordsVOS; + } + + // 形状效果(阴影) + public static List getShapeYinYing(List judgementWordsVOS, Inline 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, Inline 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, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + 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, Inline 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, Inline 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, Inline 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, Inline 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, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTOuterShadowEffect shadow = getOuterShadow(anchor); + if (shadow == null) return "无"; + + // 先尝试 prstClr + CTPresetColor prstClr = shadow.getPrstClr(); + if (prstClr != null) { + List transforms = prstClr.getEGColorTransform(); + if (transforms != null && !transforms.isEmpty()) { + for (Object transformObj : transforms) { + Object value = transformObj; + if (transformObj instanceof JAXBElement) { + value = ((JAXBElement) transformObj).getValue(); + } + if (value.getClass().getSimpleName().equals("CTAlpha")) { + try { + Method getValMethod = value.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(value); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + double transparencyPercent = 100.0 - val / 1000.0; + if (transparencyPercent < 0) transparencyPercent = 0; + if (transparencyPercent > 100) transparencyPercent = 100; + return String.format("%.1f%%", transparencyPercent); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + // 再尝试 srgbClr + CTSRgbColor srgbClr = shadow.getSrgbClr(); + if (srgbClr != null) { + List transforms = srgbClr.getEGColorTransform(); + if (transforms != null && !transforms.isEmpty()) { + for (Object transformObj : transforms) { + Object value = transformObj; + if (transformObj instanceof JAXBElement) { + value = ((JAXBElement) transformObj).getValue(); + } + if (value.getClass().getSimpleName().equals("CTAlpha")) { + try { + Method getValMethod = value.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(value); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + double transparencyPercent = 100.0 - val / 1000.0; + if (transparencyPercent < 0) transparencyPercent = 0; + if (transparencyPercent > 100) transparencyPercent = 100; + return String.format("%.1f%%", transparencyPercent); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + return "无"; + } + + // 获取“预设” + public static String getShadowPresetType(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + + return "无预设"; + } + + + + + //形状填充 + + //填充方式 + public static String getFillType(List judgementWordsVOS, Inline 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(); + } + 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()); + + + + CTShapeProperties spPr = getShapeProperties(anchor); + if (spPr == null) return "无填充"; + + if (spPr.getSolidFill() != null) return "纯色填充"; + if (spPr.getBlipFill() != null) return "图片填充"; + if (spPr.getPattFill() != null) return "图案填充"; + return "未知填充"; + } +// marshalShapeProperties(spPr); +public static String getSolidFillColor(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTShapeProperties spPr = getShapeProperties(anchor); + if (spPr == null) { + return "无形状属性"; + } + + CTSolidColorFillProperties solidFill = spPr.getSolidFill(); + if (solidFill == null) { + return "无纯色填充"; + } + + if (solidFill.getSrgbClr() != null) { + System.out.println("srgbClr = " + solidFill.getSrgbClr().getVal()); + return ColorUtils.getChineseColorName(String.valueOf(solidFill.getSrgbClr().getVal())); + } + + if (solidFill.getSchemeClr() != null) { + System.out.println("schemeClr = " + solidFill.getSchemeClr().getVal().value()); + return ColorUtils.getChineseColorName(solidFill.getSchemeClr().getVal().value()); + + } + + + + System.out.println("全部颜色属性均为空!"); + return "未知颜色"; +} + + // 纯色填充→透明度 + public static String getPictureFillTransparency(List judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + CTShapeProperties spPr = getShapeProperties(anchor); + if (spPr == null) return "无纯色填充"; + + CTSolidColorFillProperties solidFill = spPr.getSolidFill(); + if (solidFill == null) return "无纯色填充"; + + // 检查 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, + Inline anchor, + int betoLong, + WordprocessingMLPackage wordMLPackage) throws Exception { + + CTShapeProperties spPr = getShapeProperties(anchor); + if (spPr == null) return "无纹理填充"; + + CTBlipFillProperties blipFill = spPr.getBlipFill(); + if (blipFill == null) return "无纹理填充"; + + CTBlip blip = blipFill.getBlip(); + if (blip == null) return "无纹理填充"; + + 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()); + + return "0%"; // 默认不透明 + } + 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, Inline 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, Inline 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, Inline 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, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getPic() == null) { + return "否"; + } + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr == null) return "否"; + + CTEffectList effectList = spPr.getEffectLst(); + if (effectList == null) return "否"; + + CTReflectionEffect reflection = effectList.getReflection(); + if (reflection == null) return "否"; + + return "是"; + } + + // 2. 预设类型(shadow preset) + public static String getShadowPreset(List judgementWordsVOS, Inline 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() : "无"); + } + + + // 3. 透明度(单位%) + public static String getShadowTransparency( + List judgementWordsVOS, + Inline anchor, + int betoLong, + WordprocessingMLPackage wordMLPackage) { + + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getPic() == null) { + return "无倒影"; + } + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr == null) return "无倒影"; + + CTEffectList effectList = spPr.getEffectLst(); + if (effectList == null) return "无倒影"; + + CTReflectionEffect reflection = effectList.getReflection(); + if (reflection == null) return "无倒影"; + + // 读取起始透明度 stA 和结束透明度 endA,类型是 Integer + Integer stA = reflection.getStA(); // 起始透明度 + Integer endA = reflection.getEndA(); // 结束透明度 + + // 计算平均透明度(百分比) + if (stA == null && endA == null) { + return "0%"; + } + + int stAVal = stA != null ? stA : 0; + + // 透明度范围是0~100000,值越大越不透明 + // 我们转成百分比透明度(100%完全透明) + double stTransparencyPercent = 100.0 - (stAVal / 1000.0); + + // 返回平均透明度,保留一位小数 + double avgTransparency = stTransparencyPercent; + + return String.format("%.1f%%", avgTransparency); + } + + // 4. 大小(比例,单位%) + public static String getShadowSize(List judgementWordsVOS, Inline 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(); + } + 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()); + 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, Inline 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, Inline 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); + } + + + + // ----- 辅助方法 ----- + private static BigInteger getReflectionSize(CTReflectionEffect reflection) { + if (reflection == null) return null; + try { + Method m = reflection.getClass().getMethod("getSz"); + Object val = m.invoke(reflection); + if (val instanceof BigInteger) { + return (BigInteger) val; + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + // 获取倒影对象 + private static CTReflectionEffect getReflectionEffect(Inline anchor) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getPic() == null) { + return null; + } + + CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); + if (spPr == null) return null; + + CTEffectList effectLst = spPr.getEffectLst(); + if (effectLst == null) return null; + + // 通过 getReflection() 获取倒影对象 + CTReflectionEffect reflection = effectLst.getReflection(); + return reflection; + } + + + + + // 从shadow中获取透明度alpha,返回整数(0~100000) + private static Integer getAlphaVal(CTReflectionEffect reflection) { + if (reflection == null) return null; + + try { + // 先尝试用反射取所有get开头的方法,找透明度相关的 + for (Method method : reflection.getClass().getMethods()) { + String name = method.getName(); + if (name.startsWith("get") && + (name.toLowerCase().contains("alpha") || name.toLowerCase().contains("amt"))) { + Object val = method.invoke(reflection); + if (val != null) { + // 如果是BigInteger或Integer,返回 + if (val instanceof BigInteger) { + return ((BigInteger) val).intValue(); + } else if (val instanceof Integer) { + return (Integer) val; + } else if (val.getClass().getName().contains("CTPositiveFixedAngle") || + val.getClass().getName().contains("CTAlpha")) { + // 进一步递归找val属性 + Method getValMethod = val.getClass().getMethod("getVal"); + Object v = getValMethod.invoke(val); + if (v instanceof BigInteger) return ((BigInteger) v).intValue(); + if (v instanceof Integer) return (Integer) v; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + // 找不到就返回null + return null; + } + + + + + + + + + + + + + + + + public static CTShapeProperties getShapeProperties(Inline anchor) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null) { + System.out.println("Inline 或 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 Pic) { + Pic pic = (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 "未知色"; + } + + + + + + private static String mapPresetShadowValueToLabel(String prstVal) { + if (prstVal == null) return "未知"; + + switch (prstVal) { + case "shdw1": return "外部偏移下"; + case "shdw2": return "内部上"; + case "shdw3": return "内部下"; + case "shdw4": return "透视左上"; + case "shdw5": return "透视左下"; + default: return prstVal; + } + } + + // 辅助方法:从Anchor中获取阴影效果对象 + private static CTOuterShadowEffect getOuterShadow(Inline 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().getOuterShdw() != null) { + return spPr.getEffectLst().getOuterShdw(); + } + } + return null; + } + + + + + + private static String getShapeName(String shapeType) { + switch (shapeType) { + case "rect": return "矩形"; + case "roundRect": return "圆角矩形"; + case "ellipse": return "椭圆/圆形"; + case "triangle": return "三角形"; + case "diamond": return "菱形"; + case "hexagon": return "六边形"; + case "cloud": return "云形"; + case "star": return "星形"; + default: return shapeType; + } + } + + 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; + } + } + + private static String getLayoutHuanRaoName(String wrapText) { + if (wrapText == null) return "两侧"; + + switch (wrapText) { + case "bothSides": return "两侧"; + case "left": return "只在左侧"; + case "right": return "只在右侧"; + case "largest": return "最大边"; + default: return "无"; + } + } + + private static String getLineTypeName(String val) { + switch (val) { + case "sng": return "单线"; + case "dbl": return "双线"; + case "tri": return "三重线"; + default: return val; + } + } + + + + + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/endNote/EndNoteing.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/endNote/EndNoteing.java new file mode 100644 index 00000000..a679d374 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/endNote/EndNoteing.java @@ -0,0 +1,122 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.endNote; + +import jakarta.xml.bind.JAXBElement; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlObject; +import org.docx4j.XmlUtils; +import org.docx4j.wml.CTEndnotes; +import org.docx4j.wml.ContentAccessor; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; + +import javax.xml.namespace.QName; +import java.math.BigInteger; +import java.util.List; + +public class EndNoteing { + + private static final String W_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + + /** + * 判断尾注是否存在(返回 "是" 或 "否") + */ + public static String hasEndNote(XmlCursor endnoteRefCursor, org.docx4j.wml.CTEndnotes endnotes) { + if (endnoteRefCursor == null || endnotes == null) { + return "否"; + } + + String id = endnoteRefCursor.getAttributeText(new QName(W_NAMESPACE, "id")); + + + BigInteger targetId; + try { + targetId = new BigInteger(id); + } catch (NumberFormatException e) { + return "否"; + } + + // 遍历尾注集合,查找是否存在对应的尾注id + List endnoteList = endnotes.getEndnote(); + if (endnoteList == null || endnoteList.isEmpty()) { + return "否"; + } + + for (org.docx4j.wml.CTFtnEdn endnote : endnoteList) { + if (endnote.getId() != null && endnote.getId().equals(targetId)) { + return "是"; + } + } + + return "否"; + } + + + + /** + * 获取尾注内容 + * "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' " + */ + public static String getEndNoteContent(XmlCursor endnoteRefCursor, org.docx4j.wml.CTEndnotes endnotes) { + if (endnoteRefCursor == null || endnotes == null) return null; + + String id = endnoteRefCursor.getAttributeText(new QName(W_NAMESPACE, "id")); + if (id == null || id.isEmpty()) return null; + + BigInteger targetId; + try { + targetId = new BigInteger(id); + } catch (NumberFormatException e) { + return null; + } + + for (org.docx4j.wml.CTFtnEdn endnote : endnotes.getEndnote()) { + if (endnote.getId() != null && endnote.getId().equals(targetId)) { + // 提取文本内容 + StringBuilder sb = new StringBuilder(); + + // 遍历所有段落,一般是内容所在 + for (Object o : endnote.getContent()) { + String text = extractTextFromObject(o); + if (text != null) sb.append(text); + } + return sb.toString().trim(); + } + } + + return null; + } + + /** + * 从w:p, w:r, w:t等对象中递归抽取文本 + */ + private static String extractTextFromObject(Object o) { + if (o == null) return null; + + // 递归处理XmlObject,JAXBElement等 + if (o instanceof JAXBElement) { + return extractTextFromObject(((JAXBElement) o).getValue()); + } else if (o instanceof org.docx4j.wml.Text) { + return ((org.docx4j.wml.Text) o).getValue(); + } else if (o instanceof org.docx4j.wml.R) { + StringBuilder sb = new StringBuilder(); + for (Object child : ((org.docx4j.wml.R) o).getContent()) { + String t = extractTextFromObject(child); + if (t != null) sb.append(t); + } + return sb.toString(); + } else if (o instanceof org.docx4j.wml.P) { + StringBuilder sb = new StringBuilder(); + for (Object child : ((org.docx4j.wml.P) o).getContent()) { + String t = extractTextFromObject(child); + if (t != null) sb.append(t); + } + return sb.toString() + "\n"; + } + + // 其他类型可根据需要继续扩展 + + return null; + } + + + +} \ No newline at end of file diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/insert/InsertIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/insert/InsertIng.java new file mode 100644 index 00000000..5af94b01 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/insert/InsertIng.java @@ -0,0 +1,78 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.insert; + +import org.apache.xmlbeans.XmlCursor; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class InsertIng { + + // 域类型映射表(可扩展) + private static final Map FIELD_TYPE_MAP = new HashMap<>(); + static { + FIELD_TYPE_MAP.put("HYPERLINK", "连接和引用 → 打开并跳到指定文件"); + FIELD_TYPE_MAP.put("REF", "交叉引用 → 引用书签"); + FIELD_TYPE_MAP.put("PAGEREF", "交叉引用 → 引用页码"); + FIELD_TYPE_MAP.put("PAGE", "页码"); + FIELD_TYPE_MAP.put("NUMPAGES", "页总数"); + FIELD_TYPE_MAP.put("DATE", "当前日期"); + FIELD_TYPE_MAP.put("TIME", "当前时间"); + FIELD_TYPE_MAP.put("FILENAME", "文档文件名"); + FIELD_TYPE_MAP.put("AUTHOR", "作者"); + FIELD_TYPE_MAP.put("TITLE", "标题"); + FIELD_TYPE_MAP.put("SUBJECT", "主题"); + FIELD_TYPE_MAP.put("CREATEDATE", "创建日期"); + FIELD_TYPE_MAP.put("LASTSAVEDATE", "修改日期"); + FIELD_TYPE_MAP.put("FORMTEXT", "文本表单域"); + FIELD_TYPE_MAP.put("FORMLIST", "列表表单域"); + FIELD_TYPE_MAP.put("FORMCHECKBOX", "复选框表单域"); + FIELD_TYPE_MAP.put("TOC", "目录域"); + FIELD_TYPE_MAP.put("EQ", "公式域"); + FIELD_TYPE_MAP.put("SEQ", "序列域"); + FIELD_TYPE_MAP.put("TC", "目录项域"); + FIELD_TYPE_MAP.put("MERGEFIELD", "邮件合并字段域"); + } + + /** + * 获取域类型(中文描述) + */ + public static String getFieldType(XmlCursor instrTextCursor) { + if (instrTextCursor == null) return ""; + String instrText = instrTextCursor.getTextValue(); + if (instrText == null || instrText.trim().isEmpty()) return ""; + + // 第一个单词就是类型关键字 + String typeKey = instrText.trim().split("\\s+")[0].toUpperCase(); + + + if (typeKey==null) + { + return null; + } + // 映射表查找中文描述 + return FIELD_TYPE_MAP.getOrDefault(typeKey, typeKey); + } + + /** + * 获取域属性(比如 URL 或书签名) + */ + public static String getFieldAttribute(XmlCursor instrTextCursor) { + if (instrTextCursor == null) return ""; + String instrText = instrTextCursor.getTextValue(); + if (instrText == null || instrText.trim().isEmpty()) return ""; + + // 正则匹配引号内的内容 + Matcher matcher = Pattern.compile("\"([^\"]+)\"").matcher(instrText); + if (matcher.find()) { + return matcher.group(1); + } + + // 如果没有引号,就返回第一个单词后的部分 + String[] parts = instrText.trim().split("\\s+", 2); + return parts.length > 1 ? parts[1] : null; + } + + +} 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 90207a55..24117974 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 @@ -8,10 +8,10 @@ import java.math.BigInteger; public class RunText { - // 句子-字体 - public static void getParagraphAlignment(R run, String text) { + // 句子-字号 + public static String getParagraphAlignment(R run, String text) { if (run == null) { - return ; + return null ; } RPr rPr = run.getRPr(); String eastAsiaFont = null, asciiFont = null, hAnsiFont = null; @@ -38,9 +38,29 @@ public class RunText { System.out.println("西文字体(HAnsi): " + (hAnsiFont != null ? hAnsiFont : "未设置")); System.out.println("字号: " + (fontSize != null ? fontSize : "未设置")); System.out.println("--------"); + return fontSize != null ? fontSize : "未设置"; } + // 字体-字形 ( 加粗 + 倾斜) + 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(); + } + } + return eastAsiaFont != null ? eastAsiaFont : "未设置"; + } + // 字体-字形 ( 加粗 + 倾斜) public static void getParagraphFontStyle(R run, String text) { RPr rPr = run.getRPr(); boolean isBold = false; @@ -63,34 +83,157 @@ public class RunText { } System.out.println("--------"); } - // 字体-所有文字 - public static void getParagraphFontColor(R run, StyleDefinitionsPart stylePart, String text) { + // 字体-字形 ( 加粗 + 倾斜) + 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 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 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 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 void getParagraphFontColor(R run, StyleDefinitionsPart stylePart, String text) { +// RPr rPr = run.getRPr(); +// +// String color = "默认"; +// String underlineVal = "无"; +// String underlineColor = "默认"; +// +// if (rPr != null) { +// if (rPr.getColor() != null && rPr.getColor().getVal() != null) { +// color = rPr.getColor().getVal(); // 如 "FF0000" +// } +// +// if (rPr.getU() != null) { +// if (rPr.getU().getVal() != null) { +// underlineVal = rPr.getU().getVal().value(); // single, double, none 等 +// } +// if (rPr.getU().getColor() != null) { +// underlineColor = rPr.getU().getColor(); // 例如 "0000FF" +// } +// } +// } +// +// System.out.println("文本: " + text); +// System.out.println("字体颜色: " + color); +// System.out.println("下划线样式: " + underlineVal); +// System.out.println("下划线颜色: " + underlineColor); +// System.out.println("--------"); +// } + + public static String getParagraphFontColor(R run, String text) { + RPr rPr = run.getRPr(); String color = "默认"; - String underlineVal = "无"; - String underlineColor = "默认"; - 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 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 等 } + + } + } + + 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" } } } - System.out.println("文本: " + text); - System.out.println("字体颜色: " + color); - System.out.println("下划线样式: " + underlineVal); - System.out.println("下划线颜色: " + underlineColor); - System.out.println("--------"); + return underlineColor; + } // 字体-效果-删除线 public static void getParagraphStrike(R run, String text) { @@ -117,41 +260,117 @@ public class RunText { } // 字体-字符间距 - public static void getParagraphFontSpacing(R run, String text) { +// public static void getParagraphFontSpacing(R run, String text) { +// RPr rPr = run.getRPr(); +// +// String scale = "100%"; +// String spacing = "0"; +// String position = "0"; +// String kern = "无"; +// String snapToGrid = "否"; +// +// if (rPr != null) { +// if (rPr.getW() != null && rPr.getW().getVal() != null) { +// scale = rPr.getW().getVal().toString() + "%"; +// } +// if (rPr.getSpacing() != null && rPr.getSpacing().getVal() != null) { +// spacing = rPr.getSpacing().getVal().toString() + " twips"; +// } +// if (rPr.getPosition() != null && rPr.getPosition().getVal() != null) { +// position = rPr.getPosition().getVal().toString() + " pt(1/2)"; +// } +// if (rPr.getKern() != null && rPr.getKern().getVal() != null) { +// kern = rPr.getKern().getVal().toString() + " (1/100 pt)"; +// } +// if (rPr.getSnapToGrid() != null) { +// snapToGrid = rPr.getSnapToGrid().isVal() ? "是" : "否"; +// } +// } +// +// System.out.println("文本: " + text); +// System.out.println("缩放比例: " + scale); +// System.out.println("字符间距: " + spacing); +// System.out.println("字符位置: " + position); +// System.out.println("最小间距 (Kern): " + kern); +// System.out.println("对齐到网格: " + snapToGrid); +// System.out.println("--------"); +// } + + //缩放比例 + public static String getParagraphFontSpacingScale(R run, String text) { RPr rPr = run.getRPr(); String scale = "100%"; - String spacing = "0"; - String position = "0"; - String kern = "无"; - String snapToGrid = "否"; + 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)"; } + } + + 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() ? "是" : "否"; } } - System.out.println("文本: " + text); - System.out.println("缩放比例: " + scale); - System.out.println("字符间距: " + spacing); - System.out.println("字符位置: " + position); - System.out.println("最小间距 (Kern): " + kern); System.out.println("对齐到网格: " + snapToGrid); - System.out.println("--------"); + return snapToGrid; } + // 字体-文本效果 public static void getParagraphTextEffect(R run, String text) { @@ -167,4 +386,36 @@ public class RunText { System.out.println("发光 (glow): " + (runXml.contains("w14:glow") ? "是" : "否")); 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 getParagraphTextOutline(R run, String text) { + // 转换为 XML 文本 + String runXml = XmlUtils.marshaltoString(run, true); + return runXml.contains("w14:textOutline") ? "是" : "否"; + } + //阴影 + public static String getParagraphTextShadow(R run, String text) { + // 转换为 XML 文本 + String runXml = XmlUtils.marshaltoString(run, true); + return runXml.contains("w14:shadow") ? "是" : "否"; + } + //倒影 + public static String getParagraphTextReflection(R run, String text) { + // 转换为 XML 文本 + String runXml = XmlUtils.marshaltoString(run, true); + return runXml.contains("w14:reflection") ? "是" : "否"; + } + //发光 + public static String getParagraphTextGlow(R run, String text) { + // 转换为 XML 文本 + String runXml = XmlUtils.marshaltoString(run, true); + return runXml.contains("w14:glow") ? "是" : "否"; + } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/ColorUtils.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/ColorUtils.java new file mode 100644 index 00000000..2acf188a --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/ColorUtils.java @@ -0,0 +1,38 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.section; + +import java.util.HashMap; +import java.util.Map; + +public class ColorUtils { + private static final Map colorMap = new HashMap<>(); + + static { + colorMap.put("FF0000", "红色"); + colorMap.put("000000", "黑色"); + colorMap.put("FFFFFF", "白色"); + colorMap.put("00FF00", "绿色"); + colorMap.put("0000FF", "蓝色"); + colorMap.put("FFFF00", "黄色"); + colorMap.put("FFA500", "橙色"); + colorMap.put("800080", "紫色"); + colorMap.put("808080", "灰色"); + colorMap.put("FFC0CB", "粉色"); + colorMap.put("A52A2A", "棕色"); + colorMap.put("00FFFF", "青色"); + colorMap.put("ADD8E6", "淡蓝色"); + colorMap.put("FFD700", "金色"); + colorMap.put("008000", "深绿色"); + colorMap.put("4B0082", "靛蓝色"); + colorMap.put("FF1493", "深粉色"); + colorMap.put("696969", "暗灰色"); + colorMap.put("D3D3D3", "浅灰色"); + + } + + + public static String colorCodeToChinese(String hex) { + if (hex == null) return "未知颜色"; + String upperHex = hex.toUpperCase(); + return colorMap.getOrDefault(upperHex, "未知颜色"); + } +} 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 new file mode 100644 index 00000000..8c596504 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/HeaderInfo.java @@ -0,0 +1,16 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.section; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class HeaderInfo { + public String text; + public String font; + public Double fontSizePt; + public String colorHex; + public String alignment; +} 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 424f996a..a8b62079 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 @@ -2,15 +2,29 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.section; import jakarta.xml.bind.JAXBElement; import org.docx4j.XmlUtils; +import org.docx4j.dml.Graphic; +import org.docx4j.dml.GraphicData; +import org.docx4j.dml.wordprocessingDrawing.Anchor; +import org.docx4j.dml.wordprocessingDrawing.Inline; +import org.docx4j.model.structure.DocumentModel; import org.docx4j.model.structure.HeaderFooterPolicy; import org.docx4j.model.structure.SectionWrapper; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart; -import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart; +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.CTPageMar; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz; +import javax.xml.namespace.QName; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author REN @@ -35,6 +49,99 @@ public class SectionPage { } } } + //节页面设置-上边距 + 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(); + } + } + return null; + } + //节页面设置-下边距 + 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(); + } + } + return null; + } + //节页面设置-左边距 + 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(); + } + } + return null; + } + //节页面设置-右边距 + 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(); + } + } + return null; + } + //装订线边距 + 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(); + } + } + return null; + } + + // 节页面设置 - 纸张方向 + public static String getSectionPageOrientation(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + PPr pPr = paragraph.getPPr(); + if (pPr != null && pPr.getSectPr() != null) { + SectPr sectPr = pPr.getSectPr(); + SectPr.PgSz pgSz = sectPr.getPgSz(); + + if (pgSz != null) { + STPageOrientation orientation = pgSz.getOrient(); + if (orientation != null) { + String orientValue = orientation.value(); + System.out.println("纸张方向: " + ("landscape".equals(orientValue) ? "横向" : "纵向")); + return "landscape".equals(orientValue) ? "横向" : "纵向"; + } else { + System.out.println("纸张方向: 默认纵向"); + return "纵向"; // 默认值 + } + } + } + return null; + } + // 纸张方向 大小 public static void getSectionPageSize(P paragraph) { PPr pPr = paragraph.getPPr(); @@ -90,21 +197,21 @@ public class SectionPage { return twips * 0.0176389; } // 水印 - public static void getSectionPageWatermark(R run) { - // 检查是否包含 Drawing(图片水印) - if (XmlUtils.marshaltoString(run, true).contains(" sections, WordprocessingMLPackage wordMLPackage) { @@ -132,6 +239,98 @@ public class SectionPage { } } + // ----- 奇数页眉 ----- + //奇数页眉文本 + public static String getOddHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).text : null; + } + //奇数页眉字体 + public static String getOddHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).font : null; + } + //奇数页眉字号 + public static String getOddHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + HeaderInfo hi = hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage) : null; + return (hi != null && hi.fontSizePt != null) ? hi.fontSizePt + " pt" : null; + } + //奇数页眉颜色 + public static String getOddHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultHeader() == null) { + return null; + } + String colorHex = extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).colorHex; + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); + } + + // 奇数页眉对齐方式 + public static String getOddHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + String align = hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).alignment : null; + return convertAlignToChinese(align); + } + + // ----- 偶数页眉 ----- + //偶数页眉文本 + public static String getEvenHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).text : null; + } + //偶数页眉字体 + public static String getEvenHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).font : null; + } + //偶数页眉字号 + public static String getEvenHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + HeaderInfo hi = hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage) : null; + return (hi != null && hi.fontSizePt != null) ? hi.fontSizePt + " pt" : null; + } + //偶数页眉颜色 + public static String getEvenHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenHeader() == null) { + return null; + } + String colorHex = extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).colorHex; + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); + } + + // 偶数页眉对齐方式 + public static String getEvenHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + String align = hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).alignment : null; + return convertAlignToChinese(align); + } + + // ----- 首页眉 ----- + //首页眉文本 + public static String getFirstHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).text : null; + } + //首页眉字体 + public static String getFirstHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + return hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).font : null; + } + //首页眉字号 + public static String getFirstHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + HeaderInfo hi = hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage) : null; + return (hi != null && hi.fontSizePt != null) ? hi.fontSizePt + " pt" : null; + } + //首页眉颜色 + public static String getFirstHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getFirstHeader() == null) { + return null; + } + // 先取颜色代码 + String colorHex = extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).colorHex; + + // 转换成中文颜色名 + return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); + } + + // 首页眉对齐方式 + public static String getFirstHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + String align = hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).alignment : null; + return convertAlignToChinese(align); + } + + + // 页脚 public static void getSectionPageFooter(List sections, WordprocessingMLPackage wordMLPackage) { for (int i = 0; i < sections.size(); i++) { @@ -157,6 +356,800 @@ public class SectionPage { System.out.println("----------------------------"); } } + // 奇数页 + //字体 + public static String getOddFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) return null; + HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); + return info == null ? null : info.font; + } + + //字号 + public static String getOddFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) return null; + HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); + return info == null ? null : String.valueOf(info.fontSizePt); + } + //对齐方式 + public static String getOddFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getDefaultFooter() == null) return null; + HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); + return convertAlignToChinese(info.alignment); + } + + // 偶数页 + //字体 + public static String getEvenFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenFooter() == null) return null; + HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); + return info == null ? null : info.font; + + } + //字号 + public static String getEvenFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenFooter() == null) return null; + HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); + return String.valueOf(info == null ? null : info.fontSizePt); + } + + public static String getEvenFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (hfp.getEvenFooter() == null) return null; + HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); + return convertAlignToChinese(info.alignment); + } + + + // ===== 首页页脚 ===== + public static String getFirstFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); + return info == null ? null : info.font; + } + + public static String getFirstFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + 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) { + HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); + return convertAlignToChinese(info.alignment); + } + + private static FooterPart getFooterPartByParagraph(P targetParagraph, WordprocessingMLPackage wordMLPackage, List sections, boolean isOddPage) { + MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); + List allContent = mainPart.getContent(); + + SectPr currentSectPr = null; + + for (Object obj : allContent) { + if (obj instanceof P) { + P paragraph = (P) obj; + + // 每遇到一个段落,判断是否有 SectPr,有则更新当前 Section + if (paragraph.getPPr() != null && paragraph.getPPr().getSectPr() != null) { + currentSectPr = paragraph.getPPr().getSectPr(); + } + + // 找到目标段落 + if (paragraph == targetParagraph) { + break; + } + } + } + + if (currentSectPr == null) { + // 有可能最后的 sectPr 写在 document.body 上而不是段落上 + currentSectPr = wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().getSectPr(); + } + + // 使用找到的 sectPr 来查找对应的 SectionWrapper + for (SectionWrapper section : sections) { + SectPr sectPrInWrapper = section.getSectPr(); + if (sectPrInWrapper == currentSectPr || isSectPrEqual(sectPrInWrapper, currentSectPr)) { + HeaderFooterPolicy hfp = section.getHeaderFooterPolicy(); + if (hfp != null) { + return isOddPage ? hfp.getDefaultFooter() : hfp.getEvenFooter(); + } + } + } + + return null; + } + + // 比较两个 SectPr 是否等价(可扩展) + private static boolean isSectPrEqual(SectPr a, SectPr b) { + return a != null && b != null && a.toString().equals(b.toString()); + } + + + public static HeaderInfo extractFooterInfo(FooterPart footerPart, WordprocessingMLPackage wordMLPackage) { + HeaderInfo info = new HeaderInfo(); + StringBuilder textBuilder = new StringBuilder(); + List contents = footerPart.getContent(); + + for (Object obj : contents) { + if (obj instanceof P) { + P paragraph = (P) obj; + PPr pPr = paragraph.getPPr(); + String styleId = (pPr != null && pPr.getPStyle() != null) ? pPr.getPStyle().getVal() : null; + + // 对齐方式 + if (pPr != null && pPr.getJc() != null) { + info.alignment = pPr.getJc().getVal().value(); + } + + 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()); + } + } + } + } + } + } + + info.text = textBuilder.toString(); + return info; + } + + + + // 修改你的 getFooterInfo,改为递归遍历拿到所有段落的格式 + private static HeaderInfo getFooterInfo(FooterPart footerPart, WordprocessingMLPackage wordMLPackage) { + if (footerPart == null) return null; + + List contentList = footerPart.getContent(); + for (Object obj : contentList) { + List

paragraphs = extractAllParagraphs(obj); + for (P paragraph : paragraphs) { + HeaderInfo info = extractHeaderInfoFromParagraph(paragraph); + if (info != null) return info; + } + } + return null; + } + private static HeaderInfo extractHeaderInfoFromParagraph(P paragraph) { + if (paragraph == null) return null; + + HeaderInfo info = new HeaderInfo(); + + // 取对齐方式,pPr->jc->val + if (paragraph.getPPr() != null && paragraph.getPPr().getJc() != null) { + info.alignment = paragraph.getPPr().getJc().getVal().toString(); + } + + // 遍历 Runs 查找字体和字号 + List runs = paragraph.getContent(); + for (Object runObj : runs) { + Object unwrappedRun = XmlUtils.unwrap(runObj); + if (unwrappedRun instanceof R) { + R run = (R) unwrappedRun; + RPr rPr = run.getRPr(); + if (rPr != null) { + // 字体名 (w:rFonts w:ascii/w:eastAsia/w:hAnsi) + if (rPr.getRFonts() != null) { + // 优先ascii字体 + if (rPr.getRFonts().getAscii() != null) { + info.font = rPr.getRFonts().getAscii(); + } else if (rPr.getRFonts().getEastAsia() != null) { + info.font = rPr.getRFonts().getEastAsia(); + } else if (rPr.getRFonts().getHAnsi() != null) { + info.font = rPr.getRFonts().getHAnsi(); + } + } + // 字号 w:sz (单位是 half-point) + if (rPr.getSz() != null && rPr.getSz().getVal() != null) { + int szVal = rPr.getSz().getVal().intValue(); + info.fontSizePt = (double) (szVal / 2.0f); // 转成 pt + } + if (info.font != null && info.fontSizePt > 0) { + // 找到足够信息就退出 + break; + } + } + } + } + + // 如果字体字号都没读到,则返回null,表示无有效信息 + if (info.font == null && info.fontSizePt ==null && info.alignment == null) { + return null; + } + + return info; + } + + + + private static List

extractAllParagraphs(Object content) { + List

paragraphs = new ArrayList<>(); + + if (content instanceof JAXBElement) { + content = ((JAXBElement) content).getValue(); + } + + if (content instanceof P) { + paragraphs.add((P) content); + } else if (content instanceof ContentAccessor) { + List children = ((ContentAccessor) content).getContent(); + for (Object child : children) { + paragraphs.addAll(extractAllParagraphs(child)); + } + } + return paragraphs; + } + + + + //页眉上边距 + public static String getHeaderTopDistance(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + try { + if (paragraph.getPPr() != null && paragraph.getPPr().getSectPr() != null) { + SectPr sectPr = paragraph.getPPr().getSectPr(); + if (sectPr.getPgMar() != null) { + SectPr.PgMar pgMar = sectPr.getPgMar(); + if (pgMar != null) { + // 页眉顶端距离 + double distanceCm = pgMar.getHeader().doubleValue() / 567.0; + return String.format("%.2f cm", distanceCm); + } + return "页眉边距未设置完整"; + } + } + return "未找到节或页边距"; + } catch (Exception e) { + e.printStackTrace(); + return "异常: " + e.getMessage(); + } + } + + //页脚下边距 + public static String getFooterBottomDistance(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + try { + if (paragraph.getPPr() != null && paragraph.getPPr().getSectPr() != null) { + SectPr sectPr = paragraph.getPPr().getSectPr(); + if (sectPr.getPgMar() != null) { + SectPr.PgMar pgMar = sectPr.getPgMar(); + if ( pgMar != null) { + double distanceCm = pgMar.getFooter().doubleValue() / 567.0; + return String.format("%.2f cm", distanceCm); + } + return "页脚边距未设置完整"; + } + } + return "未找到节或页边距"; + } catch (Exception e) { + e.printStackTrace(); + return "异常: " + e.getMessage(); + } + } + + + // 页脚(元素) 包含页码 返回 是/否 + + public static String getHeaderContainsPageNumber( + P paragraph, + HeaderFooterPolicy hfp, + WordprocessingMLPackage wordMLPackage, + List sections) { + + if (sections == null || sections.isEmpty()) { + return null; + } + + for (SectionWrapper section : sections) { + SectPr sectPr = section.getSectPr(); + if (sectPr == null) continue; + + if (sectPr.getPgNumType() != null) { + // 这里可以进一步获取格式,比如: + if (sectPr.getPgNumType().getFmt()!=null){ + + System.out.println("检测到页码格式: " + String.valueOf(sectPr.getPgNumType().getFmt())); + return "是"; + } + + } + } + + return null; + } + + + + + +// public static void readTextWatermark( +// P paragraph, +// HeaderFooterPolicy hfp, +// WordprocessingMLPackage wordMLPackage, +// List sections) throws Exception { +// +// if (sections == null || sections.isEmpty()) { +// System.out.println("Sections为空,无法读取水印"); +// return; +// } +// +// for (SectionWrapper section : sections) { +// SectPr sectPr = section.getSectPr(); +// if (sectPr == null) continue; +// +// // getEGHdrFtrReferences() 返回 Object List,但其中可能包含 List(嵌套) +// List hdrFtrRefs = Collections.singletonList(sectPr.getEGHdrFtrReferences()); +// if (hdrFtrRefs == null) continue; +// +// for (Object relObj : hdrFtrRefs) { +// Object unwrappedRel = XmlUtils.unwrap(relObj); +// +// if (unwrappedRel instanceof List) { +// // 拆开二层集合 +// List innerList = (List) unwrappedRel; +// for (Object innerRel : innerList) { +// Object unwrappedInnerRel = XmlUtils.unwrap(innerRel); +// System.out.println("内层rel 类型:" + unwrappedInnerRel.getClass().getName()); +// if (unwrappedInnerRel instanceof HeaderReference) { +// HeaderReference headerRef = (HeaderReference) unwrappedInnerRel; +// processHeaderReference(headerRef, wordMLPackage); +// } +// } +// } else { +// System.out.println("外层rel 类型:" + unwrappedRel.getClass().getName()); +// +// if (unwrappedRel instanceof HeaderReference) { +// HeaderReference headerRef = (HeaderReference) unwrappedRel; +// processHeaderReference(headerRef, wordMLPackage); +// } +// } +// } +// } +// } +// +// // 抽出处理页眉的方法,读取页眉内容并打印文本及样式 +// private static void processHeaderReference(HeaderReference headerRef, WordprocessingMLPackage wordMLPackage) throws Exception { +// String rId = headerRef.getId(); +// if (rId == null) return; +// Part part = wordMLPackage.getMainDocumentPart().getRelationshipsPart().getPart(rId); +// if (!(part instanceof HeaderPart)) return; +// +// HeaderPart headerPart = (HeaderPart) part; +// System.out.println("读取页眉类型:" + headerRef.getType()); +// +// List headerContent = headerPart.getContent(); +// for (Object o : headerContent) { +// Object unwrapped = XmlUtils.unwrap(o); +// if (unwrapped instanceof P) { +// P p = (P) unwrapped; +// List runs = p.getContent(); +// for (Object rObj : runs) { +// Object runUnwrapped = XmlUtils.unwrap(rObj); +// if (runUnwrapped instanceof R) { +// R run = (R) runUnwrapped; +// // 检测DrawingML(水印的透明度和旋转等信息通常在这里) +// String runXml = XmlUtils.marshaltoString(run, true); +// +// // 检测VML WordArt(旧版水印) +// if (runXml.contains("]*opacity=\"([0-9\\.]+)\""); +// Matcher matcher = opacityPattern.matcher(vmlXml); +// if (matcher.find()) { +// String opacityStr = matcher.group(1); +// System.out.println("透明度: " + (Float.parseFloat(opacityStr) * 100) + "%"); +// } +// +// +// // 水印文字内容和字体字号 +// Pattern textpathPattern = Pattern.compile("]*string=\"([^\"]+)\"[^>]*style=\"([^\"]+)\""); +// Matcher textpathMatcher = textpathPattern.matcher(vmlXml); +// if (textpathMatcher.find()) { +// String text = textpathMatcher.group(1); +// String style = textpathMatcher.group(2); +// System.out.println("水印文字: " + text); +// System.out.println("样式字符串: " + style); +// +// // 解析字体和字号 +// Pattern fontFamilyPattern = Pattern.compile("font-family:([^;]+);?"); +// Matcher fontFamilyMatcher = fontFamilyPattern.matcher(style); +// if (fontFamilyMatcher.find()) { +// System.out.println("字体: " + fontFamilyMatcher.group(1)); +// } +// Pattern fontSizePattern = Pattern.compile("font-size:([^;]+);?"); +// Matcher fontSizeMatcher = fontSizePattern.matcher(style); +// if (fontSizeMatcher.find()) { +// System.out.println("字号: " + fontSizeMatcher.group(1)); +// } +// } +// } + + + + + + + + + + + + + + + + + + + + + // ----- 公共方法:获取单个属性(示例) ----- + //旋转角度 + public static String getRotationDegree(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("rotation:([-\\d\\.]+)f"); + Matcher matcher = pattern.matcher(vmlXml); + if (matcher.find()) { + float raw = Float.parseFloat(matcher.group(1)); + float deg = raw / 60000f; + return String.format("%.2f", deg); + } + return null; + } + //颜色 + public static String getFillColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("fillcolor=\"#([0-9a-fA-F]{6})\""); + Matcher matcher = pattern.matcher(vmlXml); + if (matcher.find()) { + return "#" + matcher.group(1).toUpperCase(); + } + return null; + } + //透明度 + public static String getOpacityPercent(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("opacity=\"([0-9a-fA-F]+)f\""); + Pattern opacityPattern = Pattern.compile("]*opacity=\"([0-9\\.]+)\""); + Matcher matcher = opacityPattern.matcher(vmlXml); + if (matcher.find()) { + String opacityStr = matcher.group(1); + float opacityPercent = Float.parseFloat(opacityStr) * 100; + return String.format("%.2f%%", opacityPercent); + } + + return null; + } + //水印文字 + public static String getWatermarkText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("]*string=\"([^\"]+)\""); + Matcher matcher = pattern.matcher(vmlXml); + if (matcher.find()) { + return matcher.group(1); + } + return null; + } + //字体 + public static String getFontFamily(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("font-family:([^;]+);"); + Matcher matcher = pattern.matcher(vmlXml); + if (matcher.find()) { + return matcher.group(1).trim(); + } + return null; + } + //字号 + public static String getFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("font-size:([^;]+);"); + Matcher matcher = pattern.matcher(vmlXml); + if (matcher.find()) { + return matcher.group(1).trim(); + } + return null; + } + //位置 + public static String getTextAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections); + if (vmlXml == null) return null; + Pattern pattern = Pattern.compile("v-text-align:([^;]+);"); + Matcher matcher = pattern.matcher(vmlXml); + if (matcher.find()) { + return matcher.group(1).trim(); + } + return null; + } + + + + //节 页面设置(纸张大小) 纸型 + public static String getPageSetType(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + // 获取段落所在节的 SectPr + SectPr currentSectPr = null; + + MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); + List allContent = mainPart.getContent(); + + for (Object obj : allContent) { + if (obj instanceof P) { + P p = (P) obj; + 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(); + } + + if (currentSectPr == null || currentSectPr.getPgSz() == null) { + return "未知纸张"; + } + + 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"; // 可根据需要补充 + } + + return "自定义纸张"; + } + + + // 宽度 + public static String getPageSetWidth(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); + List allContent = mainPart.getContent(); + SectPr currentSectPr = null; + // 遍历段落找到段落所在节的 SectPr + for (Object obj : allContent) { + if (obj instanceof P) { + P p = (P) obj; + + 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(); + } + + if (currentSectPr != null && currentSectPr.getPgSz() != null) { + SectPr.PgSz pgSz = currentSectPr.getPgSz(); + return pgSz.getH() != null ? twipToCm(pgSz.getH()) : "0"; + + } + + return "无"; + } + // 高度 + public static String getPageSetHeight(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); + List allContent = mainPart.getContent(); + SectPr currentSectPr = null; + + // 遍历段落找到段落所在节的 SectPr + for (Object obj : allContent) { + if (obj instanceof P) { + P p = (P) obj; + + 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(); + } + + if (currentSectPr != null && currentSectPr.getPgSz() != null) { + SectPr.PgSz pgSz = currentSectPr.getPgSz(); + return pgSz.getW() != null ? twipToCm(pgSz.getW()) :" 0"; + } + + return "无"; + } + + + public static String getPageSetupInfo(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); + List allContent = mainPart.getContent(); + SectPr currentSectPr = null; + + // 遍历段落找到段落所在节的 SectPr + for (Object obj : allContent) { + if (obj instanceof P) { + P p = (P) obj; + + 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(); + } + + if (currentSectPr != null && currentSectPr.getPgSz() != null) { + SectPr.PgSz pgSz = currentSectPr.getPgSz(); + return pgSz.getOrient() != null ? pgSz.getOrient().toString() : "未知"; + } + + return "无"; + } + + + + // ------------------------------------------- + // 私有方法:从参数中找到 VML 水印 XML 字符串(只取第一个匹配) + private static String getVMLWatermarkXml(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Exception { + if (sections == null || sections.isEmpty()) return null; + + for (SectionWrapper section : sections) { + SectPr sectPr = section.getSectPr(); + if (sectPr == null) continue; + + List hdrFtrRefs = Collections.singletonList(sectPr.getEGHdrFtrReferences()); + if (hdrFtrRefs == null) continue; + + for (Object relObj : hdrFtrRefs) { + Object unwrappedRel = XmlUtils.unwrap(relObj); + + if (unwrappedRel instanceof List) { + for (Object innerRel : (List) unwrappedRel) { + Object unwrappedInnerRel = XmlUtils.unwrap(innerRel); + if (unwrappedInnerRel instanceof HeaderReference) { + String rId = ((HeaderReference) unwrappedInnerRel).getId(); + if (rId == null) continue; + Part part = wordMLPackage.getMainDocumentPart().getRelationshipsPart().getPart(rId); + if (!(part instanceof HeaderPart)) continue; + + HeaderPart headerPart = (HeaderPart) part; + List headerContent = headerPart.getContent(); + for (Object o : headerContent) { + Object unwrapped = XmlUtils.unwrap(o); + if (unwrapped instanceof P) { + P p = (P) unwrapped; + List runs = p.getContent(); + for (Object rObj : runs) { + Object runUnwrapped = XmlUtils.unwrap(rObj); + if (runUnwrapped instanceof R) { + String runXml = XmlUtils.marshaltoString(runUnwrapped, true); + if (runXml.contains(" headerContent = headerPart.getContent(); + for (Object o : headerContent) { + Object unwrapped = XmlUtils.unwrap(o); + if (unwrapped instanceof P) { + P p = (P) unwrapped; + List runs = p.getContent(); + for (Object rObj : runs) { + Object runUnwrapped = XmlUtils.unwrap(rObj); + if (runUnwrapped instanceof R) { + String runXml = XmlUtils.marshaltoString(runUnwrapped, true); + if (runXml.contains(" contents = headerPart.getContent(); @@ -232,6 +1225,248 @@ public class SectionPage { } } } + public static HeaderInfo extractHeaderInfo(HeaderPart headerPart,WordprocessingMLPackage wordMLPackage) { + HeaderInfo info = new HeaderInfo(); + StringBuilder textBuilder = new StringBuilder(); + List contents = headerPart.getContent(); + + for (Object obj : contents) { + if (obj instanceof P) { + P paragraph = (P) obj; + // 先获取段落样式id + PPr pPr = paragraph.getPPr(); + String styleId = (pPr != null && pPr.getPStyle() != null) ? pPr.getPStyle().getVal() : null; + // 对齐方式 + if (paragraph.getPPr() != null && paragraph.getPPr().getJc() != null) { + info.alignment = paragraph.getPPr().getJc().getVal().value(); + } + + 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 = getFontSizeNameByRunAndStyles(run, wordMLPackage); +// if (info.fontSizePt == null) { +// // 样式查不到,就直接从Run获取字号数字 +// info.fontSizePt = getFontSizeFromRun(run); +// } +// +// } + // 取字号 + 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()); + } + } + } + } + } + } + + info.text = textBuilder.toString(); + return info; + } + private static String getFontFromRunOrStyle(RPr rPr, String styleId, WordprocessingMLPackage wordMLPackage) { + // 先取显式字体 + if (rPr != null && rPr.getRFonts() != null && rPr.getRFonts().getAscii() != null) { + return rPr.getRFonts().getAscii(); + } + // 再从样式表找 + if (styleId != null) { + StyleDefinitionsPart sdp = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart(); + Style style = sdp.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getRFonts() != null) { + return style.getRPr().getRFonts().getAscii(); + } + } + return null; + } + + private static Double getFontSizeFromRunOrStyle(RPr rPr, String styleId, WordprocessingMLPackage wordMLPackage) { + // 先取显式字号 + if (rPr != null && rPr.getSz() != null) { + return rPr.getSz().getVal().intValue() / 2.0; + } + // 再从样式表找 + if (styleId != null) { + StyleDefinitionsPart sdp = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart(); + Style style = sdp.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getSz() != null) { + return style.getRPr().getSz().getVal().intValue() / 2.0; + } + } + return null; + } + public static void extractTextAndStyleFromContent(List contentList, HeaderInfo info, StyleDefinitionsPart stylePart) { + for (Object obj : contentList) { + Object unwrapped = XmlUtils.unwrap(obj); + + if (unwrapped instanceof P) { + P paragraph = (P) unwrapped; + + // 先提取段落对齐 + if (info.alignment == null) { + if (paragraph.getPPr() != null && paragraph.getPPr().getJc() != null && paragraph.getPPr().getJc().getVal() != null) { + info.alignment = paragraph.getPPr().getJc().getVal().value(); + } else if (paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null && stylePart != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style pStyle = stylePart.getStyleById(styleId); + if (pStyle != null && pStyle.getPPr() != null && pStyle.getPPr().getJc() != null) { + info.alignment = pStyle.getPPr().getJc().getVal().value(); + } + } + } + + // 也尝试从段落样式继承字体 + if (info.font == null && paragraph.getPPr() != null && paragraph.getPPr().getPStyle() != null && stylePart != null) { + String styleId = paragraph.getPPr().getPStyle().getVal(); + Style pStyle = stylePart.getStyleById(styleId); + if (pStyle != null && pStyle.getRPr() != null && pStyle.getRPr().getRFonts() != null) { + RFonts pFonts = pStyle.getRPr().getRFonts(); + if (pFonts.getAscii() != null) { + info.font = pFonts.getAscii(); + } else if (pFonts.getEastAsia() != null) { + info.font = pFonts.getEastAsia(); + } + } + } + + // 遍历段落内容 + for (Object pContent : paragraph.getContent()) { + Object pUnwrapped = XmlUtils.unwrap(pContent); + + if (pUnwrapped instanceof Drawing) { + Drawing drawing = (Drawing) pUnwrapped; + extractTextAndStyleFromDrawing(drawing, info,stylePart); + } else if (pUnwrapped instanceof R) { + processRun((R) pUnwrapped, info, stylePart); + } else if (pUnwrapped instanceof ContentAccessor) { + extractTextAndStyleFromContent(((ContentAccessor) pUnwrapped).getContent(), info, stylePart); + } + } + + } else if (unwrapped instanceof R) { + processRun((R) unwrapped, info, stylePart); + + } else if (unwrapped instanceof JAXBElement) { + JAXBElement element = (JAXBElement) unwrapped; + Object value = element.getValue(); + if (value instanceof ContentAccessor) { + extractTextAndStyleFromContent(((ContentAccessor) value).getContent(), info, stylePart); + } + + } else if (unwrapped instanceof ContentAccessor) { + extractTextAndStyleFromContent(((ContentAccessor) unwrapped).getContent(), info, stylePart); + } + } + } + + public static void extractTextAndStyleFromDrawing(Drawing drawing, HeaderInfo info, StyleDefinitionsPart stylePart) { + if (drawing == null) return; + + // docx4j的Drawing里包含 Inline 或 Anchor + List drawingContents = drawing.getAnchorOrInline(); + if (drawingContents == null) return; + + for (Object drawingObj : drawingContents) { + Object unwrapped = XmlUtils.unwrap(drawingObj); + Graphic graphic = null; + if (unwrapped instanceof Inline) { + graphic = ((Inline) unwrapped).getGraphic(); + } else if (unwrapped instanceof Anchor) { + graphic = ((Anchor) unwrapped).getGraphic(); + } + + if (graphic == null) continue; + + GraphicData graphicData = graphic.getGraphicData(); + if (graphicData == null) continue; + + List gdContent = graphicData.getAny(); + for (Object gdObj : gdContent) { + Object gdUnwrapped = XmlUtils.unwrap(gdObj); + if (gdUnwrapped instanceof ContentAccessor) { + extractTextAndStyleFromContent(((ContentAccessor) gdUnwrapped).getContent(), info, stylePart); + } + } + } + } + + + // 提取字体和字号 + public static void processRun(R run, HeaderInfo info, StyleDefinitionsPart stylePart) { + if (run == null) return; + RPr rPr = run.getRPr(); + + // 颜色 + if (rPr != null && rPr.getColor() != null && rPr.getColor().getVal() != null) { + info.colorHex = rPr.getColor().getVal(); + } + + // 字体 + if (info.font == null) { + RFonts rFonts = rPr != null ? rPr.getRFonts() : null; + + // 先尝试Run自身字体 + if (rFonts != null) { + if (rFonts.getAscii() != null) { + info.font = rFonts.getAscii(); + } else if (rFonts.getEastAsia() != null) { + info.font = rFonts.getEastAsia(); + } + } + + // 如果没有,尝试从样式中继承(需自己写查找代码) + if (info.font == null && stylePart != null && run.getRPr() != null && run.getRPr().getRStyle() != null) { + String styleId = run.getRPr().getRStyle().getVal(); + Style style = stylePart.getStyleById(styleId); + if (style != null && style.getRPr() != null && style.getRPr().getRFonts() != null) { + RFonts styleFonts = style.getRPr().getRFonts(); + if (styleFonts.getAscii() != null) { + info.font = styleFonts.getAscii(); + } else if (styleFonts.getEastAsia() != null) { + info.font = styleFonts.getEastAsia(); + } + } + } + } + + // 字号 + if (info.fontSizePt == null && rPr != null && rPr.getSz() != null) { + info.fontSizePt = rPr.getSz().getVal().intValue() / 2.0; + } + + // 文本内容 + for (Object runContent : run.getContent()) { + Object val = XmlUtils.unwrap(runContent); + if (val instanceof Text) { + info.text += ((Text) val).getValue(); + } + } + } + + public static void extractTextFromFooter(FooterPart footerPart, WordprocessingMLPackage wordMLPackage) { List contents = footerPart.getContent(); @@ -304,6 +1539,78 @@ public class SectionPage { } } } + // 英文对齐转换为中文 + public static String convertAlignToChinese(String align) { + if (align == null) return null; + switch (align.toLowerCase()) { + case "left": return "左对齐"; + case "center": return "居中对齐"; + case "right": return "右对齐"; + case "both": return "两端对齐"; + default: return align; + } + } + // 根据Run和wordMLPackage样式表,尝试获取字号名称(三号、四号) + private static String getFontSizeNameByRunAndStyles(R run, WordprocessingMLPackage wordMLPackage) { + if (run == null || wordMLPackage == null) return null; + RPr rPr = run.getRPr(); + String styleId = null; + if (rPr != null && rPr.getRStyle() != null) { + styleId = rPr.getRStyle().getVal(); + } + if (styleId == null) return null; + + try { + Styles styles = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart().getJaxbElement(); + for (Style style : styles.getStyle()) { + if (styleId.equals(style.getStyleId())) { + if (style.getRPr() != null && style.getRPr().getSz() != null) { + BigInteger sz = style.getRPr().getSz().getVal(); + if (sz != null) { + double pt = sz.intValue() / 2.0; + return ptToWordFontSizeName(pt); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + // 从Run直接获取字号数字,转成字符串 + private static String getFontSizeFromRun(R run) { + if (run == null) return null; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getSz() != null) { + BigInteger sz = rPr.getSz().getVal(); + if (sz != null) { + double pt = sz.intValue() / 2.0; + return ptToWordFontSizeName(pt); + } + } + return null; + } + + // 映射pt值到Word字号名称,比如16pt对应三号 + private static String ptToWordFontSizeName(double pt) { + if (pt == 42) return "初号"; + if (pt == 36) return "小初"; + if (pt == 28) return "一号"; + if (pt == 24) return "小一"; + if (pt == 22) return "二号"; + if (pt == 18) return "小二"; + if (pt == 16) return "三号"; + if (pt == 15) return "小三"; + if (pt == 14) return "四号"; + if (pt == 12) return "小四"; + if (pt == 10.5) return "五号"; + if (pt == 9) return "小五"; + if (pt == 7.5) return "六号"; + if (pt == 6) return "小六"; + return pt + " pt"; + } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/table/TableIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/table/TableIng.java new file mode 100644 index 00000000..23012734 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/table/TableIng.java @@ -0,0 +1,194 @@ +package pc.exam.pp.module.judgement.utils.wps_word.docx4j.table; + +import org.apache.xmlbeans.XmlCursor; +import org.docx4j.wml.CTEndnotes; +import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils; + +import javax.xml.namespace.QName; +import java.util.ArrayList; +import java.util.List; + +public class TableIng { + + private static final String W_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + + /** + * 表格属性 - 对齐方式 + */ + public static String getTableAlignment(XmlCursor tblCursor, CTEndnotes endnotes) { + tblCursor.push(); + try { + String jcPath = "declare namespace w='" + W_NAMESPACE + "' .//w:tblPr/w:jc"; + tblCursor.selectPath(jcPath); + if (tblCursor.toNextSelection()) { + String alignVal = tblCursor.getAttributeText(new QName(W_NAMESPACE, "val")); + return convertAlignToChinese(alignVal); + } + return null; + } finally { + tblCursor.pop(); + } + } + + /** + * 表格属性 - 列宽 + */ + public static String getTableColumnWidths(XmlCursor tblCursor, CTEndnotes endnotes) { + tblCursor.push(); // 保存原位置 + List widths = new ArrayList<>(); + try { + String gridPath = "declare namespace w='" + W_NAMESPACE + "' .//w:tblGrid/w:gridCol"; + tblCursor.selectPath(gridPath); + int colIndex = 0; + while (tblCursor.toNextSelection()) { + colIndex++; + String wAttr = tblCursor.getAttributeText(new QName(W_NAMESPACE, "w")); + if (wAttr != null) { + try { + int twips = Integer.parseInt(wAttr); + double cm = twips * 2.54 / 1440; + widths.add(String.format("第%d列宽度=%.2fcm", colIndex, cm)); + } catch (NumberFormatException e) { + widths.add("第" + colIndex + "列宽度=未知"); + } + } else { + widths.add("第" + colIndex + "列宽度=未知"); + } + } + if (widths.isEmpty()) { + return "表格无列宽信息"; + } + return String.join(";", widths); + } finally { + tblCursor.pop(); // 回到原位置 + } + } + + + + + /** + * 表格属性 - 行高 + */ + public static String getTableRowHeights(XmlCursor tblCursor, CTEndnotes endnotes) { + tblCursor.push(); + List heights = new ArrayList<>(); + try { + String trPath = "declare namespace w='" + W_NAMESPACE + "' .//w:tr"; + tblCursor.selectPath(trPath); + + int rowIndex = 0; + while (tblCursor.toNextSelection()) { + rowIndex++; + XmlCursor rowCursor = tblCursor.newCursor(); + String trHeightPath = "declare namespace w='" + W_NAMESPACE + "' .//w:trPr/w:trHeight"; + rowCursor.selectPath(trHeightPath); + + String val = null; + if (rowCursor.toNextSelection()) { + val = rowCursor.getAttributeText(new QName(W_NAMESPACE, "val")); + } + + String heightStr; + if (val != null) { + try { + int twips = Integer.parseInt(val); + double cm = twips * 2.54 / 1440; + heightStr = String.format("%.2fcm", cm); + } catch (NumberFormatException e) { + heightStr = "未知"; + } + } else { + heightStr = "未设置"; + } + + heights.add("第" + rowIndex + "行高度=" + heightStr); + + rowCursor.dispose(); + } + return String.join(";", heights); + } finally { + tblCursor.pop(); + } + } + + + /** + * 表格边框 - 线型 + */ + public static String getTableBorderStyle(XmlCursor tblCursor, CTEndnotes endnotes) { + tblCursor.push(); + try { + String path = "declare namespace w='" + W_NAMESPACE + "' .//w:tblPr/w:tblBorders/*"; + tblCursor.selectPath(path); + if (tblCursor.toNextSelection()) { + String val = tblCursor.getAttributeText(new QName(W_NAMESPACE, "val")); + return val != null ? val : null; + } + return null; + } finally { + tblCursor.pop(); + } + } + + /** + * 表格边框 - 颜色 + */ + public static String getTableBorderColor(XmlCursor tblCursor, CTEndnotes endnotes) { + tblCursor.push(); + try { + String path = "declare namespace w='" + W_NAMESPACE + "' .//w:tblPr/w:tblBorders/*"; + tblCursor.selectPath(path); + if (tblCursor.toNextSelection()) { + String val = tblCursor.getAttributeText(new QName(W_NAMESPACE, "color")); + System.out.println(val); + System.out.println(ColorUtils.getChineseColorName(val)); + + return ColorUtils.getChineseColorName(val); + } + return null; + } finally { + tblCursor.pop(); + } + } + + /** + * 表格边框 - 宽度 + */ + public static String getTableBorderWidth(XmlCursor tblCursor, CTEndnotes endnotes) { + tblCursor.push(); + try { + String path = "declare namespace w='" + W_NAMESPACE + "' .//w:tblPr/w:tblBorders/*"; + tblCursor.selectPath(path); + if (tblCursor.toNextSelection()) { + String val = tblCursor.getAttributeText(new QName(W_NAMESPACE, "sz")); + if (val != null) { + try { + int szValue = Integer.parseInt(val); + float ptValue = szValue / 8.0f; + return ptValue + "磅"; + } catch (NumberFormatException e) { + return null; // 无法解析数字返回null + } + } + return null; + } + return null; + } finally { + tblCursor.pop(); + } + } + // 英文对齐转换为中文 + public static String convertAlignToChinese(String align) { + if (align == null) return null; + switch (align.toLowerCase()) { + case "left": return "左对齐"; + case "center": return "居中对齐"; + case "right": return "右对齐"; + case "both": return "两端对齐"; + default: return align; + } + } + + +} 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 7a4eb5f9..d549d227 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,27 +1,36 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.text; -import jakarta.xml.bind.JAXBElement; -import jakarta.xml.bind.JAXBException; +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.CTWordprocessingShape; +import org.docx4j.dml.*; import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.docx4j.jaxb.Context; -import org.docx4j.wml.CTTextEffect; -import org.docx4j.wml.R; -import org.w3c.dom.Document; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.*; +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; 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.xpath.*; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.util.Iterator; import java.util.List; public class TextInfo { // 文本内容 - public static List getTextInfo(List judgementWordsVOS, Anchor anchor, int betoLong, String firstId){ + public static List getTextInfo(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage){ String name = "【第" + betoLong + "个图形】【文本框】"; String parName = "【文本】"; String neiRongName = parName + "【文本内容】"; @@ -39,28 +48,23 @@ public class TextInfo { // 文本内容 CTWordprocessingShape textInfo = (CTWordprocessingShape) value; String textValue = textInfo.getTxbx().getTxbxContent().getContent().toString(); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + neiRongName + textValue, name + neiRongName + textValue, name); // 文字方向 String vertValue = textInfo.getBodyPr().getVert().value(); vertValue = getTextDirection(vertValue); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + fangXiangName + vertValue, name + fangXiangName + vertValue, name); String tianChongName = parName + "【填充方式】"; // 文本填充-填充方式 TODO 存在标签对不上的问题 if (textInfo.getSpPr().getSolidFill() != null) { // 纯色填充 String typeValue = "纯色填充"; - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + tianChongName + typeValue, name + tianChongName + typeValue, name); if (textInfo.getSpPr().getSolidFill().getSrgbClr() != null) { // 纯色填充-颜色 String color = textInfo.getSpPr().getSolidFill().getSrgbClr().getVal(); tianChongName = tianChongName + "【颜色】"; - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + tianChongName + color, name + tianChongName + color, name); } else if (textInfo.getSpPr().getSolidFill().getSchemeClr() != null) { // 纯色填充-颜色 String color = textInfo.getSpPr().getSolidFill().getSrgbClr().getVal(); tianChongName = tianChongName + "【颜色】"; - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + tianChongName + color, name + tianChongName + color, name); } } if (textInfo.getSpPr().getBlipFill() != null) { @@ -82,14 +86,390 @@ public class TextInfo { String layoutHuanRaoName = "【布局】【环绕方式】"; String layoutHuanRaoValue = anchor.getWrapSquare().getWrapText().value(); layoutHuanRaoValue = getTextDirection(layoutHuanRaoValue); - judgementWordsVOS = DocxSetInfo.setInfo(judgementWordsVOS, parentId, firstId, name + layoutHuanRaoName + layoutHuanRaoValue, name + layoutHuanRaoName + layoutHuanRaoValue, name); return judgementWordsVOS; } + // 文本内容 + public static String getTextValue(List judgementWordsVOS, Anchor anchor, int betoLong,WordprocessingMLPackage wordMLPackage){ + Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0); + // 正确处理 JAXBElement + if (graphicData instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) graphicData; + Object value = jaxbElement.getValue(); - public static List getTextFillFromRun(List judgementWordsVOS, R run, int betoLong, String firstId) throws JAXBException, XmlException { + // 现在可以尝试转换为实际类型 + if (value.getClass().getName().contains("CTWordprocessingShape")) { + if (value instanceof CTWordprocessingShape) { + // 文本内容 + CTWordprocessingShape textInfo = (CTWordprocessingShape) value; + String textValue = textInfo.getTxbx().getTxbxContent().getContent().toString(); + if (textValue!=null){ + return textValue; + } + } + } + + } + return null; + } + // 文字方向 + public static String getTextVert(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { + if (anchor == null + || anchor.getGraphic() == null + || anchor.getGraphic().getGraphicData() == null + || anchor.getGraphic().getGraphicData().getAny() == null + || anchor.getGraphic().getGraphicData().getAny().isEmpty()) { + return null; + } + + 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; + + // 文字方向 + String vertValue = textInfo.getBodyPr().getVert() != null + ? textInfo.getBodyPr().getVert().value() + : null; + + // 旋转角度(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 "水平"; + } + if ("vert".equals(vertValue)) { + return "垂直"; + } + if (angle == 90) { + return "所有文字旋转90度"; + } + if (angle == 270) { + return "所有文字旋转270度"; + } + if ("eaVert".equals(vertValue) && angle == 270) { + return "中文字符旋转270度"; + } + + return "未知方向"; + } + } + return null; + } + + + //填充方式 + public static String getTianChongValue(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) { + if (textInfo.getSpPr().getSolidFill() != null) { + return "纯色填充"; + } else if (textInfo.getSpPr().getBlipFill() != null) { + return "图片填充"; + } else if (textInfo.getSpPr().getGradFill() != null) { + return "渐变填充"; + } else { + return "无填充"; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + //纯色填充→颜色 + 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) { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + System.out.println("Anchor 或 Graphic 为空"); + return "无纯色填充"; + } + + List anyList = anchor.getGraphic().getGraphicData().getAny(); + if (anyList == null || anyList.isEmpty()) { + System.out.println("GraphicData 中没有任何对象"); + return "无纯色填充"; + } + + for (Object obj : anyList) { + Object value = obj instanceof JAXBElement ? ((JAXBElement) obj).getValue() : obj; + + // 文本框一般是 CTWordprocessingShape 类型 + if (value.getClass().getName().endsWith("CTWordprocessingShape")) { + try { + // 反射调用 getSpPr() + Method getSpPrMethod = value.getClass().getMethod("getSpPr"); + Object spPrObj = getSpPrMethod.invoke(value); + + if (spPrObj == null) { + System.out.println("文本框 spPr 为 null"); + return "无纯色填充"; + } + + // 反射调用 getSolidFill() + Method getSolidFillMethod = spPrObj.getClass().getMethod("getSolidFill"); + Object solidFillObj = getSolidFillMethod.invoke(spPrObj); + + if (solidFillObj == null) { + System.out.println("spPr 中无纯色填充"); + return "无纯色填充"; + } + + // 获取 srgbClr 或 schemeClr + Method getSrgbClrMethod = solidFillObj.getClass().getMethod("getSrgbClr"); + Object srgbClrObj = getSrgbClrMethod.invoke(solidFillObj); + + Method getSchemeClrMethod = solidFillObj.getClass().getMethod("getSchemeClr"); + Object schemeClrObj = getSchemeClrMethod.invoke(solidFillObj); + + // 透明度提取逻辑 + String transparency = "0%"; // 默认完全不透明 + + if (srgbClrObj != null) { + transparency = extractAlphaTransparency(srgbClrObj); + return transparency; + } else if (schemeClrObj != null) { + transparency = extractAlphaTransparency(schemeClrObj); + return transparency; + } else { + System.out.println("无srgbClr和schemeClr颜色"); + return transparency; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } else { + System.out.println("未识别的类型: " + value.getClass().getName()); + } + } + return null; + } + + + //文本效果(阴影)-绘制(是/否) + public static String getTextShadowEffect(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, XPathExpressionException { + if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) { + return "否"; + } + // 获取 GraphicData 里面的 JAXB 对象列表 + List anyList = anchor.getGraphic().getGraphicData().getAny(); + if (anyList == null || anyList.isEmpty()) { + return "否"; + } + // 这里我们遍历 anyList ,取出每个 JAXB 对象,转成 DOM,查找 shadow 节点 + for (Object jaxbObj : anyList) { + org.w3c.dom.Document doc = (org.w3c.dom.Document) XmlUtils.marshaltoW3CDomDocument(jaxbObj); + org.w3c.dom.Element root = (org.w3c.dom.Element) doc.getDocumentElement(); + + // 用 XPath 查询所有 w14:shadow 元素(不区分具体命名空间,使用local-name()) + XPath xpath = XPathFactory.newInstance().newXPath(); + + // XPath 表达式查找所有带有局部名为shadow的元素 + String expression = "//*[local-name()='shadow']"; + + NodeList shadowNodes = (NodeList) xpath.evaluate(expression, root, XPathConstants.NODESET); + if (shadowNodes.getLength() > 0) { + return "是"; + } + } + + 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"); + return null; + } + + GraphicData graphicData = anchor.getGraphic().getGraphicData(); + List anyList = graphicData.getAny(); + if (anyList == null || anyList.isEmpty()) { + System.out.println("No content inside GraphicData"); + return null; + } + + for (Object anyObj : anyList) { + Object wspObj = anyObj; + if (anyObj instanceof JAXBElement) { + wspObj = ((JAXBElement) anyObj).getValue(); + } + if (wspObj == null) continue; + + try { + // 反射获取txbx + java.lang.reflect.Method getTxbxMethod = wspObj.getClass().getMethod("getTxbx"); + Object txbx = getTxbxMethod.invoke(wspObj); + if (txbx == null) continue; + + // 反射获取txbxContent + java.lang.reflect.Method getTxbxContentMethod = txbx.getClass().getMethod("getTxbxContent"); + Object txbxContent = getTxbxContentMethod.invoke(txbx); + if (txbxContent == null) continue; + + // 反射获取w:p列表 + java.lang.reflect.Method getContentListMethod = txbxContent.getClass().getMethod("getContent"); + List paragraphs = (List) getContentListMethod.invoke(txbxContent); + if (paragraphs == null || paragraphs.isEmpty()) continue; + + for (Object pObj : paragraphs) { + if (pObj instanceof P) { + P paragraph = (P) pObj; + + List pContent = paragraph.getContent(); + if (pContent == null) continue; + + for (Object runObj : pContent) { + if (runObj instanceof R) { + R run = (R) runObj; + + // 获取文本内容 + String text = ""; + List runContents = run.getContent(); + if (runContents != null) { + for (Object rc : runContents) { + if (rc instanceof Text) { + text += ((Text) rc).getValue(); + } + } + } + + // 获取字号信息(先尝试从Run样式查样式表) + String fontSizeName = getFontSizeNameByRunAndStyles(run, wordMLPackage); + if (fontSizeName == null) { + // 样式查不到,就直接从Run获取字号数字 + fontSizeName = getFontSizeFromRun(run); + } + + System.out.println("文本内容: " + text); + System.out.println("字号: " + (fontSizeName == null ? "未设置" : fontSizeName)); + System.out.println("-------"); + + if (fontSizeName != null) { + return fontSizeName; + } + } + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + return null; + } + + // 根据Run和wordMLPackage样式表,尝试获取字号名称(三号、四号) + private static String getFontSizeNameByRunAndStyles(R run, WordprocessingMLPackage wordMLPackage) { + if (run == null || wordMLPackage == null) return null; + + RPr rPr = run.getRPr(); + String styleId = null; + if (rPr != null && rPr.getRStyle() != null) { + styleId = rPr.getRStyle().getVal(); + } + + if (styleId == null) return null; + + try { + Styles styles = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart().getJaxbElement(); + for (Style style : styles.getStyle()) { + if (styleId.equals(style.getStyleId())) { + if (style.getRPr() != null && style.getRPr().getSz() != null) { + BigInteger sz = style.getRPr().getSz().getVal(); + if (sz != null) { + double pt = sz.intValue() / 2.0; + return ptToWordFontSizeName(pt); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + // 从Run直接获取字号数字,转成字符串 + private static String getFontSizeFromRun(R run) { + if (run == null) return null; + RPr rPr = run.getRPr(); + if (rPr != null && rPr.getSz() != null) { + BigInteger sz = rPr.getSz().getVal(); + if (sz != null) { + double pt = sz.intValue() / 2.0; + return ptToWordFontSizeName(pt); + } + } + return null; + } + + // 映射pt值到Word字号名称,比如16pt对应三号 + private static String ptToWordFontSizeName(double pt) { + if (pt == 42) return "初号"; + if (pt == 36) return "小初"; + if (pt == 28) return "一号"; + if (pt == 24) return "小一"; + if (pt == 22) return "二号"; + if (pt == 18) return "小二"; + if (pt == 16) return "三号"; + if (pt == 15) return "小三"; + if (pt == 14) return "四号"; + if (pt == 12) return "小四"; + if (pt == 10.5) return "五号"; + if (pt == 9) return "小五"; + if (pt == 7.5) return "六号"; + if (pt == 6) return "小六"; + return pt + " pt"; + } + + + public static List getTextFillFromRun(List judgementWordsVOS, R run, int betoLong, String firstId) throws JAXBException, XmlException { String xml = XmlUtils.marshaltoString(run, true); org.w3c.dom.Node node = Context.jc.createMarshaller().getNode(run); XmlCursor xmlCursor = XmlObject.Factory.parse(node).newCursor(); @@ -98,7 +478,38 @@ public class TextInfo { return judgementWordsVOS; } + // 抽取透明度的辅助方法 + private static String extractAlphaTransparency(Object colorObj) { + try { + // egColorTransform是一个List + Method getEgColorTransformMethod = colorObj.getClass().getMethod("getEGColorTransform"); + List egColorTransformList = (List) getEgColorTransformMethod.invoke(colorObj); + if (egColorTransformList != null && !egColorTransformList.isEmpty()) { + for (Object transform : egColorTransformList) { + if (transform instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) transform; + if ("alpha".equals(jaxbElement.getName().getLocalPart())) { + Object alphaObj = jaxbElement.getValue(); + // 透明度字段通常是getVal() + Method getValMethod = alphaObj.getClass().getMethod("getVal"); + Object valObj = getValMethod.invoke(alphaObj); + if (valObj instanceof Number) { + double val = ((Number) valObj).doubleValue(); + // 透明度是0~100000之间,这里换算成百分比 + double opaquePercent = val / 1000.0; + double transparencyPercent = 100.0 - opaquePercent; + return String.format("%.1f%%", transparencyPercent); + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "0%"; // 默认不透明 + } private static String getWrapTextDescription(String wrapText) { if (wrapText == null) return "两侧"; @@ -121,13 +532,27 @@ public class TextInfo { case "vert270": return "垂直(270度)"; case "eaVert": - return "东亚垂直"; + return "垂直"; case "mongolianVert": - return "蒙古文垂直"; + return "垂直"; case "wordArtVert": - return "艺术字垂直"; + return "垂直"; case "wordArtVertRtl": - return "艺术字垂直(从右到左)"; + return "垂直"; + case "rot90": + return "所有文字旋转90度"; + case "rot270": + return "所有文字旋转270度"; + case "eaRot270": + return "中文字符旋转270度"; +// case "eaVert": +// return "东亚垂直"; +// case "mongolianVert": +// return "蒙古文垂直"; +// case "wordArtVert": +// return "艺术字垂直"; +// case "wordArtVertRtl": +// return "艺术字垂直(从右到左)"; default: return "水平(默认)"; } 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 new file mode 100644 index 00000000..c6dbe51b --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/utils/ColorUtils.java @@ -0,0 +1,161 @@ +package pc.exam.pp.module.judgement.utils.wps_word.utils; + +import java.util.HashMap; +import java.util.Map; + +public class ColorUtils { + + // 颜色映射表,key: 英文颜色名或16进制代码(小写),value: 中文颜色名 + private static final Map COLOR_MAP = new HashMap<>(); + + static { + // 英文颜色名映射 + COLOR_MAP.put("black", "黑色"); + COLOR_MAP.put("white", "白色"); + COLOR_MAP.put("blue", "蓝色"); + COLOR_MAP.put("red", "红色"); + COLOR_MAP.put("green", "绿色"); + COLOR_MAP.put("yellow", "黄色"); + COLOR_MAP.put("gray", "灰色"); + COLOR_MAP.put("grey", "灰色"); + COLOR_MAP.put("orange", "橙色"); + COLOR_MAP.put("purple", "紫色"); + COLOR_MAP.put("brown", "棕色"); + COLOR_MAP.put("pink", "粉色"); + COLOR_MAP.put("cyan", "青色"); + COLOR_MAP.put("magenta", "洋红"); + COLOR_MAP.put("lime", "酸橙绿"); + COLOR_MAP.put("maroon", "栗色"); + COLOR_MAP.put("navy", "藏青色"); + COLOR_MAP.put("olive", "橄榄绿"); + COLOR_MAP.put("teal", "水鸭色"); + COLOR_MAP.put("silver", "银色"); + COLOR_MAP.put("gold", "金色"); + COLOR_MAP.put("ee0000", "亮红色"); + COLOR_MAP.put("990000", "深红色"); + COLOR_MAP.put("6699ff", "浅蓝色"); + COLOR_MAP.put("006600", "深绿色"); + COLOR_MAP.put("99cc99", "浅绿色"); + COLOR_MAP.put("00b0f0", "浅蓝色"); + + + // 16进制颜色代码映射(统一小写) + COLOR_MAP.put("ff0000", "红色"); + COLOR_MAP.put("00ff00", "绿色"); + COLOR_MAP.put("0000ff", "蓝色"); + COLOR_MAP.put("ffff00", "黄色"); + COLOR_MAP.put("000000", "黑色"); + COLOR_MAP.put("ffffff", "白色"); + COLOR_MAP.put("808080", "灰色"); + COLOR_MAP.put("800000", "栗色"); + COLOR_MAP.put("ffa500", "橙色"); + COLOR_MAP.put("ffc0cb", "粉色"); + COLOR_MAP.put("00ffff", "青色"); + COLOR_MAP.put("800080", "紫色"); + COLOR_MAP.put("008000", "绿色"); + COLOR_MAP.put("000080", "藏青色"); + COLOR_MAP.put("808000", "橄榄绿"); + COLOR_MAP.put("008080", "水鸭色"); + COLOR_MAP.put("c0c0c0", "银色"); + COLOR_MAP.put("ffd700", "金色"); + COLOR_MAP.put("e54c5e", "金色"); + COLOR_MAP.put("00b050", "绿色"); + + + + COLOR_MAP.put("bg1", "背景 1"); + COLOR_MAP.put("bg1_5", "背景 1 深色 5%"); + COLOR_MAP.put("bg1_15", "背景 1 深色 15%"); + COLOR_MAP.put("bg1_25", "背景 1 深色 25%"); + COLOR_MAP.put("bg1_35", "背景 1 深色 35%"); + COLOR_MAP.put("bg1_50", "背景 1 深色 50%"); + + COLOR_MAP.put("tx1", "文字 1"); + COLOR_MAP.put("tx1_5", "文字 1 深色 5%"); + COLOR_MAP.put("tx1_15", "文字 1 深色 15%"); + COLOR_MAP.put("tx1_25", "文字 1 深色 25%"); + COLOR_MAP.put("tx1_35", "文字 1 深色 35%"); + COLOR_MAP.put("tx1_50", "文字 1 深色 50%"); + + COLOR_MAP.put("bg2", "背景 2"); + COLOR_MAP.put("bg2_5", "背景 2 深色 5%"); + COLOR_MAP.put("bg2_15", "背景 2 深色 15%"); + COLOR_MAP.put("bg2_25", "背景 2 深色 25%"); + COLOR_MAP.put("bg2_35", "背景 2 深色 35%"); + COLOR_MAP.put("bg2_50", "背景 2 深色 50%"); + + COLOR_MAP.put("tx2", "文字 2"); + COLOR_MAP.put("tx2_5", "文字 2 深色 5%"); + COLOR_MAP.put("tx2_15", "文字 2 深色 15%"); + COLOR_MAP.put("tx2_25", "文字 2 深色 25%"); + COLOR_MAP.put("tx2_35", "文字 2 深色 35%"); + COLOR_MAP.put("tx2_50", "文字 2 深色 50%"); + + COLOR_MAP.put("accent1", "个性色 1"); + COLOR_MAP.put("accent1_5", "个性色 1 深色 5%"); + COLOR_MAP.put("accent1_15", "个性色 1 深色 15%"); + COLOR_MAP.put("accent1_25", "个性色 1 深色 25%"); + COLOR_MAP.put("accent1_35", "个性色 1 深色 35%"); + COLOR_MAP.put("accent1_50", "个性色 1 深色 50%"); + COLOR_MAP.put("accent2", "个性色 2"); + COLOR_MAP.put("accent2_5", "个性色 2 深色 5%"); + COLOR_MAP.put("accent2_15", "个性色 2 深色 15%"); + COLOR_MAP.put("accent2_25", "个性色 2 深色 25%"); + COLOR_MAP.put("accent2_35", "个性色 2 深色 35%"); + COLOR_MAP.put("accent2_50", "个性色 2 深色 50%"); + + + COLOR_MAP.put("accent3", "个性色 3"); + COLOR_MAP.put("accent3_5", "个性色 3 深色 5%"); + COLOR_MAP.put("accent3_15", "个性色 3 深色 15%"); + COLOR_MAP.put("accent3_25", "个性色 3 深色 25%"); + COLOR_MAP.put("accent3_35", "个性色 3 深色 35%"); + COLOR_MAP.put("accent3_50", "个性色 3 深色 50%"); + + COLOR_MAP.put("accent4", "个性色 4"); + COLOR_MAP.put("accent4_5", "个性色 4 深色 5%"); + COLOR_MAP.put("accent4_15", "个性色 4 深色 15%"); + COLOR_MAP.put("accent4_25", "个性色 4 深色 25%"); + COLOR_MAP.put("accent4_35", "个性色 4 深色 35%"); + COLOR_MAP.put("accent4_50", "个性色 4 深色 50%"); + + COLOR_MAP.put("accent5", "个性色 5"); + COLOR_MAP.put("accent5_5", "个性色 5 深色 5%"); + COLOR_MAP.put("accent5_15", "个性色 5 深色 15%"); + COLOR_MAP.put("accent5_25", "个性色 5 深色 25%"); + COLOR_MAP.put("accent5_35", "个性色 5 深色 35%"); + COLOR_MAP.put("accent5_50", "个性色 5 深色 50%"); + COLOR_MAP.put("accent6", "个性色 6"); + COLOR_MAP.put("accent6_5", "个性色 6 深色 5%"); + COLOR_MAP.put("accent6_15", "个性色 6 深色 15%"); + COLOR_MAP.put("accent6_25", "个性色 6 深色 25%"); + COLOR_MAP.put("accent6_35", "个性色 6 深色 35%"); + COLOR_MAP.put("accent6_50", "个性色 6 深色 50%"); + } + + /** + * 根据英文颜色名或十六进制颜色代码获取中文颜色名 + */ + public static String getChineseColorName(String colorVal) { + if (colorVal == null) { + return "无"; + } + String key = colorVal.toLowerCase(); + + // 如果是8位ARGB格式(比如FF00B050),去掉Alpha通道,取后6位 + if (key.length() == 8) { + key = key.substring(2); + } + + // 查字典 + String chineseName = COLOR_MAP.get(key); + if (chineseName != null) { + return chineseName; + } + + // 查不到,返回原值(大写) + return colorVal.toUpperCase(); + } + +} +