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 e696a5fa..a4ad3692 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 @@ -92,26 +92,27 @@ public class AutoWpsController { ); return CommonResult.success(judgementWpsWordService.docxMaster(wpsDocxInfoVos, file)); } + /** * 获取指定考点的数据 * * @return * @throws Exception */ -// @PostMapping(value = "/slideMaster", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - @PostMapping(value = "/slideMaster") - public CommonResult> slideMaster(@RequestBody List wpsSlideInfoVos) throws Exception { -// public CommonResult> slideMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file) throws Exception { - // 手动解析JSON数组 -// ObjectMapper objectMapper = new ObjectMapper(); -// List wpsSlideInfoVos = objectMapper.readValue( -// jsonData, -// new TypeReference>() { -// } -// ); - MultipartFile file = null; + @PostMapping(value = "/slideMaster", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) +// @PostMapping(value = "/slideMaster") +// public CommonResult> slideMaster(@RequestBody List wpsSlideInfoVos) throws Exception { + public CommonResult> slideMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file) throws Exception { + // 手动解析JSON数组 + ObjectMapper objectMapper = new ObjectMapper(); + List wpsSlideInfoVos = objectMapper.readValue( + jsonData, + new TypeReference>() { + } + ); return CommonResult.success(judgementWpsPptxService.slideMaster(wpsSlideInfoVos, file)); } + /** * 获取指定考点的数据 * @@ -119,17 +120,16 @@ public class AutoWpsController { * @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 { + 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( + List wpsXlsxInfoVos = objectMapper.readValue( jsonData, new TypeReference>() { } ); - return CommonResult.success(judgementWpsExcelService.xlsxMaster(wpsXlsxInfoVos, file,cell)); + 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/controller/admin/AutoWps/vo/WpsSlideInfoVo.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/vo/WpsSlideInfoVo.java index 17a59b12..b36ef01e 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/vo/WpsSlideInfoVo.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/controller/admin/AutoWps/vo/WpsSlideInfoVo.java @@ -7,12 +7,6 @@ public class WpsSlideInfoVo { // 幻灯片位置(第几张幻灯片) private String slideIndex; - // 形状位置,只有在shape 形状(不包含图片) shapePic 形状图片 - private String shapeIndex; - - // 形状类型 slide 幻灯片设置 shape 形状(不包含图片) shapePic 形状图片 presentation 总幻灯片设置 - private String type; - // 大类名称 private String firstName; @@ -25,6 +19,6 @@ public class WpsSlideInfoVo { // 考点代码 private String examCode; - // 方式方法 + // 形状类型 slide 幻灯片设置 shape 形状(不包含图片) shapePic 形状图片 presentation 总幻灯片设置 private String method; } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePage.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePage.java index e87e72a4..e78aaeec 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePage.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePage.java @@ -2,15 +2,11 @@ package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.JAXBException; -import org.apache.poi.sl.usermodel.TextParagraph; import org.docx4j.dml.*; import org.docx4j.jaxb.XPathBinderAssociationIsPartialException; -import org.docx4j.openpackaging.parts.PresentationML.SlidePart; import org.pptx4j.pml.*; -import org.pptx4j.model.*; import java.math.BigInteger; -import java.util.List; public class ShapePage { @@ -419,7 +415,260 @@ public class ShapePage { // 动画 效果 - public static String getAnimation(org.pptx4j.pml.Shape sp, CTSlideTiming timing) throws JAXBException, XPathBinderAssociationIsPartialException { +// public static String getAnimation(org.pptx4j.pml.Shape sp, CTSlideTiming timing) throws JAXBException, XPathBinderAssociationIsPartialException { +// // 获取形状中的ID +// if (sp == null) return "无动画"; +// long spId = sp.getNvSpPr().getCNvPr().getId(); +// // 获取动画效果 +// CTTimeNodeList timeNodeList = timing.getTnLst(); +// for (Object obj : timeNodeList.getParOrSeqOrExcl()) { +// if (obj instanceof CTTLTimeNodeParallel) { +// CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; +// CTTLCommonTimeNodeData node = par.getCTn(); +// if (node.getChildTnLst().getParOrSeqOrExcl() != null) { +// CTTimeNodeList childTimeNodeList = node.getChildTnLst(); +// for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { +// if (object instanceof CTTLSetBehavior) { +// if (((CTTLSetBehavior) object).getCBhvr() != null) { +// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt() != null) { +// // 检查目标形状ID是否匹配 +// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) { +// // 返回动画效果 +// +// } +// } +// } +// } +// if (object instanceof CTTLTimeNodeSequence) { +// CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; +// CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); +// for (Object seqObjs : seqNodeList.getParOrSeqOrExcl()) { +// if (seqObjs instanceof CTTLSetBehavior) { +// if (((CTTLSetBehavior) seqObjs).getCBhvr() != null) { +// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt() != null) { +// // 检查目标形状ID是否匹配 +// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) { +// // 返回动画效果 +// +// } +// } +// } +// } +// if (seqObjs instanceof CTTLTimeNodeSequence) { +// CTTLTimeNodeSequence seqObj = (CTTLTimeNodeSequence) seqObjs; +// CTTimeNodeList seqNodeLists = seqObj.getCTn().getChildTnLst(); +// for (Object seqObjss : seqNodeLists.getParOrSeqOrExcl()) { +// if (seqObjss instanceof CTTLSetBehavior) { +// if (((CTTLSetBehavior) seqObjss).getCBhvr() != null) { +// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt() != null) { +// // 检查目标形状ID是否匹配 +// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) { +// // 返回动画效果 +// +// } +// } +// } +// } +// } +// } +// } +// } +// } +// } +// +// +// if (node.getDur() != null && !"indefinite".equals(node.getDur())) { +// return node.getDur(); +// } +// System.out.println(par.getCTn()); +// } +// } +// return "待开发"; +// } + + public static String getAnimation(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + final String spId = String.valueOf(sp.getNvSpPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找进入类(effect) + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 再找其它类型(包括 CTTLAnimateBehavior 的 ppt_x/ppt_y) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + /* 递归遍历所有 time nodes,返回首个命中的行为字符串 */ + private static String findFirstBehavior(CTTimeNodeList list, String spId, boolean preferEntrance) { + if (list == null || list.getParOrSeqOrExcl() == null) return null; + + for (Object raw : list.getParOrSeqOrExcl()) { + Object node = unwrap(raw); + + // 1) 直接是某种行为(Behavior) + String desc = tryDescribeIfBehavior(node, spId, preferEntrance); + if (desc != null) return desc; + + // 2) par / seq / excl,进入子列表递归 + CTTimeNodeList child = getChildList(node); + if (child != null) { + String got = findFirstBehavior(child, spId, preferEntrance); + if (got != null) return got; + } + } + return null; + } + + /* 如果对象是某种 Behavior,且目标命中当前 spId,则返回描述;否则返回 null */ + private static String tryDescribeIfBehavior(Object obj, String spId, boolean preferEntrance) { + // 统一 unwrap + obj = unwrap(obj); + + // --- 进入类效果(Effect)--- + if (obj instanceof CTTLAnimateEffectBehavior) { + CTTLAnimateEffectBehavior b = (CTTLAnimateEffectBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String filter = safe(b.getFilter()); // 如 "fade"、"strips(downRight)" 等 + return filter.isEmpty() ? "进入效果" : ("进入效果:" + filter); + } + } + + // 非优先阶段:其它类型 + if (!preferEntrance) { + // 通用 Animate (B 动画关键:ppt_x / ppt_y) + if (obj instanceof CTTLAnimateBehavior) { + CTTLAnimateBehavior b = (CTTLAnimateBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String attr = firstAttrName(b.getCBhvr()); + if ("ppt_x".equalsIgnoreCase(attr)) return "位置动画:X(ppt_x)"; + if ("ppt_y".equalsIgnoreCase(attr)) return "位置动画:Y(ppt_y)"; + return attr.isEmpty() ? "数值动画(Animate)" : ("数值动画(" + attr + ")"); + } + } + if (obj instanceof CTTLAnimateMotionBehavior) { + CTTLAnimateMotionBehavior b = (CTTLAnimateMotionBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "路径动画(Motion)"; + } + if (obj instanceof CTTLAnimateRotationBehavior) { + CTTLAnimateRotationBehavior b = (CTTLAnimateRotationBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "旋转动画"; + } + if (obj instanceof CTTLAnimateScaleBehavior) { + CTTLAnimateScaleBehavior b = (CTTLAnimateScaleBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "缩放动画"; + } + if (obj instanceof CTTLSetBehavior) { + CTTLSetBehavior b = (CTTLSetBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String to = (b.getTo()!=null && b.getTo().getStrVal()!=null) ? safe(b.getTo().getStrVal().getVal()) : ""; + String attr = firstAttrName(b.getCBhvr()); + if (!attr.isEmpty() || !to.isEmpty()) { + return "属性设置(Set):" + (attr.isEmpty() ? "" : attr) + (to.isEmpty() ? "" : (" -> " + to)); + } + return "属性设置(Set)"; + } + } + if (obj instanceof CTTLAnimateColorBehavior) { + CTTLAnimateColorBehavior b = (CTTLAnimateColorBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "颜色动画"; + } + if (obj instanceof CTTLCommandBehavior) { + CTTLCommandBehavior b = (CTTLCommandBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "命令动画(Command)"; + } + } + return null; + } + + /* 提取并返回子节点列表(par/seq/excl 三种) */ + private static CTTimeNodeList getChildList(Object node) { + node = unwrap(node); + if (node instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel n = (CTTLTimeNodeParallel) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + if (node instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence n = (CTTLTimeNodeSequence) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + if (node instanceof CTTLTimeNodeExclusive) { + CTTLTimeNodeExclusive n = (CTTLTimeNodeExclusive) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + return null; + } + + /* 判断行为的目标是否命中当前形状 spId */ + private static boolean isTarget(CTTLCommonBehaviorData cb, String spId) { + if (cb == null || cb.getTgtEl() == null || cb.getTgtEl().getSpTgt() == null) return false; + String tgt = cb.getTgtEl().getSpTgt().getSpid(); + return String.valueOf(spId).equals(tgt); + } + + /* 读第一个 attrName(如 ppt_x / ppt_y / style.visibility ...)*/ + private static String firstAttrName(CTTLCommonBehaviorData cb) { + if (cb == null || cb.getAttrNameLst() == null) return ""; + java.util.List names = cb.getAttrNameLst().getAttrName(); + return (names != null && !names.isEmpty()) ? safe(names.get(0)) : ""; + } + + /* 统一解包 JAXBElement */ + private static Object unwrap(Object o) { + return (o instanceof JAXBElement) ? ((JAXBElement) o).getValue() : o; + } + + /* 安全字符串 */ + private static String safe(String s) { + return s == null ? "" : s.trim(); + } + + + // 动画-方向 + public static String getAnimateDirection(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + String dir = getAnimation(sp, timing); + if (dir.equals("downRight")) { + return "右下"; + } + if (dir.equals("downLeft")) { + return "左下"; + } + if (dir.equals("upRight")) { + return "右上"; + } + if (dir.equals("upLeft")) { + return "左上"; + } + if (dir.equals("right")) { + return "向右"; + } + if (dir.equals("left")) { + return "向左"; + } + if (dir.equals("down")) { + return "向下"; + } + if (dir.equals("up")) { + return "向上"; + } + if (dir.equals("fromTop")) { + return "从顶部"; + } + if (dir.equals("fromBottom")) { + return "从底部"; + } + if (dir.equals("fromLeft")) { + return "从左侧"; + } + if (dir.equals("fromRight")) { + return "从右侧"; + } + return ""; + } + + // 动画 触发方式 + public static String getAnimateTrigger(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { // 获取形状中的ID if (sp == null) return "无动画"; long spId = sp.getNvSpPr().getCNvPr().getId(); @@ -429,27 +678,189 @@ public class ShapePage { if (obj instanceof CTTLTimeNodeParallel) { CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; CTTLCommonTimeNodeData node = par.getCTn(); - if (node.getDur() != null && !"indefinite".equals(node.getDur())) { - return node.getDur(); - } - // 情况2:检查子节点 -// if (node.getChildTnLst() != null) { -// for (Object child : node.getChildTnLst().getParOrSeqOrExcl()) { -// if (child instanceof CTTLTimeNodeParallel) { -// String duration = getRealDuration(((CTTLTimeNodeParallel)child).getCTn()); -// if (duration != null) return duration; -// } -// else if (child instanceof CTTLAnimateEffectBehavior) { -// return ((CTTLAnimateEffectBehavior)child).getDur(); -// } -// // 其他类型处理... -// } -// } - System.out.println(par.getCTn()); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } + } + } + } + } + } + } + } + } + } + } } } - return "待开发"; + return ""; } + // 动画 持续时间 + public static String getAnimationDuration(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + + final String spId = String.valueOf(sp.getNvSpPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找“进入类效果(effect)” + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 找不到进入类,再找其它类型(运动、旋转、缩放、Set 等) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + // 动画 延迟 + public static String getAnimationDelay(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvSpPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return ""; + } + + /// 形状动作,鼠标单机-动作 @@ -1002,7 +1413,6 @@ public class ShapePage { } } } - return ""; } /// 文本轮廓 宽度 @@ -1052,5 +1462,86 @@ public class ShapePage { return "待开发"; } + // 文字效果 阴影→绘制 + public static String getTextBoxParagraphShadowDraw(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTEffectList ctEffectList = rPr.getEffectLst(); + if (ctEffectList.getInnerShdw() != null || ctEffectList.getOuterShdw() != null || ctEffectList.getPrstShdw() != null) { + return "是"; + } else { + return "否"; + } + } + } + } + } + return ""; + } + // 文字效果 阴影→预设 + // 文字效果 阴影→效果 + public static String getTextBoxParagraphShadowEffect(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 文字效果 倒影→绘制 + public static String getTextBoxParagraphReflectionDraw(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTEffectList ctEffectList = rPr.getEffectLst(); + if (ctEffectList != null && ctEffectList.getReflection() != null) { + return "是"; + } else { + return "否"; + } + } + } + } + } + return ""; + } + + + // 文字效果 倒影→预设 + // 文字效果 倒影→效果 + public static String getTextBoxParagraphReflectionEffect(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTEffectList ctEffectList = rPr.getEffectLst(); + CTReflectionEffect ctReflectionEffect = ctEffectList.getReflection(); + return SlideUtils.getShadowLikeDescription(ctReflectionEffect); + } + } + } + } + return ""; + } + // 文字效果 发光→绘制 + public static String getTextBoxParagraphGlowDraw(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 文字效果 发光→效果 + public static String getTextBoxParagraphGlowEffect(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 文字效果 转换→弯曲 + public static String getTextBoxParagraphConvertCurve(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 文字效果 单机鼠标 + public static String getTextBoxParagraphMouseClick(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + System.out.println(timing); + return "待开发"; + } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePic.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePic.java new file mode 100644 index 00000000..199386b5 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/ShapePic.java @@ -0,0 +1,859 @@ +package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; + +import jakarta.xml.bind.JAXBElement; +import org.docx4j.dml.*; +import org.pptx4j.pml.*; + +import java.math.BigInteger; + +public class ShapePic { + + // 大小 - 高度 + public static String getShapeSizeHeight(Pic sp, CTSlideTiming timing) { + CTTransform2D xfrm = sp.getSpPr().getXfrm(); + // 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU) + CTPositiveSize2D ext = xfrm.getExt(); // 尺寸 + double heightPt = emuToPt(ext.getCy()); + return String.valueOf(heightPt); + } + + // 大小 - 宽度 + public static String getShapeSizeWidthPt(Pic sp, CTSlideTiming timing) { + CTTransform2D xfrm = sp.getSpPr().getXfrm(); + // 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU) + CTPositiveSize2D ext = xfrm.getExt(); // 尺寸 + double widthPt = emuToPt(ext.getCx()); + return String.valueOf(widthPt); + } + + // 大小 - 锁定纵横比 + public static String getShapeSizeLockAspectRatio(Pic sp, CTSlideTiming timing) { + CTTransform2D xfrm = sp.getSpPr().getXfrm(); + // 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU) + CTPositiveSize2D ext = xfrm.getExt(); // 尺寸 + Boolean lockAspectRatio = null; + if (sp.getSpPr().getPrstGeom() != null && + sp.getSpPr().getPrstGeom().getAvLst() != null) { + // 这个部分一般不会直接提供锁定横纵比,要看 spPr.getExtLst() + if (sp.getSpPr().getExtLst() != null) { + // pptx4j 没有直接的 getter,要自己解析 XML + String xml = sp.getSpPr().getExtLst().toString(); + if (xml.contains("lockAspectRatio=\"1\"") || xml.contains("lockAspectRatio=\"true\"")) { + lockAspectRatio = true; + } else if (xml.contains("lockAspectRatio=\"0\"") || xml.contains("lockAspectRatio=\"false\"")) { + lockAspectRatio = false; + } + } + } + return String.valueOf(lockAspectRatio); + } + + // EMU 转 pt + private static double emuToPt(long emu) { + return emu / 12700.0; + } + + /// 大小 旋转 + public static String getShapeSizeRotate(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + + // 位置 水平位置 + public static String getShapePositionLeft(Pic sp, CTSlideTiming timing) { + CTTransform2D xfrm = sp.getSpPr().getXfrm(); + if (xfrm != null) { + long x = xfrm.getOff().getX(); // 水平位置(EMU) + return String.valueOf(x / 9525.0); + } + return ""; + } + + // 位置 垂直位置 + public static String getShapePositionTop(Pic sp, CTSlideTiming timing) { + CTTransform2D xfrm = sp.getSpPr().getXfrm(); + if (xfrm != null) { + long y = xfrm.getOff().getY(); // 垂直位置(EMU) + return String.valueOf(y / 9525.0); + } + return ""; + } + + // 形状填充 填充方式 + public static String getShapeFillType(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + return SlideUtils.getFillType(spPr); + } + + // 形状填充 纯色填充-颜色 + public static String getShapeFillSolidColor(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getSolidFill() != null) { + return SlideUtils.getSolidFillColor(spPr.getSolidFill()); + } + return ""; + } + + // 形状填充 渐变填充-效果 + public static String getShapeFillGradientType(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getGradFill() != null) { + System.out.println("形状: " + sp.getNvPicPr().getCNvPr().getName()); + return SlideUtils.printGradientFill(spPr.getGradFill()); + } + return ""; + } + + // 形状填充 图片填充-效果 + public static String getShapeFillPictureType(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getBlipFill() != null) { + System.out.println("形状: " + sp.getNvPicPr().getCNvPr().getName()); + return SlideUtils.printImageFill(spPr.getBlipFill()); + } + return ""; + } + + // 形状填充 纹理填充-效果 + public static String getShapeFillTextureType(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getPattFill() != null) { + return SlideUtils.printPatternFill(spPr.getPattFill()); + } + return ""; + } + + // 形状填充 图案填充-效果 + public static String getShapeFillPatternType(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getPattFill() != null) { + return spPr.getPattFill().getPrst().value(); + } + return ""; + } + + // 形状填充 幻灯片背景填充-效果 + public static String getShapeFillSlideBackgroundType(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状填充 填充透明度 + public static String getShapeFillOpacity(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状线条 绘制 + public static String getShapeLineDraw(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getLn() != null) { + CTLineProperties ln = spPr.getLn(); + + // 宽度(EMU -> pt) + double widthPt = ln.getW() / 12700.0; + System.out.println("形状: " + sp.getNvPicPr().getCNvPr().getName()); + System.out.println("线宽: " + widthPt + " pt"); + + // 颜色 + if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) { + CTSRgbColor rgb = ln.getSolidFill().getSrgbClr(); + System.out.println("线条颜色: #" + rgb.getVal()); + } + + // 虚线样式 + if (ln.getPrstDash() != null) { + System.out.println("线型: " + ln.getPrstDash().getVal().value()); + } + + // 端点样式 + if (ln.getCap() != null) { + System.out.println("端点: " + ln.getCap().value()); + } + return "是"; + } + return ""; + } + + // 形状线条 线条颜色 + public static String getShapeLineColor(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getLn() != null) { + CTLineProperties ln = spPr.getLn(); + // 颜色 + if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) { + CTSRgbColor rgb = ln.getSolidFill().getSrgbClr(); + return rgb.getVal(); + } + } + return ""; + } + + // 形状线条 线条宽度 + public static String getShapeLineWidth(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getLn() != null) { + CTLineProperties ln = spPr.getLn(); + // 宽度(EMU -> pt) + double widthPt = ln.getW() / 12700.0; + return String.valueOf(widthPt); + } + return ""; + } + + // 形状线条 透明度 + public static String getShapeLineOpacity(Pic sp, CTSlideTiming timing) { + + return ""; + } + + // 形状线条 复合类型 + public static String getStrokeCompound(Pic sp, CTSlideTiming timing) { + if (sp == null || sp.getSpPr() == null) return "未定义"; + CTShapeProperties spPr = sp.getSpPr(); + CTLineProperties ln = spPr.getLn(); + if (ln == null) return "未定义"; + + STCompoundLine cmpd = ln.getCmpd(); // 可能为 null + String v = (cmpd != null) ? cmpd.value() : "sng"; // 默认单线 + switch (v) { + case "dbl": + return "双线 (dbl)"; + case "thickThin": + return "粗-细双线 (thickThin)"; + case "thinThick": + return "细-粗双线 (thinThick)"; + case "tri": + return "三重线 (tri)"; + case "sng": + default: + return "单线 (sng)"; + } + } + + // 形状线条 短划线类型 + public static String getStrokeDash(Pic sp, CTSlideTiming timing) { + if (sp == null || sp.getSpPr() == null) return "solid"; + CTShapeProperties spPr = sp.getSpPr(); + CTLineProperties ln = spPr.getLn(); + if (ln == null) return "solid"; + + // 1) 预设短划线 + if (ln.getPrstDash() != null && ln.getPrstDash().getVal() != null) { + STPresetLineDashVal val = ln.getPrstDash().getVal(); + return SlideUtils.mapPreset(val); + } + + // 2) 自定义短划线 + if (ln.getCustDash() != null && ln.getCustDash().getDs() != null && !ln.getCustDash().getDs().isEmpty()) { + StringBuilder sb = new StringBuilder("custom "); + for (CTDashStop ds : ln.getCustDash().getDs()) { + int d = ds.getD(); // dash length(相对线宽的比例单位) + int sps = ds.getSp(); // space length(相对线宽的比例单位) + sb.append("[d=").append(d).append(", sps=").append(sps).append("] "); + } + return sb.toString().trim(); + } + + // 3) 未设置 → 视为实线 + return "solid"; + } + // 形状线条 开始箭头类型 + public static String getShapeLineStartArrow(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getLn() != null) { + CTLineProperties ln = spPr.getLn(); + if (ln.getHeadEnd() != null) { + return ln.getHeadEnd().getType().value(); + } + } + return "无箭头"; + } + // 形状线条 开始箭头粗细 + public static String getShapeLineStartArrowWidth(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状线条 结尾箭头类型 + public static String getShapeLineEndArrow(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状线条 结尾箭头粗细 + public static String getShapeLineEndArrowWidth(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + + + // 形状效果 阴影-绘制 + public static String getShapeShadowDraw(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + CTEffectList effList = spPr.getEffectLst(); + if (effList == null) { + return "否"; + } + // 判断三种阴影效果 + return effList.getOuterShdw() != null + || effList.getInnerShdw() != null + || effList.getPrstShdw() != null ? "是" : "否"; + } + + // 形状效果 阴影-效果 + public static String getShapeShadowEffect(Pic sp, CTSlideTiming timing) { + if (sp == null || sp.getSpPr() == null) { + return "无阴影"; + } + CTShapeProperties spPr = sp.getSpPr(); + CTEffectList effList = spPr.getEffectLst(); + if (effList == null) { + return "无阴影"; + } + + if (effList.getOuterShdw() != null) { + return "外阴影"; + } else if (effList.getInnerShdw() != null) { + return "内阴影"; + } else if (effList.getPrstShdw() != null) { + return "预设阴影"; + } + return "无阴影"; + } + + // 形状效果 倒影-绘制 + public static String getShapeReflectionDraw(Pic sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + CTEffectList effList = spPr.getEffectLst(); + if (effList == null) { + return "无倒影"; + } + + CTReflectionEffect reflection = effList.getReflection(); + if (reflection != null) { + // 如果需要详细参数,这里可以读取 + return "有倒影"; + } + return "无倒影"; + } + + // 形状效果 倒影-效果 + public static String getShapeReflectionEffect(Pic sp, CTSlideTiming timing) { + CTReflectionEffect r = SlideUtils.getReflectionPic(sp); + if (r == null) return "无倒影"; + + // —— 1) 倒影覆盖程度:用 stPos/endPos(0..100000)估算 —— // + double st = SlideUtils.to100k(BigInteger.valueOf(r.getStPos()), 0); + double ed = SlideUtils.to100k(BigInteger.valueOf(r.getEndPos()), 100000); // 默认 100% + double coverage = Math.max(0, ed - st) / 100000.0; // 0.0 ~ 1.0 + + String coverageLabel; + if (coverage >= 0.85) { + coverageLabel = "全倒影"; + } else if (coverage >= 0.45) { + coverageLabel = "半倒影"; + } else { + coverageLabel = "短倒影"; + } + + // —— 2) 偏移描述:dist EMU -> pt;<1pt 视作“紧贴”,否则 "Xpt 偏移" —— // + double distPt = emuToPt(r.getDist()); // 可能为 0 + String offsetLabel = distPt < 1.0 ? "紧贴" : (SlideUtils.fmt1(distPt) + "pt 偏移"); + + return coverageLabel + " " + offsetLabel; + } + + // 形状效果 发光 + public static String getShapeGlow(Pic sp, CTSlideTiming timing) { + if (sp == null || sp.getSpPr() == null) return null; + CTShapeProperties spPr = sp.getSpPr(); + CTEffectList eff = spPr.getEffectLst(); + return (eff != null) ? eff.getGlow().toString() : null; + } + + // 形状效果 发光-效果 + public static String getShapeGlowEffect(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状效果 柔化边缘-绘制 + public static String getShapeSoftEdgesDraw(Pic sp, CTSlideTiming timing) { + CTSoftEdgesEffect se = getSoftEdge(sp); + return (se != null) ? "是" : "否"; + } + + // 形状效果 柔化边缘→大小 + public static String getShapeSoftEdgesSize(Pic sp, CTSlideTiming timing) { + CTSoftEdgesEffect se = getSoftEdge(sp); + return (se != null) ? String.valueOf(se.getRad()) : null; + } + // 形状效果 三维效果→绘制 + public static String getShape3DEffectDraw(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状效果 三维格式→效果 + public static String getShape3DEffect(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状效果 三维旋转→效果 + public static String getShape3DRotation(Pic sp, CTSlideTiming timing) { + return "待开发"; + } + + /* —— 内部:spPr/effectLst/softEdge —— */ + private static CTSoftEdgesEffect getSoftEdge(Pic shape) { + if (shape == null || shape.getSpPr() == null) return null; + CTShapeProperties spPr = shape.getSpPr(); + CTEffectList eff = spPr.getEffectLst(); + return (eff != null) ? eff.getSoftEdge() : null; + // 说明:极少数文件把效果写在 effectDag;若拿不到再补 effectDag 解析。 + } + + + // 动画 效果 +// public static String getAnimation(org.pptx4j.pml.Shape sp, CTSlideTiming timing) throws JAXBException, XPathBinderAssociationIsPartialException { +// // 获取形状中的ID +// if (sp == null) return "无动画"; +// long spId = sp.getNvSpPr().getCNvPr().getId(); +// // 获取动画效果 +// CTTimeNodeList timeNodeList = timing.getTnLst(); +// for (Object obj : timeNodeList.getParOrSeqOrExcl()) { +// if (obj instanceof CTTLTimeNodeParallel) { +// CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; +// CTTLCommonTimeNodeData node = par.getCTn(); +// if (node.getChildTnLst().getParOrSeqOrExcl() != null) { +// CTTimeNodeList childTimeNodeList = node.getChildTnLst(); +// for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { +// if (object instanceof CTTLSetBehavior) { +// if (((CTTLSetBehavior) object).getCBhvr() != null) { +// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt() != null) { +// // 检查目标形状ID是否匹配 +// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) { +// // 返回动画效果 +// +// } +// } +// } +// } +// if (object instanceof CTTLTimeNodeSequence) { +// CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; +// CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); +// for (Object seqObjs : seqNodeList.getParOrSeqOrExcl()) { +// if (seqObjs instanceof CTTLSetBehavior) { +// if (((CTTLSetBehavior) seqObjs).getCBhvr() != null) { +// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt() != null) { +// // 检查目标形状ID是否匹配 +// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) { +// // 返回动画效果 +// +// } +// } +// } +// } +// if (seqObjs instanceof CTTLTimeNodeSequence) { +// CTTLTimeNodeSequence seqObj = (CTTLTimeNodeSequence) seqObjs; +// CTTimeNodeList seqNodeLists = seqObj.getCTn().getChildTnLst(); +// for (Object seqObjss : seqNodeLists.getParOrSeqOrExcl()) { +// if (seqObjss instanceof CTTLSetBehavior) { +// if (((CTTLSetBehavior) seqObjss).getCBhvr() != null) { +// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt() != null) { +// // 检查目标形状ID是否匹配 +// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) { +// // 返回动画效果 +// +// } +// } +// } +// } +// } +// } +// } +// } +// } +// } +// +// +// if (node.getDur() != null && !"indefinite".equals(node.getDur())) { +// return node.getDur(); +// } +// System.out.println(par.getCTn()); +// } +// } +// return "待开发"; +// } + + public static String getAnimation(org.pptx4j.pml.Pic sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + final String spId = String.valueOf(sp.getNvPicPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找进入类(effect) + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 再找其它类型(包括 CTTLAnimateBehavior 的 ppt_x/ppt_y) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + /* 递归遍历所有 time nodes,返回首个命中的行为字符串 */ + private static String findFirstBehavior(CTTimeNodeList list, String spId, boolean preferEntrance) { + if (list == null || list.getParOrSeqOrExcl() == null) return null; + + for (Object raw : list.getParOrSeqOrExcl()) { + Object node = unwrap(raw); + + // 1) 直接是某种行为(Behavior) + String desc = tryDescribeIfBehavior(node, spId, preferEntrance); + if (desc != null) return desc; + + // 2) par / seq / excl,进入子列表递归 + CTTimeNodeList child = getChildList(node); + if (child != null) { + String got = findFirstBehavior(child, spId, preferEntrance); + if (got != null) return got; + } + } + return null; + } + + /* 如果对象是某种 Behavior,且目标命中当前 spId,则返回描述;否则返回 null */ + private static String tryDescribeIfBehavior(Object obj, String spId, boolean preferEntrance) { + // 统一 unwrap + obj = unwrap(obj); + + // --- 进入类效果(Effect)--- + if (obj instanceof CTTLAnimateEffectBehavior) { + CTTLAnimateEffectBehavior b = (CTTLAnimateEffectBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String filter = safe(b.getFilter()); // 如 "fade"、"strips(downRight)" 等 + return filter.isEmpty() ? "进入效果" : ("进入效果:" + filter); + } + } + + // 非优先阶段:其它类型 + if (!preferEntrance) { + // 通用 Animate (B 动画关键:ppt_x / ppt_y) + if (obj instanceof CTTLAnimateBehavior) { + CTTLAnimateBehavior b = (CTTLAnimateBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String attr = firstAttrName(b.getCBhvr()); + if ("ppt_x".equalsIgnoreCase(attr)) return "位置动画:X(ppt_x)"; + if ("ppt_y".equalsIgnoreCase(attr)) return "位置动画:Y(ppt_y)"; + return attr.isEmpty() ? "数值动画(Animate)" : ("数值动画(" + attr + ")"); + } + } + if (obj instanceof CTTLAnimateMotionBehavior) { + CTTLAnimateMotionBehavior b = (CTTLAnimateMotionBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "路径动画(Motion)"; + } + if (obj instanceof CTTLAnimateRotationBehavior) { + CTTLAnimateRotationBehavior b = (CTTLAnimateRotationBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "旋转动画"; + } + if (obj instanceof CTTLAnimateScaleBehavior) { + CTTLAnimateScaleBehavior b = (CTTLAnimateScaleBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "缩放动画"; + } + if (obj instanceof CTTLSetBehavior) { + CTTLSetBehavior b = (CTTLSetBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String to = (b.getTo()!=null && b.getTo().getStrVal()!=null) ? safe(b.getTo().getStrVal().getVal()) : ""; + String attr = firstAttrName(b.getCBhvr()); + if (!attr.isEmpty() || !to.isEmpty()) { + return "属性设置(Set):" + (attr.isEmpty() ? "" : attr) + (to.isEmpty() ? "" : (" -> " + to)); + } + return "属性设置(Set)"; + } + } + if (obj instanceof CTTLAnimateColorBehavior) { + CTTLAnimateColorBehavior b = (CTTLAnimateColorBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "颜色动画"; + } + if (obj instanceof CTTLCommandBehavior) { + CTTLCommandBehavior b = (CTTLCommandBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "命令动画(Command)"; + } + } + return null; + } + + /* 提取并返回子节点列表(par/seq/excl 三种) */ + private static CTTimeNodeList getChildList(Object node) { + node = unwrap(node); + if (node instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel n = (CTTLTimeNodeParallel) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + if (node instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence n = (CTTLTimeNodeSequence) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + if (node instanceof CTTLTimeNodeExclusive) { + CTTLTimeNodeExclusive n = (CTTLTimeNodeExclusive) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + return null; + } + + /* 判断行为的目标是否命中当前形状 spId */ + private static boolean isTarget(CTTLCommonBehaviorData cb, String spId) { + if (cb == null || cb.getTgtEl() == null || cb.getTgtEl().getSpTgt() == null) return false; + String tgt = cb.getTgtEl().getSpTgt().getSpid(); + return String.valueOf(spId).equals(tgt); + } + + /* 读第一个 attrName(如 ppt_x / ppt_y / style.visibility ...)*/ + private static String firstAttrName(CTTLCommonBehaviorData cb) { + if (cb == null || cb.getAttrNameLst() == null) return ""; + java.util.List names = cb.getAttrNameLst().getAttrName(); + return (names != null && !names.isEmpty()) ? safe(names.get(0)) : ""; + } + + /* 统一解包 JAXBElement */ + private static Object unwrap(Object o) { + return (o instanceof JAXBElement) ? ((JAXBElement) o).getValue() : o; + } + + /* 安全字符串 */ + private static String safe(String s) { + return s == null ? "" : s.trim(); + } + + + // 动画-方向 + public static String getAnimateDirection(Pic sp, CTSlideTiming timing) { + String dir = getAnimation(sp, timing); + if (dir.equals("downRight")) { + return "右下"; + } + if (dir.equals("downLeft")) { + return "左下"; + } + if (dir.equals("upRight")) { + return "右上"; + } + if (dir.equals("upLeft")) { + return "左上"; + } + if (dir.equals("right")) { + return "向右"; + } + if (dir.equals("left")) { + return "向左"; + } + if (dir.equals("down")) { + return "向下"; + } + if (dir.equals("up")) { + return "向上"; + } + if (dir.equals("fromTop")) { + return "从顶部"; + } + if (dir.equals("fromBottom")) { + return "从底部"; + } + if (dir.equals("fromLeft")) { + return "从左侧"; + } + if (dir.equals("fromRight")) { + return "从右侧"; + } + return ""; + } + + // 动画 触发方式 + public static String getAnimateTrigger(Pic sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvPicPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return ""; + } + // 动画 持续时间 + public static String getAnimationDuration(Pic sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + + final String spId = String.valueOf(sp.getNvPicPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找“进入类效果(effect)” + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 找不到进入类,再找其它类型(运动、旋转、缩放、Set 等) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + // 动画 延迟 + public static String getAnimationDelay(Pic sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvPicPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return ""; + } + + /// 形状动作,鼠标单机-动作 + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideAllSetting.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideAllSetting.java new file mode 100644 index 00000000..ecf34f9e --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideAllSetting.java @@ -0,0 +1,18 @@ +package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; + +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.parts.PresentationML.SlidePart; +import org.pptx4j.pml.CTSlideTiming; + +public class SlideAllSetting { + // 设计 主题模板 + public static String getAllSlideTheme( SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + // 获取第一个幻灯片母版(通常主主题在这里) + return "WPS"; + } + + // 幻灯片切换 切换声音 + + +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideConversion.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideConversion.java index 1ba31d79..b383638c 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideConversion.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideConversion.java @@ -22,7 +22,7 @@ public class SlideConversion { XMLSlideShow pptxXml = new XMLSlideShow(inputStream)) { int index = 0; String firstId = getStringRandom(); - setSlideDataInfo(firstId, "", "幻灯片", "w:p", "", false, dataInfoVOS); + setSlideDataInfo(firstId, "", "幻灯片", "slide", "", false, dataInfoVOS); for (XSLFSlide slides : pptxXml.getSlides()){ index += 1; String secondId = getStringRandom(); @@ -30,7 +30,7 @@ public class SlideConversion { // 第三层 String thirdId = getStringRandom(); int indexCnvPr = 1; - setSlideDataInfo(thirdId, secondId, "幻灯片设置", "slide", String.valueOf(index), true, dataInfoVOS); + setSlideDataInfo(thirdId, secondId, "幻灯片设置", "slideSetting", String.valueOf(index) + "_0", true, dataInfoVOS); XmlCursor spCursor = slides.getXmlObject().newCursor(); spCursor.selectPath("declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//p:sp"); while (spCursor.toNextSelection()) { @@ -39,7 +39,7 @@ public class SlideConversion { if (cNvPrXml.toNextSelection()) { String name = cNvPrXml.getTextValue(); String fourId = getStringRandom(); - setSlideDataInfo(fourId, secondId, "形状"+indexCnvPr+"->"+name, "shape", String.valueOf(indexCnvPr), true, dataInfoVOS); + setSlideDataInfo(fourId, secondId, "形状"+indexCnvPr+"->"+name, "shape", String.valueOf(index) + "_" + String.valueOf(indexCnvPr), true, dataInfoVOS); indexCnvPr += 1; } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMaster.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMaster.java index d74c4fa7..8fa61c59 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMaster.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMaster.java @@ -3,20 +3,15 @@ package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.OpcPackage; import org.docx4j.openpackaging.packages.PresentationMLPackage; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.PresentationML.SlidePart; -import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart; -import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart; -import org.docx4j.wml.P; import org.pptx4j.pml.CTSlideTiming; import org.pptx4j.pml.GroupShape; +import org.pptx4j.pml.Pic; import org.pptx4j.pml.Shape; import org.springframework.web.multipart.MultipartFile; import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsSlideInfoVo; import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.vo.JudgementSlidesVO; -import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Paragraphs; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; @@ -26,21 +21,19 @@ import java.util.List; public class SlideMaster { public static List slideMaster(List wpsSlideInfoVos, MultipartFile file) throws IOException, Docx4JException { - File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx"); +// File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx"); List judgementSlidesVOS = new ArrayList<>(); // 1、获取想要判断的文件地址(文件流) -// try (InputStream inputStream = file.getInputStream()) { - try { + try (InputStream inputStream = file.getInputStream()) { +// try { // 加载 .pptx 文件 PresentationMLPackage ppt = - (PresentationMLPackage) OpcPackage.load(files); + (PresentationMLPackage) OpcPackage.load(inputStream); // 你可以在这里处理 ppt,比如读取文字、页数等 for (WpsSlideInfoVo wpsSlideInfoVo : wpsSlideInfoVos) { // 幻灯片位置(第几张幻灯片) - String slideIndex = wpsSlideInfoVo.getSlideIndex(); - String shapeIndex = wpsSlideInfoVo.getShapeIndex(); - // 形状类型 slide 幻灯片设置 shape 形状(不包含图片) shapePic 形状图片 presentation 总幻灯片设置 - String type = wpsSlideInfoVo.getType(); + String slideIndex = wpsSlideInfoVo.getSlideIndex().split("_")[0]; + String shapeIndex = wpsSlideInfoVo.getSlideIndex().split("_")[1]; // 大类名称 String firstName = wpsSlideInfoVo.getFirstName(); // 方法名称 @@ -51,11 +44,13 @@ public class SlideMaster { String examCode = wpsSlideInfoVo.getExamCode(); // 方式方法 String method = wpsSlideInfoVo.getMethod(); - if (type.equals("shape")) { - // 查询幻灯片图片 - List slideParts = ppt.getMainPresentationPart().getSlideParts(); - int slideIndexFoFile = 0; - for (SlidePart slidePart : slideParts) { + + String content = wpsSlideInfoVo.getSlideIndex() + "@" + firstName + "@" + function + "@" + examName + "@" + examCode + "@" + method; + // 查询幻灯片图片 + List slideParts = ppt.getMainPresentationPart().getSlideParts(); + int slideIndexFoFile = 0; + for (SlidePart slidePart : slideParts) { + if (method.equals("shape")) { // 获取幻灯片内容 slideIndexFoFile++; if (slideIndex.equals(String.valueOf(slideIndexFoFile))) { @@ -63,14 +58,12 @@ public class SlideMaster { // 遍历 shape tree 中的 sp(shape)元素 GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); CTSlideTiming timing = slidePart.getJaxbElement().getTiming(); - List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); int shapeIndexFoFile = 0; - for (Object shapeObj : shapes) { + for (Object shapeObj : shapes) { shapeIndexFoFile++; - if (shapeObj instanceof Shape) { - Shape shape = (Shape) shapeObj; + if (shapeObj instanceof Shape shape) { if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) { - // 查询出具体想要查询哪个段落的数据 // 目标对象 ShapePage shapeFunction = new ShapePage(); // 获取参数中类型的定义 @@ -80,13 +73,48 @@ public class SlideMaster { // 实际参数值 Object[] arguments = {shape, timing}; String value = (String) methodWithArgs.invoke(shapeFunction, arguments); - System.out.println(value); + JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); + judgementSlidesVO.setContent(content + "@" + value); + judgementSlidesVO.setContentIn(firstName + examName + value); + judgementSlidesVO.setScoreRate(1); + judgementSlidesVOS.add(judgementSlidesVO); + } + } else if (shapeObj instanceof Pic pic) { + if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) { + // 目标对象 + ShapePic shapePicFunction = new ShapePic(); + // 获取参数中类型的定义 + Class[] paramTypes = {Pic.class, CTSlideTiming.class}; + // 带参数的方法调用示例 + Method methodWithArgs = shapePicFunction.getClass().getMethod(function, paramTypes); + // 实际参数值 + Object[] arguments = {pic, timing}; + String value = (String) methodWithArgs.invoke(shapePicFunction, arguments); + JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); + judgementSlidesVO.setContent(content + "@" + value); + judgementSlidesVO.setContentIn(firstName + examName + value); + judgementSlidesVO.setScoreRate(1); + judgementSlidesVOS.add(judgementSlidesVO); } } } + } } + if (method.equals("slideSetting")) { + SlideSetting slideSettingFunction = new SlideSetting(); + Class[] paramTypes = {SlidePart.class, PresentationMLPackage.class}; + Method methodWithArgs = slideSettingFunction.getClass().getMethod(function, paramTypes); + Object[] arguments = {slidePart, ppt}; + String value = (String) methodWithArgs.invoke(slideSettingFunction, arguments); + JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); + judgementSlidesVO.setContent(content + "@" + value); + judgementSlidesVO.setContentIn(firstName + examName + value); + judgementSlidesVO.setScoreRate(1); + judgementSlidesVOS.add(judgementSlidesVO); + } } + } } 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_pptx/pptx4j/SlideSetting.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideSetting.java new file mode 100644 index 00000000..ece02bcd --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideSetting.java @@ -0,0 +1,162 @@ +package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; + +import org.docx4j.dml.CTPositiveSize2D; +import org.docx4j.dml.CTTransform2D; +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.parts.PresentationML.SlideLayoutPart; +import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart; +import org.docx4j.openpackaging.parts.PresentationML.SlidePart; +import org.pptx4j.pml.CTBackground; +import org.pptx4j.pml.CTBackgroundProperties; +import org.pptx4j.pml.CTSlideTiming; +import org.pptx4j.pml.*; +import org.docx4j.dml.*; + +import java.lang.reflect.Method; +import java.util.List; + +// 单个幻灯片设置 +public class SlideSetting { + // 幻灯片 版式 + public static String getSlideLayout(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + SlideLayoutPart layoutPart = slidePart.getSlideLayoutPart(); + if (layoutPart.getContents().getCSld() != null) { + return layoutPart.getContents().getCSld().getName(); + } + return ""; + } + // 设计 主题模板 + public static String getSlideTheme(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + // 获取第一个幻灯片母版(通常主主题在这里) + return "WPS"; + } + + // 背景填充 填充方式 + public static String getSlideBackground(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + CTBackground ctBackground = slidePart.getContents().getCSld().getBg(); + if (ctBackground != null) { + CTBackgroundProperties ctBackgroundProperties = ctBackground.getBgPr(); + return getBackgroundFillType(ctBackgroundProperties); + } + return ""; + } + private static String getBackgroundFillType(CTBackgroundProperties bgPr) { + if (bgPr.getSolidFill() != null) return "纯色填充"; + if (bgPr.getGradFill() != null) return "渐变填充"; + if (bgPr.getBlipFill() != null) return "图片填充"; + if (bgPr.getPattFill() != null) return "图案填充"; + if (bgPr.getGrpFill() != null) return "组合填充"; + if (bgPr.getNoFill() != null) return "无填充"; + return "未知填充类型"; + } + // 背景填充 纯色填充-颜色 + public static String getSlideBackgroundSolidColor(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + CTBackground ctBackground = slidePart.getContents().getCSld().getBg(); + if (ctBackground != null) { + CTBackgroundProperties ctBackgroundProperties = ctBackground.getBgPr(); + return getColorValue(ctBackgroundProperties); + } + return ""; + } + private static String getColorValue(CTBackgroundProperties solidFill) { + if (solidFill.getSolidFill().getSrgbClr() != null) { + return "RGB颜色: " + solidFill.getSolidFill().getSrgbClr().getVal(); + } else if (solidFill.getSolidFill().getSchemeClr() != null) { + return "主题颜色: " + solidFill.getSolidFill().getSchemeClr().getVal(); + } else if (solidFill.getSolidFill().getHslClr() != null) { + return "HSL颜色"; + } else if (solidFill.getSolidFill().getSysClr() != null) { + return "系统颜色"; + } else if (solidFill.getSolidFill().getPrstClr() != null) { + return "预设颜色: " + solidFill.getSolidFill().getPrstClr().getVal(); + } + return "未知颜色类型"; + } + + // 背景填充 隐藏背景图形 + public static String getSlideBackgroundHideShape(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + return "待开发"; + } + + // 幻灯片切换 切换效果 + public static String getSlideTransition(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + Sld slides = (Sld) slidePart.getJaxbElement(); + Sld slide = (Sld) slidePart.getContents(); + CTSlideTransition ctSlideTransition = slide.getTransition(); + return ""; + } + // 幻灯片切换 切换声音 + public static String getSlideTransitionSound(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + Sld slide = (Sld) slidePart.getContents(); + CTSlideTransition ctSlideTransition = slide.getTransition(); + CTTransitionSoundAction ctTransitionSoundAction = ctSlideTransition.getSndAc(); + if (ctTransitionSoundAction != null) { + return ctTransitionSoundAction.getStSnd().getSnd().getName(); + } + return ""; + } + + // 幻灯片切换 切换速度 + public static String getSlideTransitionSpeed(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + Sld slide = (Sld) slidePart.getContents(); + CTSlideTransition ctSlideTransition = slide.getTransition(); + Long speed = ctSlideTransition.getAdvTm(); + if (speed != null) { + return String.valueOf(speed) + "毫秒"; + } + return ""; + } + + // 隐藏幻灯片 + public static String getSlideHide(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + Sld sld = slidePart.getJaxbElement(); + if (sld != null) { + return sld.isShow() ? "显示幻灯片" : "隐藏幻灯片"; + } + return ""; + } + + // 页眉页脚 包含→日期和时间 + public static String getSlideHeaderFooterDateTime(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + for (Object shapeObj : shapes) { + if (shapeObj instanceof Shape) { + Shape shape = (Shape) shapeObj; + if (shape.getNvSpPr().getCNvPr().getName().equals("日期")) { + return "包含"; + } + } + } + return ""; + } + // 页眉页脚 包含→幻灯片编号 + public static String getSlideHeaderFooterSlideNumber(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + for (Object shapeObj : shapes) { + if (shapeObj instanceof Shape) { + Shape shape = (Shape) shapeObj; + if (shape.getNvSpPr().getCNvPr().getName().equals("编号")) { + return "包含"; + } + } + } + return ""; + } + // 页眉页脚 包含→页脚 + public static String getSlideHeaderFooterFooter(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + for (Object shapeObj : shapes) { + if (shapeObj instanceof Shape) { + Shape shape = (Shape) shapeObj; + if (shape.getNvSpPr().getCNvPr().getName().equals("页脚")) { + return "包含"; + } + } + } + return ""; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideUtils.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideUtils.java index 342b8172..6713150a 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideUtils.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideUtils.java @@ -98,23 +98,34 @@ public class SlideUtils { // 常见值:solid, dash, dot, dashDot, lgDash, lgDashDot, lgDashDotDot, // sysDash, sysDot, sysDashDot, sysDashDotDot switch (v) { - case SOLID: return "solid"; - case DASH: return "dash"; - case DOT: return "dot"; - case DASH_DOT: return "dashDot"; - case LG_DASH: return "lgDash"; - case LG_DASH_DOT: return "lgDashDot"; - case LG_DASH_DOT_DOT:return "lgDashDotDot"; - case SYS_DASH: return "sysDash"; - case SYS_DOT: return "sysDot"; - case SYS_DASH_DOT: return "sysDashDot"; - case SYS_DASH_DOT_DOT:return "sysDashDotDot"; - default: return v.value(); // 兜底:返回原枚举字符串 + case SOLID: + return "solid"; + case DASH: + return "dash"; + case DOT: + return "dot"; + case DASH_DOT: + return "dashDot"; + case LG_DASH: + return "lgDash"; + case LG_DASH_DOT: + return "lgDashDot"; + case LG_DASH_DOT_DOT: + return "lgDashDotDot"; + case SYS_DASH: + return "sysDash"; + case SYS_DOT: + return "sysDot"; + case SYS_DASH_DOT: + return "sysDashDot"; + case SYS_DASH_DOT_DOT: + return "sysDashDotDot"; + default: + return v.value(); // 兜底:返回原枚举字符串 } } - /* ——— 内部:拿到 spPr/effectLst/reflection ——— */ public static CTReflectionEffect getReflection(org.pptx4j.pml.Shape shape) { if (shape == null || shape.getSpPr() == null) return null; @@ -124,14 +135,24 @@ public class SlideUtils { // 如遇到效果写在 effectDag,可再补充从 spPr.getEffectDag() 解析。 } + public static CTReflectionEffect getReflectionPic(org.pptx4j.pml.Pic shape) { + if (shape == null || shape.getSpPr() == null) return null; + CTShapeProperties spPr = shape.getSpPr(); + CTEffectList eff = spPr.getEffectLst(); + return (eff != null) ? eff.getReflection() : null; + // 如遇到效果写在 effectDag,可再补充从 spPr.getEffectDag() 解析。 + } + /* ——— 工具 ——— */ public static double emuToPt(BigInteger emu) { if (emu == null) return 0.0; return emu.doubleValue() / 12700.0; // 1 pt = 12700 EMU } + public static double to100k(BigInteger v, int def) { return v == null ? def : v.doubleValue(); } + public static String fmt1(double d) { return new DecimalFormat("#.##").format(d); // 保留 1~2 位,去掉多余 0 } @@ -141,6 +162,7 @@ public class SlideUtils { if (val == null || val.length != 3) return "000000"; return String.format("%02X%02X%02X", val[0] & 0xFF, val[1] & 0xFF, val[2] & 0xFF); } + public static String extractAlpha(java.util.List alphas) { if (alphas != null && !alphas.isEmpty()) { double pct = alphas.get(0).getVal() / 1000.0; @@ -148,10 +170,47 @@ public class SlideUtils { } return null; } + public static int iOr0(BigInteger bi) { return bi == null ? 0 : bi.intValue(); } + public static String fmt(double d) { return (Math.round(d * 100.0) / 100.0) + ""; // 保留 2 位小数 } + + public static String getShadowLikeDescription(CTReflectionEffect reflection) { + if (reflection == null) return "无阴影效果"; + + // 1. 判断是否为底部反射(类似阴影) + boolean isBottomReflection = + (reflection.getAlgn().value().equals("b")) || + (reflection.getSy() < 0); + + if (!isBottomReflection) { + return "特殊反射效果"; + } + + // 2. 生成类似阴影的描述 + StringBuilder desc = new StringBuilder("阴影效果-"); + + // 模糊程度 + double blurPt = reflection.getBlurRad() / 12700.0; + if (blurPt > 5) desc.append("强模糊"); + else if (blurPt > 2) desc.append("模糊"); + else desc.append("清晰"); + + // 距离/强度 + double distance = reflection.getEndPos() / 1000.0; + if (distance > 40) desc.append("-大距离"); + else if (distance > 20) desc.append("-中距离"); + else desc.append("-小距离"); + + // 透明度 + int alpha = reflection.getStA(); + if (alpha < 30000) desc.append("-高透明"); + else if (alpha < 60000) desc.append("-半透明"); + + return desc.toString(); + } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/vo/JudgementSlidesVO.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/vo/JudgementSlidesVO.java index 6b1e9405..f4eb1e45 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/vo/JudgementSlidesVO.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/vo/JudgementSlidesVO.java @@ -14,4 +14,6 @@ public class JudgementSlidesVO { // 考点名称 private String contentIn; + private int scoreRate; + }