【新增】 最新版PPTX得判分

This commit is contained in:
dlaren
2025-08-16 17:53:27 +08:00
parent b40ba22ef4
commit aa15ce65b2
20 changed files with 3093 additions and 619 deletions

View File

@@ -5,12 +5,10 @@ import com.example.exam.exam.dal.*;
import com.example.exam.exam.mapper.EducationPaperMapper;
import com.example.exam.exam.mapper.EducationPaperQuMapper;
import com.example.exam.exam.mapper.EducationPaperSchemeMapper;
import com.example.exam.exam.service.autoforchoice.AutoForChoiceService;
import com.example.exam.exam.service.c.JudgementService;
import com.example.exam.exam.service.question.IExamQuestionService;
import com.example.exam.exam.service.stupaperscore.StuPaperScoreService;
import com.example.exam.exam.service.tenant.SystemTenantService;
import com.example.exam.exam.service.wpspptx.WpsPptxJudgementDto;
import com.example.exam.exam.utils.HtmlAppender;
import com.example.exam.exam.utils.c.LogFileUtils;
import com.fasterxml.jackson.databind.JsonNode;

View File

@@ -6,13 +6,8 @@ import com.example.exam.exam.dal.ExamQuestionAnswer;
import com.example.exam.exam.dal.SourceAndText;
import com.example.exam.exam.service.wpsexcel.vo.JudgementXlsxVO;
import com.example.exam.exam.service.wpsexcel.vo.WpsXlsxInfoVo;
import com.example.exam.exam.service.wpspptx.WpsPptxJudgementDto;
import com.example.exam.exam.service.wpsword.docx4j.DocxMaster;
import com.example.exam.exam.service.wpsword.docx4j.vo.JudgementWordsVO;
import com.example.exam.exam.service.wpsword.docx4j.vo.WpsDocxInfoVo;
import com.example.exam.exam.utils.CustomMultipartFile;
import com.example.exam.exam.utils.HtmlAppender;
import com.example.exam.exam.utils.c.LogFileUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

View File

@@ -1,6 +1,5 @@
package com.example.exam.exam.service.wpsexcel;
import com.example.exam.exam.service.wpspptx.WpsPptxJudgementDto;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Cell;

View File

@@ -1,40 +0,0 @@
package com.example.exam.exam.service.wpspptx;
/**
* @author REN
*/
public class DeclareNamespaceForPPT {
public static String getNameSpace(String titleName) {
if ("p".equals(titleName)) {
return "http://schemas.openxmlformats.org/presentationml/2006/main";
}
if ("a".equals(titleName)) {
return "http://schemas.openxmlformats.org/drawingml/2006/main";
}
if ("pic".equals(titleName)) {
return "http://schemas.openxmlformats.org/drawingml/2006/picture";
}
if ("c".equals(titleName)) {
return "http://schemas.openxmlformats.org/drawingml/2006/chart";
}
if ("dgm".equals(titleName)) {
return "http://schemas.openxmlformats.org/drawingml/2006/diagram";
}
if ("lc".equals(titleName)) {
return "http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas";
}
if ("v".equals(titleName)) {
return "urn:schemas-microsoft-com:vml";
}
if ("m".equals(titleName)) {
return "http://schemas.openxmlformats.org/officeDocument/2006/math";
}
if ("mc".equals(titleName)) {
return "http://schemas.openxmlformats.org/markup-compatibility/2006";
}
if ("a14".equals(titleName)) {
return "http://schemas.microsoft.com/office/drawing/2010/main";
}
return "";
}
}

View File

@@ -1,161 +0,0 @@
package com.example.exam.exam.service.wpspptx;
import java.util.List;
/**
* @author REN
*/
public class JudgementConvert {
public static String getConvert(List<String> valueList, String formula){
String value = "";
if ("NULL".equals(formula)) {
value = valueList.get(0);
} else if (formula.contains("PageSize")) {
value = pageSize(valueList.get(0), valueList.get(1));
} else if (formula.contains("timing")) {
// 查询自定义动画效果
value = getTiming(valueList.get(0), valueList.get(1));
} else if (formula.contains("Timing_filter")) {
// 动画方向查询
value = getTimingFilter(valueList.get(0));
} else if (formula.contains("Timing_Evt")) {
// 动画触发方式
value = getTimingEvt(valueList.get(0));
}
// 继续转换
return value;
}
private static String getTimingEvt(String evt) {
String evtValue = "";
if (evt.contains("onBegin")) {
if (evt.contains("downRight")) {
evtValue += "自动开始";
}
if (evt.contains("onClick")) {
evtValue += "鼠标点击时开始";
}
if (evt.contains("afterPrevious")) {
evtValue += "在上一个动画之后开始";
}
if (evt.contains("withPrevious")) {
evtValue += "与上一个动画同时开始";
}
if (evt.contains("onEnd")) {
evtValue += "在另一个动画结束时开始";
}
if (evt.contains("onNext")) {
evtValue += "在下一动画前触发";
}
if (evt.contains("onPrev")) {
evtValue += "在上一动画前触发";
}
}
return evtValue;
}
private static String getTimingFilter(String filter) {
String filterValue = "";
if (filter.contains("strips")) {
if (filter.contains("downRight")) {
filterValue += "从左上往右下飞入";
}
if (filter.contains("upLeft")) {
filterValue += "从右下往左上飞入";
}
if (filter.contains("upRight")) {
filterValue += "从左下往右上飞入";
}
if (filter.contains("downLeft")) {
filterValue += "从右上往左下飞入";
}
}
return filterValue;
}
private static String getTiming(String transition, String filter){
String transitionValue = "";
String filterValue = "";
if ("in".equals(transition)) {
transitionValue = "进入动画";
if ("fade".equals(filter)) {
filterValue = "淡入淡出";
} else if ("fly(fromLeft)".equals(filter)) {
filterValue = "飞入(从左)";
} else if ("fly(fromRight)".equals(filter)) {
filterValue = "飞入(从右)";
} else if ("fly(fromTop)".equals(filter)) {
filterValue = "飞入(从上)";
} else if ("fly(fromBottom)".equals(filter)) {
filterValue = "飞入(从下)";
} else if ("strips(downRight)".equals(filter)) {
filterValue = "条纹(右下)";
} else if ("strips(upLeft)".equals(filter)) {
filterValue = "条纹(左上)";
} else if ("checkerboard(across)".equals(filter)) {
filterValue = "棋盘格(横向)";
} else if ("wipe(right)".equals(filter)) {
filterValue = "擦除(从左向右)";
} else if ("wipe(left)".equals(filter)) {
filterValue = "擦除(从右向左)";
} else if ("randomBars(vertical)".equals(filter)) {
filterValue = "随机条形(垂直)";
} else if ("zoom".equals(filter)) {
filterValue = "缩放";
} else if ("circle(out)".equals(filter)) {
filterValue = "圆形展开";
} else if ("plus(out)".equals(filter)) {
filterValue = "十字形展开";
} else if ("diamond(out)".equals(filter)) {
filterValue = "菱形展开";
} else if ("shape(circle)".equals(filter)) {
filterValue = "使用形状(圆形)";
}
} else if ("out".equals(transition)) {
transitionValue = "退出动画";
if ("fade".equals(filter)) {
filterValue = "淡出";
} else if ("fly(toLeft)".equals(filter)) {
filterValue = "飞出(向左)";
} else if ("fly(toRight)".equals(filter)) {
filterValue = "飞出(向右)";
} else if ("strips(upLeft)".equals(filter)) {
filterValue = "条纹(左上)";
} else if ("wipe(left)".equals(filter)) {
filterValue = "擦除(向左)";
} else if ("zoom".equals(filter)) {
filterValue = "缩小";
} else if ("shape(diamond)".equals(filter)) {
filterValue = "菱形消失";
}
} else if ("emph".equals(transition)) {
transitionValue = "强调动画";
} else if ("path".equals(transition)) {
transitionValue = "动作路径动画";
}
return transitionValue + filterValue;
}
private static String pageSize(String widthTwip, String heightTwip){
// 转换为 cm1 twip = 1/1440 英寸 = 2.54 / 1440 cm
double widthCm = Double.parseDouble(widthTwip) / 360000;
double heightCm = Double.parseDouble(heightTwip) / 360000;
// 四舍五入保留一位小数
widthCm = Math.round(widthCm * 10) / 10.0;
heightCm = Math.round(heightCm * 10) / 10.0;
// 判断标准纸型(以 cm 为单位进行匹配)
String paperType = "自定义";
if (approx(widthCm, 21.0) && approx(heightCm, 29.7)) {
paperType = "A4";
} else if (approx(widthCm, 29.7) && approx(heightCm, 42.0)) {
paperType = "A3";
} else if (approx(widthCm, 14.8) && approx(heightCm, 21.0)) {
paperType = "A5";
}
return paperType;
}
private static boolean approx(double a, double b) {
return Math.abs(a - b) < 0.5; // 误差范围 0.5cm 内
}
}

View File

@@ -1,39 +0,0 @@
package com.example.exam.exam.service.wpspptx;
import lombok.Data;
/**
* @author REN
*/
@Data
public class JudgementReqVo {
private String path;
// 中文考点
private String chineseName;
// 查询段落所在文件名称
private String fileNama;
// 查询的段落
private String paragraph;
// 需要查询的标签
private String title;
// 需要查询的参数 当isText == 1 为null
private String valueList;
// 值转换类型 String
private String type;
// 需要查找的是文本还是参数0参数1文本
private String isText;
// 对结果的判断0:否 返回值1:是 返回 是否)
private String isTrue;
// 0:内参数1外参数2内外参数方法使用#继续分割)
private String isParameter;
}

View File

@@ -1,303 +0,0 @@
package com.example.exam.exam.service.wpspptx;
import org.apache.commons.io.IOUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @author REN
*/
public class JudgementWpsPPT {
public static List<WpsPptxJudgementDto> getValues(List<JudgementReqVo> judgementReq) throws InvalidFormatException, IOException, ParserConfigurationException, SAXException {
// 1、文件路径或者文件的属性
// 2、查找文件的属性其中字段为name firstFunction SecondFunction ThirdFunction FourthFunction isboo betong 转换
// 3、名称 标签 标签段落 值标签 关联关系 关联文件名称 )
// 4、逻辑关系 通过name查询文档数据根据标签段落查询对应的段落值再根据标签地址查询对应在查询对应的值标签外部文件暂定
// String fileNama = "presentation";
// String functions = "(//p:presentation)[1]";
// String slide = "p:sldSz";
// String values = "cx#cy";
// 创建返回值
List<WpsPptxJudgementDto> judgementList = new ArrayList<>();
for (JudgementReqVo reqVo : judgementReq) {
String path = reqVo.getPath();
String chineseName = reqVo.getChineseName();
String fileNama = reqVo.getFileNama();
String paragraph = reqVo.getParagraph();
String title = reqVo.getTitle();
String values = reqVo.getValueList();
String type = reqVo.getType();
String isText = reqVo.getIsText();
String isTrue = reqVo.getIsTrue();
String isParameter = reqVo.getIsParameter();
String firstName = paragraph.split(":")[0].split("/")[paragraph.split(":")[0].split("/").length -1];
// 组合之后可能用到的考点信息
String englishName = fileNama + "@!" + paragraph + "@!" + title + "@!" + values + "@!" + type + "@!" + isText + "@!" + isTrue + "@!" + isParameter;
XmlCursor cursor = getXmlCursor(path, fileNama, null);
System.out.println(cursor.xmlText());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
XmlCursor slideCursor = cursor;
if (title.contains(">")) {
// 说明想要
title = title.replace(">", "");
judgementList = setData(judgementList, chineseName + "正在开发中", englishName + "@!" + "正在开发中");
} else
// 当标签存在特定值得时候,不需要继续第一段得查询,直接用所有得进行查找
if (title.contains("?")) {
title = title.replace("?", "");
// 条件成立
// 获取 XML 字符串
String xmlText = slideCursor.getObject().xmlText();
InputStream is = new ByteArrayInputStream(xmlText.getBytes(StandardCharsets.UTF_8));
// 然后再用 builder.parse
Document doc = builder.parse(is);
System.out.println(xmlText);
// 通过递归调用 查询标签位置,可能出现得层级关系,避免出现一层不对得情况查询多个标签地址
Element element = XmlRecursiveFinder.findElement(doc, title);
List<String> valuesList = new ArrayList<>();
if ("1".equals(isTrue)) {
String value = element == null ? "" : "" ;
judgementList = setData(judgementList, chineseName + value, englishName+ "@!" + value);
} else {
String[] valuesArr = values.split("#");
String oneValue = "";
for (String yivalue : valuesArr) {
if ("0".equals(isText)) {
oneValue = element.getAttribute(yivalue);
} else {
oneValue = element.getTextContent();
}
valuesList.add(oneValue);
}
// 开始进行考点等的判断
String pointValue = JudgementConvert.getConvert(valuesList, type);
if ("1".equals(isTrue)) {
pointValue = pointValue != null ? "" : "";
}
judgementList = setData(judgementList, chineseName + pointValue, englishName+ "@!" + pointValue);
}
} else {
if (!paragraph.isEmpty()) {
slideCursor.selectPath("declare namespace " + firstName + "='" + DeclareNamespaceForPPT.getNameSpace(firstName) + "' " + paragraph);
}
// 当查询到或者说不需要查询的时候进入
if (slideCursor.toNextSelection() || paragraph.isEmpty()) {
// 获取 XML 字符串
String xmlText = slideCursor.getObject().xmlText();
InputStream is = new ByteArrayInputStream(xmlText.getBytes(StandardCharsets.UTF_8));
// 然后再用 builder.parse
Document doc = builder.parse(is);
if ("Filling_Method".equals(type)) {
// 说明判断填充方式
String value = getFillingMethod(doc, slideCursor, title);
judgementList = setData(judgementList, chineseName + value, englishName+ "@!" + value);
} else if ("Filling_Bg".equals(type)) {
// 说明判断背景填充方式
String value = getFillingBg(doc, slideCursor, title);
judgementList = setData(judgementList, chineseName + value, englishName+ "@!" + value);
} else if ("Line_Spacing".equals(type)) {
// 行距类型
String value = getLineSpacing(doc, slideCursor, title);
judgementList = setData(judgementList, chineseName + value, englishName+ "@!" + value);
}
else {
// 通过递归调用 查询标签位置,可能出现得层级关系,避免出现一层不对得情况查询多个标签地址
Element element = XmlRecursiveFinder.findElement(doc, title);
if (element == null) {
return judgementList;
}
List<String> valuesList = new ArrayList<>();
if ("1".equals(isTrue)) {
String value = element == null ? "" : "" ;
judgementList = setData(judgementList, chineseName + value, englishName+ "@!" + value);
} else {
String[] valuesArr = values.split("#");
String oneValue = "";
for (String yivalue : valuesArr) {
if ("0".equals(isText)) {
oneValue = element.getAttribute(yivalue);
} else {
oneValue = element.getTextContent();
}
valuesList.add(oneValue);
}
// 开始进行考点等的判断
String pointValue = JudgementConvert.getConvert(valuesList, type);
if ("1".equals(isTrue)) {
pointValue = pointValue != null ? "" : "";
}
judgementList = setData(judgementList, chineseName + pointValue, englishName+ "@!" + pointValue);
}
}
} else {
// 如果存在备选方案,使用备选的参数
// 先过去RID的对应的值所有方法中带有了#区分第一段是获取值,第二段是获取外文件的参数
}
}
}
return judgementList;
}
public static String getFillingMethod(Document doc, XmlCursor cursor, String titleName) {
String value = "";
for (String title : titleName.split("#")) {
// 获取填充方法
NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]);
if (nodeList.getLength() > 0) {
if (title.equals("a:solidFill")) {
value = "纯色填充";
}
}
}
return value;
}
public static String getLineSpacing(Document doc, XmlCursor cursor, String titleName) {
String value = "";
for (String title : titleName.split("#")) {
// 获取填充方法
NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]);
if (nodeList.getLength() > 0) {
if (title.equals("a:spcPts")) {
value = "固定行距";
}
if (title.equals("a:spcPct")) {
value = "多倍行距";
}
}
}
return value;
}
public static String getProjectSymbols(Document doc, XmlCursor cursor, String titleName) {
String value = "";
for (String title : titleName.split("#")) {
// 获取填充方法
NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]);
if (nodeList.getLength() > 0) {
if (title.equals("a:buChar")) {
value = "普通圆点";
}
if (title.equals("a:buBlip")) {
value = "图片项目符号";
}
if (title.equals("a:buAutoNum")) {
value = "自动编号";
}
}
}
return value;
}
public static String getFillingBg(Document doc, XmlCursor cursor, String titleName) {
String value = "";
for (String title : titleName.split("#")) {
// 获取填充方法
NodeList nodeList = doc.getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(title.split(":")[0]), title.split(":")[1]);
if (nodeList.getLength() > 0) {
if (title.equals("a:solidFill")) {
value = "纯色填充";
}
if (title.equals("a:gradFill")) {
value = "渐变填充";
}
if (title.equals("a:blipFill")) {
value = "图片填充";
}
if (title.equals("a:pattFill")) {
value = "图案填充";
}
if (title.equals("a:grpFill")) {
value = "分组填充";
}
}
}
return value;
}
public static XmlCursor getXmlCursor(String path, String titleName, String value) throws IOException, InvalidFormatException {
XmlCursor cursor = null;
try (OPCPackage pkg = OPCPackage.open(path)) {
for (PackagePart part : pkg.getParts()) {
String entryName = part.getPartName().getName();
if (value == null) {
// 如果说明是空的话,直接查询文件名称
if (entryName.contains(titleName) && entryName.contains(".xml") && !entryName.contains(".rels")) {
try (InputStream is = part.getInputStream()) {
String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8);
XmlObject xmlObject = XmlObject.Factory.parse(xmlContent);
cursor = xmlObject.newCursor();
break;
} catch (XmlException e) {
throw new RuntimeException(e);
}
}
} else {
// 想查询外置文件
if (entryName.contains(titleName) && entryName.contains(".xml") && entryName.contains(".rels")) {
try (InputStream is = part.getInputStream()) {
String xmlContent = IOUtils.toString(is, StandardCharsets.UTF_8);
XmlObject xmlObject = XmlObject.Factory.parse(xmlContent);
xmlObject.newCursor().selectPath("declare namespace r='http://schemas.openxmlformats.org/package/2006/relationships' .//r:Relationships/r:Relationship");
if (xmlObject.newCursor().toNextSelection()) {
String id = xmlObject.newCursor().getAttributeText(new QName("Id"));
if (id.equals(value)) {
String target = xmlObject.newCursor().getAttributeText(new QName("Target"));
getXmlCursor(path, target.split("/")[target.split("/").length-1], null);
}
}
} catch (XmlException e) {
throw new RuntimeException(e);
}
}
}
}
}
return cursor;
}
/**
* 向考点对象存放数据
* @param wpsPptxJudgementDtoList 考点对象数组
* @param chineseName 给考点对象添加的中文描述
* @param englishName 给考点对象添加的英文描述
* @return 考点对象数组
*/
private static List<WpsPptxJudgementDto> setData(List<WpsPptxJudgementDto> wpsPptxJudgementDtoList,
String chineseName,
String englishName) {
WpsPptxJudgementDto judgementDto = new WpsPptxJudgementDto();
judgementDto.setContentIn(chineseName);
judgementDto.setContent(englishName);
judgementDto.setScoreRate("1");
wpsPptxJudgementDtoList.add(judgementDto);
return wpsPptxJudgementDtoList;
}
}

View File

@@ -3,10 +3,14 @@ package com.example.exam.exam.service.wpspptx;
import com.example.exam.exam.dal.ExamQuestion;
import com.example.exam.exam.dal.ExamQuestionAnswer;
import com.example.exam.exam.dal.SourceAndText;
import com.example.exam.exam.service.wpspptx.pptx4j.SlideMaster;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.JudgementSlidesVO;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.SlideDataInfoVO;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.WpsSlideInfoVo;
import com.example.exam.exam.utils.HtmlAppender;
import com.example.exam.exam.utils.c.LogFileUtils;
import org.springframework.stereotype.Service;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
@@ -26,30 +30,27 @@ public class JudgementWpsPptxServiceImpl implements JudgementWpsPptxService {
// LogFileUtils.writeLine("✅ 开始WPS_Pptx判分");
judgementStr = HtmlAppender.appendHtmlLine(judgementStr, "✅ 开始WPS_Pptx判分");
double wpsPptScore = 0;
List<JudgementReqVo> judgementReq = new ArrayList<>();
List<WpsSlideInfoVo> judgementReq = new ArrayList<>();
// 3、获取答案得组成
List<ExamQuestionAnswer> answerList = examQuestion.getAnswerList();
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
JudgementReqVo judgementReqVo = new JudgementReqVo();
WpsSlideInfoVo judgementReqVo = new WpsSlideInfoVo();
// 拆分数据、
String[] pptxInfos = examQuestionAnswer.getContent().split("@!");
judgementReqVo.setFileNama(pptxInfos[0]);
judgementReqVo.setParagraph(pptxInfos[1]);
judgementReqVo.setTitle(pptxInfos[2]);
judgementReqVo.setValueList(pptxInfos[3]);
judgementReqVo.setType(pptxInfos[4]);
judgementReqVo.setIsText(pptxInfos[5]);
judgementReqVo.setIsTrue(pptxInfos[6]);
judgementReqVo.setIsParameter(pptxInfos[7]);
judgementReqVo.setPath(path);
String[] pptxInfos = examQuestionAnswer.getContent().split("@");
judgementReqVo.setSlideIndex(pptxInfos[0]);
judgementReqVo.setFirstName(pptxInfos[1]);
judgementReqVo.setFunction(pptxInfos[2]);
judgementReqVo.setExamName(pptxInfos[3]);
judgementReqVo.setExamCode(pptxInfos[4]);
judgementReqVo.setMethod(pptxInfos[5]);
judgementReq.add(judgementReqVo);
}
List<WpsPptxJudgementDto> judgementDtos = JudgementWpsPPT.getValues(judgementReq);
List<JudgementSlidesVO> judgementDtos = SlideMaster.slideMaster(judgementReq, new File(path));
// 4、进行关联判断
for (ExamQuestionAnswer examQuestionAnswer : answerList) {
boolean flag = false;
double one_sorce = 0;
for (WpsPptxJudgementDto pptxJudgementDto : judgementDtos) {
for (JudgementSlidesVO pptxJudgementDto : judgementDtos) {
if (pptxJudgementDto.getContent().equals(examQuestionAnswer.getContent())) {
flag = true;
// 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重

View File

@@ -1,14 +0,0 @@
package com.example.exam.exam.service.wpspptx;
import lombok.Data;
/**
* @author REN
*/
@Data
public class WpsPptxJudgementDto {
private String content;
private String contentIn;
private String image;
private String scoreRate;
}

View File

@@ -1,39 +0,0 @@
package com.example.exam.exam.service.wpspptx;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XmlRecursiveFinder {
// 递归入口
public static Element findElementRecursive(Node startNode, String[] tags, int index) {
if (index >= tags.length || startNode == null) return null;
String tag = tags[index].split(":")[1];
String namespace = tags[index].split(":")[0];
NodeList children;
if (startNode instanceof Document) {
children = ((Document) startNode).getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(namespace), tag);
} else {
children = ((Element) startNode).getElementsByTagNameNS(DeclareNamespaceForPPT.getNameSpace(namespace), tag);
}
if (children.getLength() == 0) return null;
Element child = (Element) children.item(0);
if (index == tags.length - 1) {
// 最后一层,返回当前节点
return child;
}
return findElementRecursive(child, tags, index + 1);
}
// 封装外部调用
public static Element findElement(Document doc, String titleName) {
String[] title = titleName.split("@");
return findElementRecursive(doc, title, 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,859 @@
package com.example.exam.exam.service.wpspptx.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();
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈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();
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈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();
// 获取位置和尺寸(单位 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;
}
}
}
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/endPos0..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(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 "位置动画Xppt_x";
if ("ppt_y".equalsIgnoreCase(attr)) return "位置动画Yppt_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<String> 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 "";
}
/// 形状动作,鼠标单机-动作
}

View File

@@ -0,0 +1,17 @@
package com.example.exam.exam.service.wpspptx.pptx4j;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.PresentationMLPackage;
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
public class SlideAllSetting {
// 设计 主题模板
public static String getAllSlideTheme( SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException {
// 获取第一个幻灯片母版(通常主主题在这里)
return "WPS";
}
// 幻灯片切换 切换声音
}

View File

@@ -0,0 +1,97 @@
package com.example.exam.exam.service.wpspptx.pptx4j;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.xmlbeans.XmlCursor;
import org.springframework.web.multipart.MultipartFile;
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.vo.SlideDataInfoVO;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SlideConversion {
// 大纲
// 1、获取第一层目录 幻灯片 - 母版 - 设置 - 其他
// 2、获取第二层目录 第x页 - 幻灯片母版/备注母版 - 幻灯片设置 - 文件操作
// 3、获取第三层目录 幻灯片设置/形状 - 幻灯片母版 - 幻灯片设置 - 文件操作
public static List<SlideDataInfoVO> SlideDataInfos(MultipartFile file) throws Exception {
List<SlideDataInfoVO> dataInfoVOS = new ArrayList<>();
try (InputStream inputStream = file.getInputStream();
XMLSlideShow pptxXml = new XMLSlideShow(inputStream)) {
int index = 0;
String firstId = getStringRandom();
setSlideDataInfo(firstId, "", "幻灯片", "slide", "", false, dataInfoVOS);
for (XSLFSlide slides : pptxXml.getSlides()){
index += 1;
String secondId = getStringRandom();
setSlideDataInfo(secondId, firstId, ""+index+"", "sld", String.valueOf(index), true, dataInfoVOS);
// 第三层
String thirdId = getStringRandom();
int indexCnvPr = 1;
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()) {
XmlCursor cNvPrXml = spCursor.newCursor();
cNvPrXml.selectPath("declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//p:cNvPr/@name");
if (cNvPrXml.toNextSelection()) {
String name = cNvPrXml.getTextValue();
String fourId = getStringRandom();
setSlideDataInfo(fourId, secondId, "形状"+indexCnvPr+"->"+name, "shape", String.valueOf(index) + "_" + String.valueOf(indexCnvPr), true, dataInfoVOS);
indexCnvPr += 1;
}
}
XmlCursor picCursor = slides.getXmlObject().newCursor();
System.out.println(picCursor.xmlText());
picCursor.selectPath("declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//p:pic");
int indexCnvPrPic = 1;
while (picCursor.toNextSelection()) {
String fourId = getStringRandom();
setSlideDataInfo(fourId, secondId, "图像"+indexCnvPrPic+"->图像", "shapePic", String.valueOf(indexCnvPrPic), true, dataInfoVOS);
indexCnvPrPic += 1;
}
}
// 设置
String firstIds = getStringRandom();
setSlideDataInfo(firstIds, "", "设置", "presentation", String.valueOf(0), false, dataInfoVOS);
String secondIds = getStringRandom();
setSlideDataInfo(secondIds, firstIds, "幻灯片设置", "presentation", String.valueOf(0), true, dataInfoVOS);
// 母版
// String dFirstId = getStringRandom();
// setPptxInfo("母版", "p:sld", "p:sld", filePath, dFirstId, "0", pptxInfoReqVos);
} catch (Exception e) {
throw new RuntimeException(e);
}
return dataInfoVOS;
}
public static String getStringRandom() {
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
// 生成指定长度的随机字符字符串
for (int i = 0; i < 10; i++) {
int randomIndex = random.nextInt(characters.length());
// 随机字符
sb.append(characters.charAt(randomIndex));
}
return sb.toString();
}
// 想数组中添加数据
public static void setSlideDataInfo(String id, String parentId, String text,
String type, String index, boolean isTrue, List<SlideDataInfoVO> dataInfoVOS) throws Exception {
SlideDataInfoVO dataInfo = new SlideDataInfoVO();
dataInfo.setId(id);
dataInfo.setParentId(parentId);
dataInfo.setName(text);
dataInfo.setType(type);
dataInfo.setIndex(index);
dataInfo.setClick(isTrue);
dataInfoVOS.add(dataInfo);
}
}

View File

@@ -0,0 +1,128 @@
package com.example.exam.exam.service.wpspptx.pptx4j;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.JudgementSlidesVO;
import com.example.exam.exam.service.wpspptx.pptx4j.vo.WpsSlideInfoVo;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.PresentationMLPackage;
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.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class SlideMaster {
public static List<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, File file) throws IOException, Docx4JException {
// File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx");
List<JudgementSlidesVO> judgementSlidesVOS = new ArrayList<>();
// 1、获取想要判断的文件地址文件流
// try (InputStream inputStream = file.getInputStream()) {
try {
// 加载 .pptx 文件
PresentationMLPackage ppt =
(PresentationMLPackage) OpcPackage.load(file);
// 你可以在这里处理 ppt比如读取文字、页数等
for (WpsSlideInfoVo wpsSlideInfoVo : wpsSlideInfoVos) {
// 幻灯片位置(第几张幻灯片)
String slideIndex = wpsSlideInfoVo.getSlideIndex().split("_")[0];
String shapeIndex = wpsSlideInfoVo.getSlideIndex().split("_")[1];
// 大类名称
String firstName = wpsSlideInfoVo.getFirstName();
// 方法名称
String function = wpsSlideInfoVo.getFunction();
// 考点名称
String examName = wpsSlideInfoVo.getExamName();
// 考点代码
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) {
if (method.equals("shape")) {
// 获取幻灯片内容
slideIndexFoFile++;
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);
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();
}
return judgementSlidesVOS;
}
}

View File

@@ -0,0 +1,154 @@
package com.example.exam.exam.service.wpspptx.pptx4j;
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.SlidePart;
import org.pptx4j.pml.*;
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<Object> 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<Object> 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<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
for (Object shapeObj : shapes) {
if (shapeObj instanceof Shape) {
Shape shape = (Shape) shapeObj;
if (shape.getNvSpPr().getCNvPr().getName().equals("页脚")) {
return "包含";
}
}
}
return "";
}
}

View File

@@ -0,0 +1,215 @@
package com.example.exam.exam.service.wpspptx.pptx4j;
import org.docx4j.dml.*;
import java.math.BigInteger;
import java.text.DecimalFormat;
public class SlideUtils {
public static String getFillType(CTShapeProperties spPr) {
if (spPr == null) return "未知";
if (spPr.getNoFill() != null) {
return "无填充";
}
if (spPr.getSolidFill() != null) {
return "纯色填充";
}
if (spPr.getGradFill() != null) {
return "渐变填充";
}
if (spPr.getBlipFill() != null) {
return "图片填充";
}
if (spPr.getPattFill() != null) {
return "图案填充";
}
return "其他/未知";
}
// 形状填充 纯色填充-颜色
public static String getSolidFillColor(CTSolidColorFillProperties solidFill) {
if (solidFill.getSrgbClr() != null) {
// 直接 RGB 颜色
CTSRgbColor rgb = solidFill.getSrgbClr();
return "#" + rgb.getVal(); // 已经是16进制字符串
}
if (solidFill.getSchemeClr() != null) {
// 主题色(这里只返回主题色名称)
CTSchemeColor scheme = solidFill.getSchemeClr();
return "主题色: " + scheme.getVal().value();
}
if (solidFill.getPrstClr() != null) {
// 预设色(只返回名字)
CTPresetColor preset = solidFill.getPrstClr();
return "预设色: " + preset.getVal().value();
}
return "未知颜色";
}
public static String printGradientFill(CTGradientFillProperties gradFill) {
// 渐变点
CTGradientStopList gsList = gradFill.getGsLst();
if (gsList != null) {
for (CTGradientStop gs : gsList.getGs()) {
String color = "未知颜色";
if (gs.getSrgbClr() != null) {
CTSRgbColor rgb = gs.getSrgbClr();
color = "#" + rgb.getVal();
} else if (gs.getSchemeClr() != null) {
CTSchemeColor scheme = gs.getSchemeClr();
color = "主题色: " + scheme.getVal().value();
}
return "渐变点 位置: " + (gs.getPos() / 1000.0) + "% 颜色: " + color;
}
}
// 渐变类型(线性 / 放射)
if (gradFill.getLin() != null) {
double angle = gradFill.getLin().getAng() / 60000.0; // 单位换成度
return "渐变类型: 线性 角度: " + angle + "°";
} else if (gradFill.getPath() != null) {
return "渐变类型: " + gradFill.getPath().getPath().value();
}
return "未知渐变类型";
}
public static String printImageFill(CTBlipFillProperties blipFill) {
// 填充模式
CTTileInfoProperties tile = blipFill.getTile();
if (tile != null) {
return "填充模式: 平铺";
}
CTStretchInfoProperties stretch = blipFill.getStretch();
if (stretch != null) {
return "填充模式: 拉伸";
}
return "未知填充模式";
}
public static String printPatternFill(CTPatternFillProperties pattFill) {
// 预设纹理/图案名称
return pattFill.getPrst().value();
}
public static String mapPreset(STPresetLineDashVal v) {
// 常见值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(); // 兜底:返回原枚举字符串
}
}
/* ——— 内部:拿到 spPr/effectLst/reflection ——— */
public static CTReflectionEffect getReflection(org.pptx4j.pml.Shape 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 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
}
public static String rgbBytesToHex(byte[] val) {
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<CTPositiveFixedPercentage> alphas) {
if (alphas != null && !alphas.isEmpty()) {
double pct = alphas.get(0).getVal() / 1000.0;
return String.format("%.0f%%", pct);
}
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();
}
}

View File

@@ -0,0 +1,19 @@
package com.example.exam.exam.service.wpspptx.pptx4j.vo;
import lombok.Data;
/**
* @author REN
*/
@Data
public class JudgementSlidesVO {
// 考点
private String content;
// 考点名称
private String contentIn;
private int scoreRate;
}

View File

@@ -0,0 +1,19 @@
package com.example.exam.exam.service.wpspptx.pptx4j.vo;
import lombok.Data;
@Data
public class SlideDataInfoVO {
private String name;
private String type;
private String id;
private String parentId;
private String index;
private boolean isClick;
}

View File

@@ -0,0 +1,24 @@
package com.example.exam.exam.service.wpspptx.pptx4j.vo;
import lombok.Data;
@Data
public class WpsSlideInfoVo {
// 幻灯片位置(第几张幻灯片)
private String slideIndex;
// 大类名称
private String firstName;
// 方法名称
private String function;
// 考点名称
private String examName;
// 考点代码
private String examCode;
// 形状类型 slide 幻灯片设置 shape 形状(不包含图片) shapePic 形状图片 presentation 总幻灯片设置
private String method;
}