【新增】 最新版PPTX得判分
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
@@ -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){
|
||||
// 转换为 cm:1 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 内
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
// 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
// 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU)
|
||||
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
|
||||
double heightPt = emuToPt(ext.getCy());
|
||||
return String.valueOf(heightPt);
|
||||
}
|
||||
|
||||
// 大小 - 宽度
|
||||
public static String getShapeSizeWidthPt(Pic sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
// 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU)
|
||||
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
|
||||
double widthPt = emuToPt(ext.getCx());
|
||||
return String.valueOf(widthPt);
|
||||
}
|
||||
|
||||
// 大小 - 锁定纵横比
|
||||
public static String getShapeSizeLockAspectRatio(Pic sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
// 获取位置和尺寸(单位 EMU,1英寸=914400 EMU,1cm≈360000 EMU)
|
||||
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
|
||||
Boolean lockAspectRatio = null;
|
||||
if (sp.getSpPr().getPrstGeom() != null &&
|
||||
sp.getSpPr().getPrstGeom().getAvLst() != null) {
|
||||
// 这个部分一般不会直接提供锁定横纵比,要看 spPr.getExtLst()
|
||||
if (sp.getSpPr().getExtLst() != null) {
|
||||
// pptx4j 没有直接的 getter,要自己解析 XML
|
||||
String xml = sp.getSpPr().getExtLst().toString();
|
||||
if (xml.contains("lockAspectRatio=\"1\"") || xml.contains("lockAspectRatio=\"true\"")) {
|
||||
lockAspectRatio = true;
|
||||
} else if (xml.contains("lockAspectRatio=\"0\"") || xml.contains("lockAspectRatio=\"false\"")) {
|
||||
lockAspectRatio = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.valueOf(lockAspectRatio);
|
||||
}
|
||||
|
||||
// EMU 转 pt
|
||||
private static double emuToPt(long emu) {
|
||||
return emu / 12700.0;
|
||||
}
|
||||
|
||||
/// 大小 旋转
|
||||
public static String getShapeSizeRotate(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
// 位置 水平位置
|
||||
public static String getShapePositionLeft(Pic sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
if (xfrm != null) {
|
||||
long x = xfrm.getOff().getX(); // 水平位置(EMU)
|
||||
return String.valueOf(x / 9525.0);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 位置 垂直位置
|
||||
public static String getShapePositionTop(Pic sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
if (xfrm != null) {
|
||||
long y = xfrm.getOff().getY(); // 垂直位置(EMU)
|
||||
return String.valueOf(y / 9525.0);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状填充 填充方式
|
||||
public static String getShapeFillType(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
return SlideUtils.getFillType(spPr);
|
||||
}
|
||||
|
||||
// 形状填充 纯色填充-颜色
|
||||
public static String getShapeFillSolidColor(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
if (spPr != null && spPr.getSolidFill() != null) {
|
||||
return SlideUtils.getSolidFillColor(spPr.getSolidFill());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状填充 渐变填充-效果
|
||||
public static String getShapeFillGradientType(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
|
||||
if (spPr != null && spPr.getGradFill() != null) {
|
||||
System.out.println("形状: " + sp.getNvPicPr().getCNvPr().getName());
|
||||
return SlideUtils.printGradientFill(spPr.getGradFill());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状填充 图片填充-效果
|
||||
public static String getShapeFillPictureType(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
|
||||
if (spPr != null && spPr.getBlipFill() != null) {
|
||||
System.out.println("形状: " + sp.getNvPicPr().getCNvPr().getName());
|
||||
return SlideUtils.printImageFill(spPr.getBlipFill());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状填充 纹理填充-效果
|
||||
public static String getShapeFillTextureType(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
if (spPr != null && spPr.getPattFill() != null) {
|
||||
return SlideUtils.printPatternFill(spPr.getPattFill());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状填充 图案填充-效果
|
||||
public static String getShapeFillPatternType(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
if (spPr != null && spPr.getPattFill() != null) {
|
||||
return spPr.getPattFill().getPrst().value();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状填充 幻灯片背景填充-效果
|
||||
public static String getShapeFillSlideBackgroundType(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
// 形状填充 填充透明度
|
||||
public static String getShapeFillOpacity(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
// 形状线条 绘制
|
||||
public static String getShapeLineDraw(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
|
||||
if (spPr != null && spPr.getLn() != null) {
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
|
||||
// 宽度(EMU -> pt)
|
||||
double widthPt = ln.getW() / 12700.0;
|
||||
System.out.println("形状: " + sp.getNvPicPr().getCNvPr().getName());
|
||||
System.out.println("线宽: " + widthPt + " pt");
|
||||
|
||||
// 颜色
|
||||
if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) {
|
||||
CTSRgbColor rgb = ln.getSolidFill().getSrgbClr();
|
||||
System.out.println("线条颜色: #" + rgb.getVal());
|
||||
}
|
||||
|
||||
// 虚线样式
|
||||
if (ln.getPrstDash() != null) {
|
||||
System.out.println("线型: " + ln.getPrstDash().getVal().value());
|
||||
}
|
||||
|
||||
// 端点样式
|
||||
if (ln.getCap() != null) {
|
||||
System.out.println("端点: " + ln.getCap().value());
|
||||
}
|
||||
return "是";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状线条 线条颜色
|
||||
public static String getShapeLineColor(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
|
||||
if (spPr != null && spPr.getLn() != null) {
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
// 颜色
|
||||
if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) {
|
||||
CTSRgbColor rgb = ln.getSolidFill().getSrgbClr();
|
||||
return rgb.getVal();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状线条 线条宽度
|
||||
public static String getShapeLineWidth(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
|
||||
if (spPr != null && spPr.getLn() != null) {
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
// 宽度(EMU -> pt)
|
||||
double widthPt = ln.getW() / 12700.0;
|
||||
return String.valueOf(widthPt);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状线条 透明度
|
||||
public static String getShapeLineOpacity(Pic sp, CTSlideTiming timing) {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状线条 复合类型
|
||||
public static String getStrokeCompound(Pic sp, CTSlideTiming timing) {
|
||||
if (sp == null || sp.getSpPr() == null) return "未定义";
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
if (ln == null) return "未定义";
|
||||
|
||||
STCompoundLine cmpd = ln.getCmpd(); // 可能为 null
|
||||
String v = (cmpd != null) ? cmpd.value() : "sng"; // 默认单线
|
||||
switch (v) {
|
||||
case "dbl":
|
||||
return "双线 (dbl)";
|
||||
case "thickThin":
|
||||
return "粗-细双线 (thickThin)";
|
||||
case "thinThick":
|
||||
return "细-粗双线 (thinThick)";
|
||||
case "tri":
|
||||
return "三重线 (tri)";
|
||||
case "sng":
|
||||
default:
|
||||
return "单线 (sng)";
|
||||
}
|
||||
}
|
||||
|
||||
// 形状线条 短划线类型
|
||||
public static String getStrokeDash(Pic sp, CTSlideTiming timing) {
|
||||
if (sp == null || sp.getSpPr() == null) return "solid";
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
if (ln == null) return "solid";
|
||||
|
||||
// 1) 预设短划线
|
||||
if (ln.getPrstDash() != null && ln.getPrstDash().getVal() != null) {
|
||||
STPresetLineDashVal val = ln.getPrstDash().getVal();
|
||||
return SlideUtils.mapPreset(val);
|
||||
}
|
||||
|
||||
// 2) 自定义短划线
|
||||
if (ln.getCustDash() != null && ln.getCustDash().getDs() != null && !ln.getCustDash().getDs().isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder("custom ");
|
||||
for (CTDashStop ds : ln.getCustDash().getDs()) {
|
||||
int d = ds.getD(); // dash length(相对线宽的比例单位)
|
||||
int sps = ds.getSp(); // space length(相对线宽的比例单位)
|
||||
sb.append("[d=").append(d).append(", sps=").append(sps).append("] ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
// 3) 未设置 → 视为实线
|
||||
return "solid";
|
||||
}
|
||||
// 形状线条 开始箭头类型
|
||||
public static String getShapeLineStartArrow(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
if (spPr != null && spPr.getLn() != null) {
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
if (ln.getHeadEnd() != null) {
|
||||
return ln.getHeadEnd().getType().value();
|
||||
}
|
||||
}
|
||||
return "无箭头";
|
||||
}
|
||||
// 形状线条 开始箭头粗细
|
||||
public static String getShapeLineStartArrowWidth(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
// 形状线条 结尾箭头类型
|
||||
public static String getShapeLineEndArrow(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
// 形状线条 结尾箭头粗细
|
||||
public static String getShapeLineEndArrowWidth(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
|
||||
// 形状效果 阴影-绘制
|
||||
public static String getShapeShadowDraw(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
CTEffectList effList = spPr.getEffectLst();
|
||||
if (effList == null) {
|
||||
return "否";
|
||||
}
|
||||
// 判断三种阴影效果
|
||||
return effList.getOuterShdw() != null
|
||||
|| effList.getInnerShdw() != null
|
||||
|| effList.getPrstShdw() != null ? "是" : "否";
|
||||
}
|
||||
|
||||
// 形状效果 阴影-效果
|
||||
public static String getShapeShadowEffect(Pic sp, CTSlideTiming timing) {
|
||||
if (sp == null || sp.getSpPr() == null) {
|
||||
return "无阴影";
|
||||
}
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
CTEffectList effList = spPr.getEffectLst();
|
||||
if (effList == null) {
|
||||
return "无阴影";
|
||||
}
|
||||
|
||||
if (effList.getOuterShdw() != null) {
|
||||
return "外阴影";
|
||||
} else if (effList.getInnerShdw() != null) {
|
||||
return "内阴影";
|
||||
} else if (effList.getPrstShdw() != null) {
|
||||
return "预设阴影";
|
||||
}
|
||||
return "无阴影";
|
||||
}
|
||||
|
||||
// 形状效果 倒影-绘制
|
||||
public static String getShapeReflectionDraw(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
CTEffectList effList = spPr.getEffectLst();
|
||||
if (effList == null) {
|
||||
return "无倒影";
|
||||
}
|
||||
|
||||
CTReflectionEffect reflection = effList.getReflection();
|
||||
if (reflection != null) {
|
||||
// 如果需要详细参数,这里可以读取
|
||||
return "有倒影";
|
||||
}
|
||||
return "无倒影";
|
||||
}
|
||||
|
||||
// 形状效果 倒影-效果
|
||||
public static String getShapeReflectionEffect(Pic sp, CTSlideTiming timing) {
|
||||
CTReflectionEffect r = SlideUtils.getReflectionPic(sp);
|
||||
if (r == null) return "无倒影";
|
||||
|
||||
// —— 1) 倒影覆盖程度:用 stPos/endPos(0..100000)估算 —— //
|
||||
double st = SlideUtils.to100k(BigInteger.valueOf(r.getStPos()), 0);
|
||||
double ed = SlideUtils.to100k(BigInteger.valueOf(r.getEndPos()), 100000); // 默认 100%
|
||||
double coverage = Math.max(0, ed - st) / 100000.0; // 0.0 ~ 1.0
|
||||
|
||||
String coverageLabel;
|
||||
if (coverage >= 0.85) {
|
||||
coverageLabel = "全倒影";
|
||||
} else if (coverage >= 0.45) {
|
||||
coverageLabel = "半倒影";
|
||||
} else {
|
||||
coverageLabel = "短倒影";
|
||||
}
|
||||
|
||||
// —— 2) 偏移描述:dist EMU -> pt;<1pt 视作“紧贴”,否则 "Xpt 偏移" —— //
|
||||
double distPt = emuToPt(r.getDist()); // 可能为 0
|
||||
String offsetLabel = distPt < 1.0 ? "紧贴" : (SlideUtils.fmt1(distPt) + "pt 偏移");
|
||||
|
||||
return coverageLabel + " " + offsetLabel;
|
||||
}
|
||||
|
||||
// 形状效果 发光
|
||||
public static String getShapeGlow(Pic sp, CTSlideTiming timing) {
|
||||
if (sp == null || sp.getSpPr() == null) return null;
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
CTEffectList eff = spPr.getEffectLst();
|
||||
return (eff != null) ? eff.getGlow().toString() : null;
|
||||
}
|
||||
|
||||
// 形状效果 发光-效果
|
||||
public static String getShapeGlowEffect(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
// 形状效果 柔化边缘-绘制
|
||||
public static String getShapeSoftEdgesDraw(Pic sp, CTSlideTiming timing) {
|
||||
CTSoftEdgesEffect se = getSoftEdge(sp);
|
||||
return (se != null) ? "是" : "否";
|
||||
}
|
||||
|
||||
// 形状效果 柔化边缘→大小
|
||||
public static String getShapeSoftEdgesSize(Pic sp, CTSlideTiming timing) {
|
||||
CTSoftEdgesEffect se = getSoftEdge(sp);
|
||||
return (se != null) ? String.valueOf(se.getRad()) : null;
|
||||
}
|
||||
// 形状效果 三维效果→绘制
|
||||
public static String getShape3DEffectDraw(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
// 形状效果 三维格式→效果
|
||||
public static String getShape3DEffect(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
// 形状效果 三维旋转→效果
|
||||
public static String getShape3DRotation(Pic sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
}
|
||||
|
||||
/* —— 内部:spPr/effectLst/softEdge —— */
|
||||
private static CTSoftEdgesEffect getSoftEdge(Pic shape) {
|
||||
if (shape == null || shape.getSpPr() == null) return null;
|
||||
CTShapeProperties spPr = shape.getSpPr();
|
||||
CTEffectList eff = spPr.getEffectLst();
|
||||
return (eff != null) ? eff.getSoftEdge() : null;
|
||||
// 说明:极少数文件把效果写在 effectDag;若拿不到再补 effectDag 解析。
|
||||
}
|
||||
|
||||
|
||||
// 动画 效果
|
||||
// public static String getAnimation(org.pptx4j.pml.Shape sp, CTSlideTiming timing) throws JAXBException, XPathBinderAssociationIsPartialException {
|
||||
// // 获取形状中的ID
|
||||
// if (sp == null) return "无动画";
|
||||
// long spId = sp.getNvSpPr().getCNvPr().getId();
|
||||
// // 获取动画效果
|
||||
// CTTimeNodeList timeNodeList = timing.getTnLst();
|
||||
// for (Object obj : timeNodeList.getParOrSeqOrExcl()) {
|
||||
// if (obj instanceof CTTLTimeNodeParallel) {
|
||||
// CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj;
|
||||
// CTTLCommonTimeNodeData node = par.getCTn();
|
||||
// if (node.getChildTnLst().getParOrSeqOrExcl() != null) {
|
||||
// CTTimeNodeList childTimeNodeList = node.getChildTnLst();
|
||||
// for (Object object : childTimeNodeList.getParOrSeqOrExcl()) {
|
||||
// if (object instanceof CTTLSetBehavior) {
|
||||
// if (((CTTLSetBehavior) object).getCBhvr() != null) {
|
||||
// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt() != null) {
|
||||
// // 检查目标形状ID是否匹配
|
||||
// if (((CTTLSetBehavior) object).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) {
|
||||
// // 返回动画效果
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (object instanceof CTTLTimeNodeSequence) {
|
||||
// CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object;
|
||||
// CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst();
|
||||
// for (Object seqObjs : seqNodeList.getParOrSeqOrExcl()) {
|
||||
// if (seqObjs instanceof CTTLSetBehavior) {
|
||||
// if (((CTTLSetBehavior) seqObjs).getCBhvr() != null) {
|
||||
// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt() != null) {
|
||||
// // 检查目标形状ID是否匹配
|
||||
// if (((CTTLSetBehavior) seqObjs).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) {
|
||||
// // 返回动画效果
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (seqObjs instanceof CTTLTimeNodeSequence) {
|
||||
// CTTLTimeNodeSequence seqObj = (CTTLTimeNodeSequence) seqObjs;
|
||||
// CTTimeNodeList seqNodeLists = seqObj.getCTn().getChildTnLst();
|
||||
// for (Object seqObjss : seqNodeLists.getParOrSeqOrExcl()) {
|
||||
// if (seqObjss instanceof CTTLSetBehavior) {
|
||||
// if (((CTTLSetBehavior) seqObjss).getCBhvr() != null) {
|
||||
// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt() != null) {
|
||||
// // 检查目标形状ID是否匹配
|
||||
// if (((CTTLSetBehavior) seqObjss).getCBhvr().getTgtEl().getSpTgt().getSpid().equals(String.valueOf(spId))) {
|
||||
// // 返回动画效果
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (node.getDur() != null && !"indefinite".equals(node.getDur())) {
|
||||
// return node.getDur();
|
||||
// }
|
||||
// System.out.println(par.getCTn());
|
||||
// }
|
||||
// }
|
||||
// return "待开发";
|
||||
// }
|
||||
|
||||
public static String getAnimation(Pic sp, CTSlideTiming timing) {
|
||||
if (sp == null || timing == null || timing.getTnLst() == null) return "无动画";
|
||||
final String spId = String.valueOf(sp.getNvPicPr().getCNvPr().getId());
|
||||
CTTimeNodeList root = timing.getTnLst();
|
||||
|
||||
// 先找进入类(effect)
|
||||
String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true);
|
||||
if (entrance != null) return entrance;
|
||||
|
||||
// 再找其它类型(包括 CTTLAnimateBehavior 的 ppt_x/ppt_y)
|
||||
String any = findFirstBehavior(root, spId, /*preferEntrance=*/false);
|
||||
return any != null ? any : "无动画";
|
||||
}
|
||||
|
||||
/* 递归遍历所有 time nodes,返回首个命中的行为字符串 */
|
||||
private static String findFirstBehavior(CTTimeNodeList list, String spId, boolean preferEntrance) {
|
||||
if (list == null || list.getParOrSeqOrExcl() == null) return null;
|
||||
|
||||
for (Object raw : list.getParOrSeqOrExcl()) {
|
||||
Object node = unwrap(raw);
|
||||
|
||||
// 1) 直接是某种行为(Behavior)
|
||||
String desc = tryDescribeIfBehavior(node, spId, preferEntrance);
|
||||
if (desc != null) return desc;
|
||||
|
||||
// 2) par / seq / excl,进入子列表递归
|
||||
CTTimeNodeList child = getChildList(node);
|
||||
if (child != null) {
|
||||
String got = findFirstBehavior(child, spId, preferEntrance);
|
||||
if (got != null) return got;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* 如果对象是某种 Behavior,且目标命中当前 spId,则返回描述;否则返回 null */
|
||||
private static String tryDescribeIfBehavior(Object obj, String spId, boolean preferEntrance) {
|
||||
// 统一 unwrap
|
||||
obj = unwrap(obj);
|
||||
|
||||
// --- 进入类效果(Effect)---
|
||||
if (obj instanceof CTTLAnimateEffectBehavior) {
|
||||
CTTLAnimateEffectBehavior b = (CTTLAnimateEffectBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) {
|
||||
String filter = safe(b.getFilter()); // 如 "fade"、"strips(downRight)" 等
|
||||
return filter.isEmpty() ? "进入效果" : ("进入效果:" + filter);
|
||||
}
|
||||
}
|
||||
|
||||
// 非优先阶段:其它类型
|
||||
if (!preferEntrance) {
|
||||
// 通用 Animate (B 动画关键:ppt_x / ppt_y)
|
||||
if (obj instanceof CTTLAnimateBehavior) {
|
||||
CTTLAnimateBehavior b = (CTTLAnimateBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) {
|
||||
String attr = firstAttrName(b.getCBhvr());
|
||||
if ("ppt_x".equalsIgnoreCase(attr)) return "位置动画:X(ppt_x)";
|
||||
if ("ppt_y".equalsIgnoreCase(attr)) return "位置动画:Y(ppt_y)";
|
||||
return attr.isEmpty() ? "数值动画(Animate)" : ("数值动画(" + attr + ")");
|
||||
}
|
||||
}
|
||||
if (obj instanceof CTTLAnimateMotionBehavior) {
|
||||
CTTLAnimateMotionBehavior b = (CTTLAnimateMotionBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) return "路径动画(Motion)";
|
||||
}
|
||||
if (obj instanceof CTTLAnimateRotationBehavior) {
|
||||
CTTLAnimateRotationBehavior b = (CTTLAnimateRotationBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) return "旋转动画";
|
||||
}
|
||||
if (obj instanceof CTTLAnimateScaleBehavior) {
|
||||
CTTLAnimateScaleBehavior b = (CTTLAnimateScaleBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) return "缩放动画";
|
||||
}
|
||||
if (obj instanceof CTTLSetBehavior) {
|
||||
CTTLSetBehavior b = (CTTLSetBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) {
|
||||
String to = (b.getTo()!=null && b.getTo().getStrVal()!=null) ? safe(b.getTo().getStrVal().getVal()) : "";
|
||||
String attr = firstAttrName(b.getCBhvr());
|
||||
if (!attr.isEmpty() || !to.isEmpty()) {
|
||||
return "属性设置(Set):" + (attr.isEmpty() ? "" : attr) + (to.isEmpty() ? "" : (" -> " + to));
|
||||
}
|
||||
return "属性设置(Set)";
|
||||
}
|
||||
}
|
||||
if (obj instanceof CTTLAnimateColorBehavior) {
|
||||
CTTLAnimateColorBehavior b = (CTTLAnimateColorBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) return "颜色动画";
|
||||
}
|
||||
if (obj instanceof CTTLCommandBehavior) {
|
||||
CTTLCommandBehavior b = (CTTLCommandBehavior) obj;
|
||||
if (isTarget(b.getCBhvr(), spId)) return "命令动画(Command)";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* 提取并返回子节点列表(par/seq/excl 三种) */
|
||||
private static CTTimeNodeList getChildList(Object node) {
|
||||
node = unwrap(node);
|
||||
if (node instanceof CTTLTimeNodeParallel) {
|
||||
CTTLTimeNodeParallel n = (CTTLTimeNodeParallel) node;
|
||||
return n.getCTn() != null ? n.getCTn().getChildTnLst() : null;
|
||||
}
|
||||
if (node instanceof CTTLTimeNodeSequence) {
|
||||
CTTLTimeNodeSequence n = (CTTLTimeNodeSequence) node;
|
||||
return n.getCTn() != null ? n.getCTn().getChildTnLst() : null;
|
||||
}
|
||||
if (node instanceof CTTLTimeNodeExclusive) {
|
||||
CTTLTimeNodeExclusive n = (CTTLTimeNodeExclusive) node;
|
||||
return n.getCTn() != null ? n.getCTn().getChildTnLst() : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* 判断行为的目标是否命中当前形状 spId */
|
||||
private static boolean isTarget(CTTLCommonBehaviorData cb, String spId) {
|
||||
if (cb == null || cb.getTgtEl() == null || cb.getTgtEl().getSpTgt() == null) return false;
|
||||
String tgt = cb.getTgtEl().getSpTgt().getSpid();
|
||||
return String.valueOf(spId).equals(tgt);
|
||||
}
|
||||
|
||||
/* 读第一个 attrName(如 ppt_x / ppt_y / style.visibility ...)*/
|
||||
private static String firstAttrName(CTTLCommonBehaviorData cb) {
|
||||
if (cb == null || cb.getAttrNameLst() == null) return "";
|
||||
java.util.List<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 "";
|
||||
}
|
||||
|
||||
/// 形状动作,鼠标单机-动作
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
// 幻灯片切换 切换声音
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 中的 sp(shape)元素
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user