【新增】 补全ppt考点,考点汉化

This commit is contained in:
dlaren
2025-09-09 13:28:39 +08:00
parent 6420ed6ba7
commit ba7e23708f
10 changed files with 386 additions and 124 deletions

View File

@@ -586,7 +586,7 @@ public class ShapePage {
List<Object> 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<Object> 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 "待开发";
}

View File

@@ -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 {
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈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 {
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈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();
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈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<Object> 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<Object> 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 "";
}

View File

@@ -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 ? "横向" : "纵向";
}
}

View File

@@ -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();
}

View File

@@ -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<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, MultipartFile file) throws IOException, Docx4JException {
// File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx");
List<JudgementSlidesVO> 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<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();
int slideIndexFoFile = 0;
for (SlidePart slidePart : slideParts) {
slideIndexFoFile++;
if (method.equals("shape")) {
// 获取幻灯片内容
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
// 查询幻灯片
// 遍历 shape tree 中的 spshape元素
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
CTSlideTiming timing = slidePart.getJaxbElement().getTiming();
List<Object> 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<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();
int slideIndexFoFile = 0;
for (SlidePart slidePart : slideParts) {
slideIndexFoFile++;
if (method.equals("shape")) {
// 获取幻灯片内容
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
// 查询幻灯片
// 遍历 shape tree 中的 spshape元素
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
CTSlideTiming timing = slidePart.getJaxbElement().getTiming();
List<Object> 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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<Object> 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<Object> 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<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
for (Object shapeObj : shapes) {

View File

@@ -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 "自定义";
}
}

View File

@@ -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);
}
/**

View File

@@ -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":