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 744f76d1..9953b005 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 @@ -586,7 +586,7 @@ public class ShapePage { List parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl(); String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "dur"); if (!value.isEmpty()) { - return Integer.parseInt(value) / 1000 + "秒"; + return (double) Integer.parseInt(value) / 1000 + "秒"; } return ""; } @@ -600,7 +600,7 @@ public class ShapePage { List parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl(); String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "delay"); if (!value.isEmpty()) { - return Integer.parseInt(value) / 1000 + "秒"; + return (double) Integer.parseInt(value) / 1000 + "秒"; } return ""; } @@ -772,7 +772,7 @@ public class ShapePage { // 遍历段落中的所有文本运行 Object obj = paragraph.getEGTextRun().get(0); if (obj instanceof CTRegularTextRun ctRegularTextRun) { - return String.valueOf(ctRegularTextRun.getRPr().getSz()); + return String.valueOf(ctRegularTextRun.getRPr().getSz() / 100); } } } @@ -1019,11 +1019,10 @@ public class ShapePage { // 遍历所有段落 for (CTTextParagraph paragraph : textBody.getP()) { if (paragraph.getPPr().getLnSpc().getSpcPct() != null) { - return "百分比行距"; // EMU 转 pt + return (double) paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt } else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) { return "固定行距"; // EMU 转 pt } - } } return ""; @@ -1036,9 +1035,9 @@ public class ShapePage { // 遍历所有段落 for (CTTextParagraph paragraph : textBody.getP()) { if (paragraph.getPPr().getLnSpc().getSpcPct() != null) { - return paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt + return (double) paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt } else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) { - return paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 10 + "磅固定行距"; // EMU 转 pt + return (double) paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 10 + "磅固定行距"; // EMU 转 pt } } @@ -1048,6 +1047,21 @@ public class ShapePage { /// 段落 项目符号和编号→类型 public static String getTextBoxParagraphSymbol(org.pptx4j.pml.Shape sp, CTSlideTiming timing) { + CTTextBody ctTextBody = sp.getTxBody(); + // 只需要获取一组的数据即可 + CTTextCharBullet ctTextCharBullet = ctTextBody.getP().get(0).getPPr().getBuChar(); + if (ctTextCharBullet != null) { + return "项目符号"; + } + CTTextBlipBullet ctTextBlipBullet = ctTextBody.getP().get(0).getPPr().getBuBlip(); + if (ctTextBlipBullet != null) { + return "项目符号"; + } + // 编号 + CTTextAutonumberBullet numberingLevel = ctTextBody.getP().get(0).getPPr().getBuAutoNum(); + if (numberingLevel != null) { + return "编号"; + } 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 index fdcced5c..cd9534f7 100644 --- 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 @@ -3,6 +3,7 @@ package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; import jakarta.xml.bind.JAXBElement; import org.docx4j.dml.*; import org.pptx4j.pml.*; +import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToCmConverter; import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.Transition; import java.math.BigInteger; @@ -16,7 +17,11 @@ public class ShapePic { // 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU) CTPositiveSize2D ext = xfrm.getExt(); // 尺寸 double heightPt = emuToPt(ext.getCy()); - return String.valueOf(heightPt); + // 转换成磅 + double pt = Math.round(heightPt * 100.0) / 100.0; + // 转换成厘米 + double cm = PtToCmConverter.pointsToCm(pt); + return pt + "磅(" + cm + "厘米)"; } // 大小 - 宽度 @@ -25,29 +30,20 @@ public class ShapePic { // 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU) CTPositiveSize2D ext = xfrm.getExt(); // 尺寸 double widthPt = emuToPt(ext.getCx()); - return String.valueOf(widthPt); + // 转换成磅 + double pt = Math.round(widthPt * 100.0) / 100.0; + // 转换成厘米 + double cm = PtToCmConverter.pointsToCm(pt); + return pt + "磅(" + cm + "厘米)"; } // 大小 - 锁定纵横比 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; - } - } + CTPictureLocking ctPictureLocking = sp.getNvPicPr().getCNvPicPr().getPicLocks(); + if (ctPictureLocking != null) { + return ctPictureLocking.isNoChangeAspect() ? "是" : "否"; } - return String.valueOf(lockAspectRatio); + return "否"; } // EMU 转 pt @@ -562,7 +558,7 @@ public class ShapePic { List parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl(); String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "dur"); if (!value.isEmpty()) { - return Integer.parseInt(value) / 1000 + "秒"; + return (double) Integer.parseInt(value) / 1000 + "秒"; } return ""; } @@ -576,7 +572,7 @@ public class ShapePic { List parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl(); String value = getAniationInfo(parOrSeqOrExcl, null, String.valueOf(spId), "delay"); if (!value.isEmpty()) { - return Integer.parseInt(value) / 1000 + "秒"; + return (double) Integer.parseInt(value) / 1000 + "秒"; } 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 index ecf34f9e..00c20e79 100644 --- 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 @@ -1,18 +1,91 @@ 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; +import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart; +import org.pptx4j.pml.CTShowInfoKiosk; +import org.pptx4j.pml.Presentation; +import org.pptx4j.pml.PresentationPr; +import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PptxUtils; +import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToCmConverter; +import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToEmuConverter; public class SlideAllSetting { // 设计 主题模板 - public static String getAllSlideTheme( SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getAllSlideTheme(MainPresentationPart mpp, PresentationPr presentationPr) { // 获取第一个幻灯片母版(通常主主题在这里) return "WPS"; } - // 幻灯片切换 切换声音 + // 幻灯片放映 + // 幻灯片放映-放映类型 + public static String getAllSlideShowType(MainPresentationPart mpp, PresentationPr presentationPr) { + CTShowInfoKiosk ctShowInfoKiosk = presentationPr.getShowPr().getKiosk(); + if (ctShowInfoKiosk != null) { + return "在展台浏览(全屏)"; + } else { + return "演示者放映(全屏)"; + } + } + + // 幻灯片放映-循环播放,按ESC键终止 + public static String getAllSlideShowLoop(MainPresentationPart mpp, PresentationPr presentationPr) { + return presentationPr.getShowPr().isLoop() ? "是" : "否"; + } + + // 幻灯片放映-放映范围 + public static String getAllSlideShowRange(MainPresentationPart mpp, PresentationPr presentationPr) { + long st = presentationPr.getShowPr().getSldRg().getSt(); + long end = presentationPr.getShowPr().getSldRg().getEnd(); + return "第" + st + "页-第" + end + "页"; + } + + // 幻灯片大小 幻灯片大小 + public static String getAllSlideSize(MainPresentationPart mpp, PresentationPr presentationPr) { + // 拿到 JAXB 模型对象 + Presentation pres = (Presentation) mpp.getJaxbElement(); + int cx = pres.getSldSz().getCx(); + int cy = pres.getSldSz().getCy(); + return PptxUtils.detectAspectName(cx, cy); + } + + //幻灯片大小 宽度 + public static String getAllSlideSizeCx(MainPresentationPart mpp, PresentationPr presentationPr) { + // 拿到 JAXB 模型对象 + Presentation pres = (Presentation) mpp.getJaxbElement(); + int cx = pres.getSldSz().getCx(); + return PtToEmuConverter.emuToPt(cx)+ "磅("+ PtToCmConverter.pointsToCm(PtToEmuConverter.emuToPt(cx))+"厘米)"; + } + + //幻灯片大小 高度 + public static String getAllSlideSizeCy(MainPresentationPart mpp, PresentationPr presentationPr) { + // 拿到 JAXB 模型对象 + Presentation pres = (Presentation) mpp.getJaxbElement(); + int cy = pres.getSldSz().getCy(); + return PtToEmuConverter.emuToPt(cy)+ "磅("+ PtToCmConverter.pointsToCm(PtToEmuConverter.emuToPt(cy))+"厘米)"; + } + + // 幻灯片大小 幻灯片编号起始值 + public static String getAllSlideSizeStartNum(MainPresentationPart mpp, PresentationPr presentationPr) { + // 拿到 JAXB 模型对象 + Presentation pres = (Presentation) mpp.getJaxbElement(); + int firstNum = pres.getFirstSlideNum(); + return String.valueOf(firstNum); + } + + // 幻灯片大小 方向 + public static String getAllSlideSizeOrientation(MainPresentationPart mpp, PresentationPr presentationPr) { + Presentation pres = (Presentation) mpp.getJaxbElement(); + int cy = pres.getSldSz().getCy(); + int cx = pres.getSldSz().getCx(); + return cy > cx ? "纵向" : "横向"; + } + + // 幻灯片大小 备注方向 + public static String getAllSlideSizeNoteOrientation(MainPresentationPart mpp, PresentationPr presentationPr) { + Presentation pres = (Presentation) mpp.getJaxbElement(); + long cy = pres.getNotesSz().getCy(); + long cx = pres.getNotesSz().getCx(); + return cx > cy ? "横向" : "纵向"; + } } 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 7ec1d3f3..e25ab7b1 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 @@ -55,10 +55,14 @@ public class SlideConversion { if (shapeObj instanceof Pic) { setSlideDataInfo(fourId, secondId, "形状"+slideIndexFoShape+"->"+((Pic) shapeObj).getNvPicPr().getCNvPr().getName(), "shape", String.valueOf(slideIndexFoFile) + "_" + String.valueOf(slideIndexFoShape), true, "第"+slideIndexFoFile+"页", dataInfoVOS); } - - } } + // 幻灯片设置 + String firstSetId = getStringRandom(); + String secondSetId = getStringRandom(); + setSlideDataInfo(firstSetId, "", "设置", "slideAllSetting", "", false, "", dataInfoVOS); + setSlideDataInfo(secondSetId, firstSetId, "幻灯片设置", "slideAllSetting", "0_0", true, "设置", dataInfoVOS); + } 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/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 c0c2fbcf..58c274c5 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 @@ -1,13 +1,21 @@ package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; +import org.docx4j.XmlUtils; +import org.docx4j.dml.CTBlipFillProperties; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.OpcPackage; import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.parts.JaxbXmlPart; +import org.docx4j.openpackaging.parts.Part; +import org.docx4j.openpackaging.parts.Parts; +import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart; +import org.docx4j.openpackaging.parts.PresentationML.NotesMasterPart; +import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart; import org.docx4j.openpackaging.parts.PresentationML.SlidePart; -import org.pptx4j.pml.CTSlideTiming; -import org.pptx4j.pml.GroupShape; -import org.pptx4j.pml.Pic; -import org.pptx4j.pml.Shape; +import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; +import org.docx4j.relationships.Relationship; +import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; +import org.pptx4j.pml.*; 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; @@ -17,9 +25,11 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import java.util.Map; 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"); List judgementSlidesVOS = new ArrayList<>(); @@ -29,6 +39,8 @@ public class SlideMaster { // 加载 .pptx 文件 PresentationMLPackage ppt = (PresentationMLPackage) OpcPackage.load(inputStream); + // 获取母版 + SlideMasterPart slideMasterPart = SlideMasterUtils.getSlideMaster(ppt); // 你可以在这里处理 ppt,比如读取文字、页数等 for (WpsSlideInfoVo wpsSlideInfoVo : wpsSlideInfoVos) { // 幻灯片位置(第几张幻灯片) @@ -44,79 +56,96 @@ public class SlideMaster { String examCode = wpsSlideInfoVo.getExamCode(); // 方式方法 String method = wpsSlideInfoVo.getMethod(); - String content = wpsSlideInfoVo.getSlideIndex() + "@" + firstName + "@" + function + "@" + examName + "@" + examCode + "@" + method; - // 查询幻灯片图片 - List slideParts = ppt.getMainPresentationPart().getSlideParts(); - int slideIndexFoFile = 0; - for (SlidePart slidePart : slideParts) { - slideIndexFoFile++; - if (method.equals("shape")) { - // 获取幻灯片内容 - if (slideIndex.equals(String.valueOf(slideIndexFoFile))) { - // 查询幻灯片 - // 遍历 shape tree 中的 sp(shape)元素 - GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); - CTSlideTiming timing = slidePart.getJaxbElement().getTiming(); - List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); - int shapeIndexFoFile = 0; - for (Object shapeObj : shapes) { - shapeIndexFoFile++; - if (shapeObj instanceof Shape shape) { - if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) { - // 目标对象 - ShapePage shapeFunction = new ShapePage(); - // 获取参数中类型的定义 - Class[] paramTypes = {Shape.class, CTSlideTiming.class}; - // 带参数的方法调用示例 - Method methodWithArgs = shapeFunction.getClass().getMethod(function, paramTypes); - // 实际参数值 - Object[] arguments = {shape, timing}; - String value = (String) methodWithArgs.invoke(shapeFunction, arguments); - if (!value.isEmpty()) { - JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); - judgementSlidesVO.setContent(content + "@" + value); - judgementSlidesVO.setContentIn(firstName + examName + value); - judgementSlidesVO.setScoreRate(1); - judgementSlidesVOS.add(judgementSlidesVO); + // 获取整个ppt文件的设置 + if (method.equals("slideAllSetting")) { + MainPresentationPart mpp = ppt.getMainPresentationPart(); + PresentationPr presentationPr = SlideMasterUtils.getPresentationPr(ppt); + SlideAllSetting slideAllSettingFunction = new SlideAllSetting(); + Class[] paramTypes = {MainPresentationPart.class, PresentationPr.class}; + Method methodWithArgs = slideAllSettingFunction.getClass().getMethod(function, paramTypes); + Object[] arguments = {mpp, presentationPr}; + String value = (String) methodWithArgs.invoke(slideAllSettingFunction, arguments); + if (!value.isEmpty()) { + JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); + judgementSlidesVO.setContent(content + "@" + value); + judgementSlidesVO.setContentIn(firstName + examName + value); + judgementSlidesVO.setScoreRate(1); + judgementSlidesVOS.add(judgementSlidesVO); + } + } else { + // 查询幻灯片图片 + List slideParts = ppt.getMainPresentationPart().getSlideParts(); + int slideIndexFoFile = 0; + for (SlidePart slidePart : slideParts) { + slideIndexFoFile++; + if (method.equals("shape")) { + // 获取幻灯片内容 + if (slideIndex.equals(String.valueOf(slideIndexFoFile))) { + // 查询幻灯片 + // 遍历 shape tree 中的 sp(shape)元素 + GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); + CTSlideTiming timing = slidePart.getJaxbElement().getTiming(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + int shapeIndexFoFile = 0; + for (Object shapeObj : shapes) { + shapeIndexFoFile++; + if (shapeObj instanceof Shape shape) { + if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) { + // 目标对象 + ShapePage shapeFunction = new ShapePage(); + // 获取参数中类型的定义 + Class[] paramTypes = {Shape.class, CTSlideTiming.class}; + // 带参数的方法调用示例 + Method methodWithArgs = shapeFunction.getClass().getMethod(function, paramTypes); + // 实际参数值 + Object[] arguments = {shape, timing}; + String value = (String) methodWithArgs.invoke(shapeFunction, arguments); + if (!value.isEmpty()) { + 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); - if (!value.isEmpty()) { - 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); + if (!value.isEmpty()) { + JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); + judgementSlidesVO.setContent(content + "@" + value); + judgementSlidesVO.setContentIn(firstName + examName + value); + judgementSlidesVO.setScoreRate(1); + judgementSlidesVOS.add(judgementSlidesVO); + } } } } } } - } - if (method.equals("slideSetting")) { - if (slideIndex.equals(String.valueOf(slideIndexFoFile))) { - 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); - if (!value.isEmpty()) { - JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); - judgementSlidesVO.setContent(content + "@" + value); - judgementSlidesVO.setContentIn(firstName + examName + value); - judgementSlidesVO.setScoreRate(1); - judgementSlidesVOS.add(judgementSlidesVO); + if (method.equals("slideSetting")) { + if (slideIndex.equals(String.valueOf(slideIndexFoFile))) { + SlideSetting slideSettingFunction = new SlideSetting(); + Class[] paramTypes = {SlidePart.class, PresentationMLPackage.class, SlideMasterPart.class}; + Method methodWithArgs = slideSettingFunction.getClass().getMethod(function, paramTypes); + Object[] arguments = {slidePart, ppt, slideMasterPart}; + String value = (String) methodWithArgs.invoke(slideSettingFunction, arguments); + if (!value.isEmpty()) { + JudgementSlidesVO judgementSlidesVO = new JudgementSlidesVO(); + judgementSlidesVO.setContent(content + "@" + value); + judgementSlidesVO.setContentIn(firstName + examName + value); + judgementSlidesVO.setScoreRate(1); + judgementSlidesVOS.add(judgementSlidesVO); + } } } } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMasterUtils.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMasterUtils.java new file mode 100644 index 00000000..742a7fb1 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/SlideMasterUtils.java @@ -0,0 +1,122 @@ +package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j; + +import org.docx4j.XmlUtils; +import org.docx4j.dml.CTBlipFillProperties; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.parts.JaxbXmlPart; +import org.docx4j.openpackaging.parts.Part; +import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart; +import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart; +import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; +import org.docx4j.relationships.Relationship; +import org.pptx4j.jaxb.Context; +import org.pptx4j.pml.CTBackground; +import org.pptx4j.pml.Presentation; +import org.pptx4j.pml.PresentationPr; + +import java.util.Map; + +public class SlideMasterUtils { + + public static SlideMasterPart getSlideMaster(PresentationMLPackage ppt) { + // 2. 获取 Presentation 对象 + MainPresentationPart mpp = ppt.getMainPresentationPart(); + Presentation pres = (Presentation) mpp.getJaxbElement(); + RelationshipsPart rels = ppt.getMainPresentationPart().getRelationshipsPart(); + // 获取母版ID + Presentation.SldMasterIdLst idList = pres.getSldMasterIdLst(); + for (Presentation.SldMasterIdLst.SldMasterId smId : idList.getSldMasterId()) { + // 不同生成类版本,取 r:id 的方法名可能不同:getRid() 或 getId2() + String rId = null; + try { + rId = smId.getRid(); // 新一点的模型常见 + } catch (Throwable ignore) { + } + if (rId == null) { + try { + // 某些版本字段名为 id2(即 r:id) + rId = (String) Presentation.SldMasterIdLst.SldMasterId.class.getMethod("getId2").invoke(smId); + } catch (Throwable ignore) { + } + } + if (rId == null) { + System.out.println("找不到该 sldMasterId 对应的 r:id。"); + continue; + } + Relationship rel = rels.getRelationshipByID(rId); + if (rel == null) { + System.out.println("关系未找到: " + rId); + continue; + } + // 再通过关系拿到具体的 Part + Part part = rels.getPart(rel); + if (part instanceof SlideMasterPart) { + SlideMasterPart master = (SlideMasterPart) part; + return master; + } + } + return null; + } + + // /ppt/presProps.xml 的关系类型 + private static final String REL_TYPE_PRES_PROPS = + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps"; + + // (可选兜底)/ppt/presProps.xml 的 Content-Type + private static final String PRES_PROPS_CT = + "application/vnd.openxmlformats-officedocument.presentationml.presentationProperties+xml"; + + /** + * 仿照你给的 getSlideMaster 写法: + * 从 MainPresentationPart 的关系里定位 /ppt/presProps.xml,返回 PresentationPr。 + * 若不存在或无法解析,则返回 null。 + */ + public static PresentationPr getPresentationPr(PresentationMLPackage ppt) { + try { + MainPresentationPart mpp = ppt.getMainPresentationPart(); + RelationshipsPart rels = mpp.getRelationshipsPart(); + + // 1) 先按关系类型精确查找 + for (Relationship rel : rels.getRelationships().getRelationship()) { + if (REL_TYPE_PRES_PROPS.equals(rel.getType())) { + Part part = rels.getPart(rel); + PresentationPr pr = toPresentationPr(part); + if (pr != null) return pr; + } + } + + // 2) 兜底:在整包里按 Content-Type 扫描(有的文件没建关系时可用) + for (Part p : ppt.getParts().getParts().values()) { + if (PRES_PROPS_CT.equals(p.getContentType())) { + PresentationPr pr = toPresentationPr(p); + if (pr != null) return pr; + } + } + } catch (Exception ignore) { + } + return null; + } + + /** + * 将 Part 转为 PresentationPr: + * - 若 JAXB 根对象已是 PresentationPr,直接返回; + * - 否则把 JAXB 对象序列化为 XML,再用 Context.jcPML 反序列化成 PresentationPr(兼容老版本根类型 CTPresentationProperties)。 + */ + @SuppressWarnings("unchecked") + private static PresentationPr toPresentationPr(Part part) { + try { + if (part instanceof JaxbXmlPart) { + Object jaxb = ((JaxbXmlPart) part).getJaxbElement(); + if (jaxb instanceof PresentationPr) { + return (PresentationPr) jaxb; + } + // 兼容老版本类型:用 XML 重新反解为 PresentationPr + String xml = XmlUtils.marshaltoString(jaxb, true); + Object obj = XmlUtils.unmarshalString(xml, Context.jcPML, PresentationPr.class); + return (PresentationPr) obj; + } + } catch (Exception ignore) { + } + return null; + } +} 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 index a3d64a97..9875858a 100644 --- 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 @@ -20,7 +20,7 @@ import java.util.List; // 单个幻灯片设置 public class SlideSetting { // 幻灯片 版式 - public static String getSlideLayout(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideLayout(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { SlideLayoutPart layoutPart = slidePart.getSlideLayoutPart(); if (layoutPart.getContents().getCSld() != null) { return layoutPart.getContents().getCSld().getName(); @@ -28,19 +28,23 @@ public class SlideSetting { return ""; } // 设计 主题模板 - public static String getSlideTheme(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideTheme(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { // 获取第一个幻灯片母版(通常主主题在这里) return "WPS"; } // 背景填充 填充方式 - public static String getSlideBackground(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideBackground(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { + Sld slide = slidePart.getContents(); CTBackground ctBackground = slidePart.getContents().getCSld().getBg(); if (ctBackground != null) { CTBackgroundProperties ctBackgroundProperties = ctBackground.getBgPr(); return getBackgroundFillType(ctBackgroundProperties); + } else { + // 需要检查母版的设置 + CTBackgroundProperties ctBackgroundProperties = slideMasterPart.getJaxbElement().getCSld().getBg().getBgPr(); + return getBackgroundFillType(ctBackgroundProperties); } - return ""; } private static String getBackgroundFillType(CTBackgroundProperties bgPr) { if (bgPr.getSolidFill() != null) return "纯色填充"; @@ -52,7 +56,7 @@ public class SlideSetting { return "未知填充类型"; } // 背景填充 纯色填充-颜色 - public static String getSlideBackgroundSolidColor(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideBackgroundSolidColor(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { CTBackground ctBackground = slidePart.getContents().getCSld().getBg(); if (ctBackground != null) { CTBackgroundProperties ctBackgroundProperties = ctBackground.getBgPr(); @@ -76,19 +80,19 @@ public class SlideSetting { } // 背景填充 隐藏背景图形 - public static String getSlideBackgroundHideShape(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideBackgroundHideShape(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { return "待开发"; } // 幻灯片切换 切换效果 - public static String getSlideTransition(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideTransition(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) 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 { + public static String getSlideTransitionSound(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { Sld slide = (Sld) slidePart.getContents(); CTSlideTransition ctSlideTransition = slide.getTransition(); CTTransitionSoundAction ctTransitionSoundAction = ctSlideTransition.getSndAc(); @@ -100,18 +104,18 @@ public class SlideSetting { } // 幻灯片切换 切换速度 - public static String getSlideTransitionSpeed(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideTransitionSpeed(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { Sld slide = (Sld) slidePart.getContents(); CTSlideTransition ctSlideTransition = slide.getTransition(); Long speed = ctSlideTransition.getAdvTm(); if (speed != null) { - return String.valueOf(speed) + "毫秒"; + return speed / 1000 + "秒"; } return ""; } // 隐藏幻灯片 - public static String getSlideHide(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideHide(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { Sld sld = slidePart.getJaxbElement(); if (sld != null) { return sld.isShow() ? "显示幻灯片" : "隐藏幻灯片"; @@ -120,7 +124,7 @@ public class SlideSetting { } // 页眉页脚 包含→日期和时间 - public static String getSlideHeaderFooterDateTime(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideHeaderFooterDateTime(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); for (Object shapeObj : shapes) { @@ -134,7 +138,7 @@ public class SlideSetting { return ""; } // 页眉页脚 包含→幻灯片编号 - public static String getSlideHeaderFooterSlideNumber(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideHeaderFooterSlideNumber(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); for (Object shapeObj : shapes) { @@ -148,7 +152,7 @@ public class SlideSetting { return ""; } // 页眉页脚 包含→页脚 - public static String getSlideHeaderFooterFooter(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + public static String getSlideHeaderFooterFooter(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException { GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); for (Object shapeObj : shapes) { diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PptxUtils.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PptxUtils.java new file mode 100644 index 00000000..fac05ff1 --- /dev/null +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PptxUtils.java @@ -0,0 +1,20 @@ +package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils; + +public class PptxUtils { + public static String detectAspectName(long width, long height) { + if (width <= 0 || height <= 0) return "自定"; + // 不区分横竖:3:4 也当 4:3 + long w = Math.max(width, height), h = Math.min(width, height); + double tol = 0.01; // 允许1%误差 + + // 判断 4:3 -> w/h ≈ 4/3 <=> |w*3 - h*4| / max(w*3, h*4) <= tol + long l43 = w * 3, r43 = h * 4; + if (Math.abs(l43 - r43) <= Math.max(l43, r43) * tol) return "4:3"; + + // 判断 16:9 -> w/h ≈ 16/9 <=> |w*9 - h*16| / max(w*9, h*16) <= tol + long l169 = w * 9, r169 = h * 16; + if (Math.abs(l169 - r169) <= Math.max(l169, r169) * tol) return "16:9"; + + return "自定义"; + } +} diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PtToCmConverter.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PtToCmConverter.java index 539359cb..0198872b 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PtToCmConverter.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/PtToCmConverter.java @@ -12,8 +12,8 @@ public class PtToCmConverter { * @param points 点数 * @return 厘米数 */ - public static double pointsToCm(double points) { - return points * PT_TO_CM; + public static int pointsToCm(double points) { + return (int) Math.round(points * PT_TO_CM); } /** diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/Transition.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/Transition.java index 6e6998af..4de906e2 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/Transition.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_pptx/pptx4j/utils/Transition.java @@ -30,7 +30,7 @@ public class Transition { case "drumroll.wav": return "擂鼓 / 滚奏"; case "click.wav": - return "点击"; + return "单机"; case "coin.wav": return "硬币"; case "bomb.wav": @@ -86,7 +86,7 @@ public class Transition { case "onclick": case "clickeffect": return "单击时"; - case "witheffect": + case "witheffect": return "之前"; case "onbegin": return "与上一动画同时"; case "aftereffect":