From aa15ce65b22432816849dd8630d230f3a196bc69 Mon Sep 17 00:00:00 2001 From: dlaren Date: Sat, 16 Aug 2025 17:53:27 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91=20?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E7=89=88PPTX=E5=BE=97=E5=88=A4=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutoForWinEdgeSettingServiceImpl.java | 2 - .../JudgementWpsExcelServiceImpl.java | 5 - .../exam/service/wpsexcel/WpsExcelUtils.java | 1 - .../wpspptx/DeclareNamespaceForPPT.java | 40 - .../service/wpspptx/JudgementConvert.java | 161 -- .../exam/service/wpspptx/JudgementReqVo.java | 39 - .../exam/service/wpspptx/JudgementWpsPPT.java | 303 ---- .../wpspptx/JudgementWpsPptxServiceImpl.java | 31 +- .../service/wpspptx/WpsPptxJudgementDto.java | 14 - .../service/wpspptx/XmlRecursiveFinder.java | 39 - .../service/wpspptx/pptx4j/ShapePage.java | 1545 +++++++++++++++++ .../exam/service/wpspptx/pptx4j/ShapePic.java | 859 +++++++++ .../wpspptx/pptx4j/SlideAllSetting.java | 17 + .../wpspptx/pptx4j/SlideConversion.java | 97 ++ .../service/wpspptx/pptx4j/SlideMaster.java | 128 ++ .../service/wpspptx/pptx4j/SlideSetting.java | 154 ++ .../service/wpspptx/pptx4j/SlideUtils.java | 215 +++ .../wpspptx/pptx4j/vo/JudgementSlidesVO.java | 19 + .../wpspptx/pptx4j/vo/SlideDataInfoVO.java | 19 + .../wpspptx/pptx4j/vo/WpsSlideInfoVo.java | 24 + 20 files changed, 3093 insertions(+), 619 deletions(-) delete mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java delete mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java delete mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java delete mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java delete mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java delete mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePage.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePic.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideAllSetting.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideConversion.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideMaster.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideSetting.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideUtils.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/JudgementSlidesVO.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/SlideDataInfoVO.java create mode 100644 src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/WpsSlideInfoVo.java diff --git a/src/main/java/com/example/exam/exam/service/autoForWinEdgeSetting/AutoForWinEdgeSettingServiceImpl.java b/src/main/java/com/example/exam/exam/service/autoForWinEdgeSetting/AutoForWinEdgeSettingServiceImpl.java index 0dcd9d9..f5a7ca2 100644 --- a/src/main/java/com/example/exam/exam/service/autoForWinEdgeSetting/AutoForWinEdgeSettingServiceImpl.java +++ b/src/main/java/com/example/exam/exam/service/autoForWinEdgeSetting/AutoForWinEdgeSettingServiceImpl.java @@ -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; diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java index 8dc21ec..0e459a6 100644 --- a/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/JudgementWpsExcelServiceImpl.java @@ -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; diff --git a/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java b/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java index 3abb770..7ea5812 100644 --- a/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java +++ b/src/main/java/com/example/exam/exam/service/wpsexcel/WpsExcelUtils.java @@ -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; diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java b/src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java deleted file mode 100644 index 44e4ec2..0000000 --- a/src/main/java/com/example/exam/exam/service/wpspptx/DeclareNamespaceForPPT.java +++ /dev/null @@ -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 ""; - } -} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java deleted file mode 100644 index 4c51eef..0000000 --- a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementConvert.java +++ /dev/null @@ -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 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 内 - } -} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java deleted file mode 100644 index d19a27f..0000000 --- a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementReqVo.java +++ /dev/null @@ -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; - -} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java deleted file mode 100644 index e7d7f0c..0000000 --- a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPPT.java +++ /dev/null @@ -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 getValues(List 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 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 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 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 setData(List wpsPptxJudgementDtoList, - String chineseName, - String englishName) { - WpsPptxJudgementDto judgementDto = new WpsPptxJudgementDto(); - judgementDto.setContentIn(chineseName); - judgementDto.setContent(englishName); - judgementDto.setScoreRate("1"); - wpsPptxJudgementDtoList.add(judgementDto); - return wpsPptxJudgementDtoList; - } -} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java index 25960ac..9b62d16 100644 --- a/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java +++ b/src/main/java/com/example/exam/exam/service/wpspptx/JudgementWpsPptxServiceImpl.java @@ -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 judgementReq = new ArrayList<>(); + List judgementReq = new ArrayList<>(); // 3、获取答案得组成 List 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 judgementDtos = JudgementWpsPPT.getValues(judgementReq); + List 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; // 得分 根据权重进行得分 每个选项分值 = 总分 / 总权重 diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java b/src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java deleted file mode 100644 index 051f9df..0000000 --- a/src/main/java/com/example/exam/exam/service/wpspptx/WpsPptxJudgementDto.java +++ /dev/null @@ -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; -} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java b/src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java deleted file mode 100644 index de6835d..0000000 --- a/src/main/java/com/example/exam/exam/service/wpspptx/XmlRecursiveFinder.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePage.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePage.java new file mode 100644 index 0000000..23fd46d --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePage.java @@ -0,0 +1,1545 @@ +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 ShapePage { + + // 大小 - 高度 + public static String getShapeSizeHeight(Shape 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(Shape 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(Shape 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(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 位置 水平位置 + public static String getShapePositionLeft(Shape 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(Shape 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 getShapeText(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状填充 填充方式 + public static String getShapeFillType(Shape sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + return SlideUtils.getFillType(spPr); + } + + // 形状填充 纯色填充-颜色 + public static String getShapeFillSolidColor(Shape sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getSolidFill() != null) { + return SlideUtils.getSolidFillColor(spPr.getSolidFill()); + } + return ""; + } + + // 形状填充 渐变填充-效果 + public static String getShapeFillGradientType(Shape sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getGradFill() != null) { + System.out.println("形状: " + sp.getNvSpPr().getCNvPr().getName()); + return SlideUtils.printGradientFill(spPr.getGradFill()); + } + return ""; + } + + // 形状填充 图片填充-效果 + public static String getShapeFillPictureType(Shape sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + + if (spPr != null && spPr.getBlipFill() != null) { + System.out.println("形状: " + sp.getNvSpPr().getCNvPr().getName()); + return SlideUtils.printImageFill(spPr.getBlipFill()); + } + return ""; + } + + // 形状填充 纹理填充-效果 + public static String getShapeFillTextureType(Shape sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getPattFill() != null) { + return SlideUtils.printPatternFill(spPr.getPattFill()); + } + return ""; + } + + // 形状填充 图案填充-效果 + public static String getShapeFillPatternType(Shape sp, CTSlideTiming timing) { + CTShapeProperties spPr = sp.getSpPr(); + if (spPr != null && spPr.getPattFill() != null) { + return spPr.getPattFill().getPrst().value(); + } + return ""; + } + + // 形状填充 幻灯片背景填充-效果 + public static String getShapeFillSlideBackgroundType(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状填充 填充透明度 + public static String getShapeFillOpacity(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状线条 绘制 + public static String getShapeLineDraw(Shape 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.getNvSpPr().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(Shape 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(Shape 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(Shape sp, CTSlideTiming timing) { + + return ""; + } + + // 形状线条 复合类型 + public static String getStrokeCompound(Shape 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(Shape 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(Shape 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(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状线条 结尾箭头类型 + public static String getShapeLineEndArrow(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状线条 结尾箭头粗细 + public static String getShapeLineEndArrowWidth(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + + // 形状效果 阴影-绘制 + public static String getShapeShadowDraw(Shape 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(Shape 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(Shape 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(Shape sp, CTSlideTiming timing) { + CTReflectionEffect r = SlideUtils.getReflection(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(Shape 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(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 形状效果 柔化边缘-绘制 + public static String getShapeSoftEdgesDraw(Shape sp, CTSlideTiming timing) { + CTSoftEdgesEffect se = getSoftEdge(sp); + return (se != null) ? "是" : "否"; + } + + // 形状效果 柔化边缘→大小 + public static String getShapeSoftEdgesSize(Shape sp, CTSlideTiming timing) { + CTSoftEdgesEffect se = getSoftEdge(sp); + return (se != null) ? String.valueOf(se.getRad()) : null; + } + // 形状效果 三维效果→绘制 + public static String getShape3DEffectDraw(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状效果 三维格式→效果 + public static String getShape3DEffect(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 形状效果 三维旋转→效果 + public static String getShape3DRotation(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + /* —— 内部:spPr/effectLst/softEdge —— */ + private static CTSoftEdgesEffect getSoftEdge(Shape 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(Shape sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + final String spId = String.valueOf(sp.getNvSpPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找进入类(effect) + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 再找其它类型(包括 CTTLAnimateBehavior 的 ppt_x/ppt_y) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + /* 递归遍历所有 time nodes,返回首个命中的行为字符串 */ + private static String findFirstBehavior(CTTimeNodeList list, String spId, boolean preferEntrance) { + if (list == null || list.getParOrSeqOrExcl() == null) return null; + + for (Object raw : list.getParOrSeqOrExcl()) { + Object node = unwrap(raw); + + // 1) 直接是某种行为(Behavior) + String desc = tryDescribeIfBehavior(node, spId, preferEntrance); + if (desc != null) return desc; + + // 2) par / seq / excl,进入子列表递归 + CTTimeNodeList child = getChildList(node); + if (child != null) { + String got = findFirstBehavior(child, spId, preferEntrance); + if (got != null) return got; + } + } + return null; + } + + /* 如果对象是某种 Behavior,且目标命中当前 spId,则返回描述;否则返回 null */ + private static String tryDescribeIfBehavior(Object obj, String spId, boolean preferEntrance) { + // 统一 unwrap + obj = unwrap(obj); + + // --- 进入类效果(Effect)--- + if (obj instanceof CTTLAnimateEffectBehavior) { + CTTLAnimateEffectBehavior b = (CTTLAnimateEffectBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String filter = safe(b.getFilter()); // 如 "fade"、"strips(downRight)" 等 + return filter.isEmpty() ? "进入效果" : ("进入效果:" + filter); + } + } + + // 非优先阶段:其它类型 + if (!preferEntrance) { + // 通用 Animate (B 动画关键:ppt_x / ppt_y) + if (obj instanceof CTTLAnimateBehavior) { + CTTLAnimateBehavior b = (CTTLAnimateBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String attr = firstAttrName(b.getCBhvr()); + if ("ppt_x".equalsIgnoreCase(attr)) return "位置动画:X(ppt_x)"; + if ("ppt_y".equalsIgnoreCase(attr)) return "位置动画:Y(ppt_y)"; + return attr.isEmpty() ? "数值动画(Animate)" : ("数值动画(" + attr + ")"); + } + } + if (obj instanceof CTTLAnimateMotionBehavior) { + CTTLAnimateMotionBehavior b = (CTTLAnimateMotionBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "路径动画(Motion)"; + } + if (obj instanceof CTTLAnimateRotationBehavior) { + CTTLAnimateRotationBehavior b = (CTTLAnimateRotationBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "旋转动画"; + } + if (obj instanceof CTTLAnimateScaleBehavior) { + CTTLAnimateScaleBehavior b = (CTTLAnimateScaleBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "缩放动画"; + } + if (obj instanceof CTTLSetBehavior) { + CTTLSetBehavior b = (CTTLSetBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) { + String to = (b.getTo()!=null && b.getTo().getStrVal()!=null) ? safe(b.getTo().getStrVal().getVal()) : ""; + String attr = firstAttrName(b.getCBhvr()); + if (!attr.isEmpty() || !to.isEmpty()) { + return "属性设置(Set):" + (attr.isEmpty() ? "" : attr) + (to.isEmpty() ? "" : (" -> " + to)); + } + return "属性设置(Set)"; + } + } + if (obj instanceof CTTLAnimateColorBehavior) { + CTTLAnimateColorBehavior b = (CTTLAnimateColorBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "颜色动画"; + } + if (obj instanceof CTTLCommandBehavior) { + CTTLCommandBehavior b = (CTTLCommandBehavior) obj; + if (isTarget(b.getCBhvr(), spId)) return "命令动画(Command)"; + } + } + return null; + } + + /* 提取并返回子节点列表(par/seq/excl 三种) */ + private static CTTimeNodeList getChildList(Object node) { + node = unwrap(node); + if (node instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel n = (CTTLTimeNodeParallel) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + if (node instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence n = (CTTLTimeNodeSequence) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + if (node instanceof CTTLTimeNodeExclusive) { + CTTLTimeNodeExclusive n = (CTTLTimeNodeExclusive) node; + return n.getCTn() != null ? n.getCTn().getChildTnLst() : null; + } + return null; + } + + /* 判断行为的目标是否命中当前形状 spId */ + private static boolean isTarget(CTTLCommonBehaviorData cb, String spId) { + if (cb == null || cb.getTgtEl() == null || cb.getTgtEl().getSpTgt() == null) return false; + String tgt = cb.getTgtEl().getSpTgt().getSpid(); + return String.valueOf(spId).equals(tgt); + } + + /* 读第一个 attrName(如 ppt_x / ppt_y / style.visibility ...)*/ + private static String firstAttrName(CTTLCommonBehaviorData cb) { + if (cb == null || cb.getAttrNameLst() == null) return ""; + java.util.List names = cb.getAttrNameLst().getAttrName(); + return (names != null && !names.isEmpty()) ? safe(names.get(0)) : ""; + } + + /* 统一解包 JAXBElement */ + private static Object unwrap(Object o) { + return (o instanceof JAXBElement) ? ((JAXBElement) o).getValue() : o; + } + + /* 安全字符串 */ + private static String safe(String s) { + return s == null ? "" : s.trim(); + } + + + // 动画-方向 + public static String getAnimateDirection(Shape sp, CTSlideTiming timing) { + String dir = getAnimation(sp, timing); + if (dir.equals("downRight")) { + return "右下"; + } + if (dir.equals("downLeft")) { + return "左下"; + } + if (dir.equals("upRight")) { + return "右上"; + } + if (dir.equals("upLeft")) { + return "左上"; + } + if (dir.equals("right")) { + return "向右"; + } + if (dir.equals("left")) { + return "向左"; + } + if (dir.equals("down")) { + return "向下"; + } + if (dir.equals("up")) { + return "向上"; + } + if (dir.equals("fromTop")) { + return "从顶部"; + } + if (dir.equals("fromBottom")) { + return "从底部"; + } + if (dir.equals("fromLeft")) { + return "从左侧"; + } + if (dir.equals("fromRight")) { + return "从右侧"; + } + return ""; + } + + // 动画 触发方式 + public static String getAnimateTrigger(Shape sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvSpPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.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(Shape sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + + final String spId = String.valueOf(sp.getNvSpPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找“进入类效果(effect)” + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 找不到进入类,再找其它类型(运动、旋转、缩放、Set 等) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + // 动画 延迟 + public static String getAnimationDelay(Shape sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvSpPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return ""; + } + + + + /// 形状动作,鼠标单机-动作 + + + /// 文本框 文本 + public static String getTextBoxText(Shape sp, CTSlideTiming timing) { + StringBuilder textContent = new StringBuilder(); + // 检查形状是否有文本框 + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun textRun) { + if (textRun.getT() != null) { + textContent.append(textRun.getT()); + } + } + } + textContent.append("\n"); // 段落间添加换行 + } + } + + return textContent.toString().trim(); + } + + /// 文本框 文本-垂直对齐 + public static String getTextBoxVerticalAlign(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBodyProperties textBody = sp.getTxBody().getBodyPr(); + STTextAnchoringType anchor = textBody.getAnchor(); + // 遍历所有段落 + if (anchor.value().equals("t")) { + return "顶部对齐"; + } else if (anchor.value().equals("ctr")) { + return "居中对齐"; + } else if (anchor.value().equals("b")) { + return "底部对齐"; + } else if (anchor.value().equals("just")) { + return "两端对齐"; + } else if (anchor.value().equals("dist")) { + return "分散对齐"; + } + } + return ""; + } + + /// 文本框 文本-对齐 + public static String getTextBoxAlign(Shape sp, CTSlideTiming timing) { + STTextVerticalType stTextVerticalType = sp.getTxBody().getBodyPr().getVert(); + if (stTextVerticalType != null) { + return stTextVerticalType.value(); + } else { + return "无垂直对齐设置"; + } + } + + /// 文本框 文本-大小调整方式 + public static String getTextBoxSizeAdjust(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() == null || sp.getTxBody().getBodyPr() == null) { + return "noAutofit"; // 默认值 + } + CTTextBodyProperties bodyPr = sp.getTxBody().getBodyPr(); + + if (bodyPr.getNoAutofit() != null) { + return "noAutofit"; + } else if (bodyPr.getNormAutofit() != null) { + return "normAutofit"; + } else if (bodyPr.getSpAutoFit() != null) { + return "spAutofit"; + } + + return "noAutofit"; // 默认值 + } + + /// 文本框 上边距 + public static String getTextBoxTopMargin(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null && sp.getTxBody().getBodyPr() != null) { + CTTextBodyProperties bodyPr = sp.getTxBody().getBodyPr(); + Integer top = bodyPr.getTIns(); + return top.toString(); + } + return ""; + } + + /// 文本框 下边距 + public static String getTextBoxBottomMargin(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null && sp.getTxBody().getBodyPr() != null) { + CTTextBodyProperties bodyPr = sp.getTxBody().getBodyPr(); + Integer bottom = bodyPr.getBIns(); + return bottom.toString(); + } + return ""; + } + + /// 文本框 左边距 + public static String getTextBoxLeftMargin(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null && sp.getTxBody().getBodyPr() != null) { + CTTextBodyProperties bodyPr = sp.getTxBody().getBodyPr(); + Integer left = bodyPr.getLIns(); + return left.toString(); + } + return ""; + } + + /// 文本框 右边距 + public static String getTextBoxRightMargin(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null && sp.getTxBody().getBodyPr() != null) { + CTTextBodyProperties bodyPr = sp.getTxBody().getBodyPr(); + Integer right = bodyPr.getRIns(); + return right.toString(); + } + return ""; + } + + /// 文本框 自动换行 + public static String getTextBoxAutoWrap(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null + && sp.getTxBody().getBodyPr() != null) { + STTextWrappingType stTextWrappingType = sp.getTxBody().getBodyPr().getWrap(); + return stTextWrappingType.value(); + } + return ""; + } + + /// 字体 西文字体 + public static String getTextBoxFontFamily(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + return ctRegularTextRun.getRPr().getLatin().getTypeface(); + } + } + } + return ""; + } + + // 字体 中文字体 + public static String getTextBoxFontFamilyChinese(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + return ctRegularTextRun.getRPr().getEa().getTypeface(); + } + } + } + return ""; + } + + /// 字体 字体大小 + public static String getTextBoxFontSize(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + return String.valueOf(ctRegularTextRun.getRPr().getSz()); + } + } + } + return ""; + } + + /// 字体 加粗 + public static String getTextBoxFontBold(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + if (ctRegularTextRun.getRPr().isB()) { + return "是"; + } else { + return "否"; + } + } + } + } + return ""; + } + + /// 字体 倾斜 + public static String getTextBoxFontItalic(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + if (ctRegularTextRun.getRPr().isI()) { + return "是"; + } else { + return "否"; + } + } + } + } + return ""; + } + + /// 字体 字体颜色 + public static String getTextBoxFontColor(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + return ctRegularTextRun.getRPr().getSolidFill().getSrgbClr().getVal(); + } + } + } + return ""; + } + + /// 字体 下划线线型 + public static String getTextBoxFontUnderline(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + // 遍历段落中的所有文本运行 + Object obj = paragraph.getEGTextRun().get(0); + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + return ctRegularTextRun.getRPr().getU().value(); + } + } + } + return ""; + } + + /// 字体 下划线颜色 + public static String getTextBoxFontUnderlineColor(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + for (CTTextParagraph paragraph : textBody.getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + if (rPr != null && rPr.getUFill() != null && rPr.getUFill().getSolidFill() != null) { + return rPr.getUFill().getSolidFill().getSrgbClr().getVal(); + } + } + } + } + } + return ""; + } + + /// 字体 删除线 + public static String getTextBoxFontStrike(Shape sp, CTSlideTiming timing) { + // 获取文本框字体是否有删除线 + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + for (CTTextParagraph paragraph : textBody.getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + if (rPr != null && rPr.getStrike() != null) { + return "是"; + } + } + } + } + } + return "否"; + } + + /// 字体 双删除线 + public static String getTextBoxFontDoubleStrike(Shape sp, CTSlideTiming timing) { + + return "待开发"; + } + + /// 字体 上标 + public static String getTextBoxFontSuperscript(Shape sp, CTSlideTiming timing) { + + return "待开发"; + } + + /// 字体 下标 + public static String getTextBoxFontSubscript(Shape sp, CTSlideTiming timing) { + + return "待开发"; + } + + /// 字体 偏移量 + public static String getTextBoxFontOffset(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + /// 字体 小型大写字母 + public static String getTextBoxFontSmallCaps(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + /// 字体 全部大写 + public static String getTextBoxFontAllCaps(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + /// 字体 等高字符 + public static String getTextBoxFontFixedWidth(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + /// 段落 对齐方式 + public static String getTextBoxParagraphAlign(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getFontAlgn() != null) { + return paragraph.getPPr().getFontAlgn().value(); + } + } + } + return ""; + } + + /// 段落 文本之前 + public static String getTextBoxParagraphIndentBefore(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getSpcBef() != null) { + return String.valueOf(paragraph.getPPr().getSpcBef().getSpcPts().getVal()); + } + } + } + return "0"; + } + + /// 段落 缩进→特殊 + public static String getTextBoxParagraphIndentSpecial(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getIndent() != null) { + return paragraph.getPPr().getIndent() > 0 ? "首行缩进" : "悬挂缩进"; + } + } + } + return "0"; + } + /// 段落 缩进→度量值 + public static String getTextBoxParagraphIndentMeasure(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getIndent() != null) { + return paragraph.getPPr().getIndent() / 12700.0 + " pt"; // EMU 转 pt + } + } + } + return "0"; + } + /// 段落 间距→段前 + public static String getTextBoxParagraphSpaceBefore(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getSpcBef() != null) { + return paragraph.getPPr().getSpcBef().getSpcPts().getVal() / 100 + " pt"; // EMU 转 pt + } + } + } + return ""; + } + /// 段落 间距→段后 + public static String getTextBoxParagraphSpaceAfter(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getSpcBef() != null) { + return paragraph.getPPr().getSpcAft().getSpcPts().getVal() / 100 + " pt"; // EMU 转 pt + } + } + } + return ""; + } + /// 段落 间距→行距→类型 + public static String getTextBoxParagraphSpaceLine(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getLnSpc().getSpcPct() != null) { + return "百分比行距"; // EMU 转 pt + } else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) { + return "固定行距"; // EMU 转 pt + } + + } + } + return ""; + } + /// 段落 间距→行距→设置值 + public static String getTextBoxParagraphSpaceLineValue(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + // 遍历所有段落 + for (CTTextParagraph paragraph : textBody.getP()) { + if (paragraph.getPPr().getLnSpc().getSpcPct() != null) { + return paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt + } else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) { + return paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 10 + "磅固定行距"; // EMU 转 pt + } + + } + } + return ""; + } + /// 段落 项目符号和编号→类型 + public static String getTextBoxParagraphSymbol(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + /// 文本填充 填充方式 + public static String getTextBoxParagraphFillType(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + for (CTTextParagraph paragraph : textBody.getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + if (rPr.getSolidFill() != null) { + return "纯色填充"; + } + if (rPr.getGradFill() != null) { + return "渐变填充"; + } + if (rPr.getBlipFill() != null) { + return "图片填充"; + } + if (rPr.getPattFill() != null) { + return "图案填充"; + } + if (rPr.getAltLang() != null) { + return "纹理填充"; + } + if (rPr.getNoFill() != null) { + return "无填充"; + } + } + } + } + } + return ""; + } + /// 文本填充 纯色填充→颜色 + public static String getTextBoxParagraphFillColor(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + CTTextBody textBody = sp.getTxBody(); + for (CTTextParagraph paragraph : textBody.getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + if (rPr.getSolidFill() != null) { + if (rPr.getSolidFill().getSrgbClr() != null) { + return rPr.getSolidFill().getSrgbClr().getVal(); + } else if (rPr.getSolidFill().getSchemeClr() != null) { + return rPr.getSolidFill().getSchemeClr().getVal().value(); + } else if (rPr.getSolidFill().getSysClr() != null) { + return rPr.getSolidFill().getSysClr().getVal(); + } + return "暂未查询到"; + } + } + } + } + } + return ""; + } + /// 文本填充 渐变填充→效果 + public static String getTextBoxParagraphFillGradient(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + /// 文本填充 图片填充→效果 + public static String getTextBoxParagraphFillImage(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + /// 文本填充 纹理填充→效果 + public static String getTextBoxParagraphFillTexture(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + /// 文本填充 图案填充→效果 + public static String getTextBoxParagraphFillPattern(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + /// 文本填充 填充透明度 + public static String getTextBoxParagraphFillOpacity(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + /// 文本轮廓 颜色 + public static String getTextBoxParagraphOutlineColor(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTLineProperties ctLineProperties = rPr.getLn(); + return ctLineProperties.getSolidFill().getSrgbClr().getVal(); + } + } + } + } + + return ""; + } + /// 文本轮廓 透明度 + public static String getTextBoxParagraphOutlineOpacity(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTLineProperties ctLineProperties = rPr.getLn(); + CTSolidColorFillProperties fill = ctLineProperties.getSolidFill(); + if (fill == null) return "不透明"; + int alpha = 0; + if (fill.getSrgbClr() != null) { + alpha = Integer.parseInt(fill.getSrgbClr().getVal()); + } + else if (fill.getSchemeClr() != null) { + alpha = fill.getSchemeClr().getVal().ordinal(); + } + return String.valueOf((100000 - alpha) / 1000.0); + } + } + } + } + return ""; + } + /// 文本轮廓 宽度 + public static String getTextBoxParagraphOutlineWidth(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTLineProperties ctLineProperties = rPr.getLn(); + // 获取宽度(单位:EMU) + long widthEmu = ctLineProperties.getW(); + // 转换为磅(1磅≈12700EMU) + double widthPt = widthEmu / 12700.0; + return widthPt + "pt"; + } + } + } + } + return ""; + } + /// 文本轮廓 高度 + public static String getTextBoxParagraphOutlineHeight(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + // 获取字体大小(单位:百分之一磅) + int fontSize = rPr.getSz(); + double fontSizePt = fontSize / 100.0; + // 估算轮廓高度(通常为字体高度的10-15%) + double outlineHeightPt = fontSizePt * 0.12; // 12%估算 + return outlineHeightPt + "pt"; + } + } + } + } + return ""; + } + /// 文本轮廓 复合类型 + public static String getTextBoxParagraphOutlineType(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + /// 文本轮廓 短划线类型 + public static String getTextBoxParagraphOutlineDashType(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 文字效果 阴影→绘制 + public static String getTextBoxParagraphShadowDraw(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTEffectList ctEffectList = rPr.getEffectLst(); + if (ctEffectList.getInnerShdw() != null || ctEffectList.getOuterShdw() != null || ctEffectList.getPrstShdw() != null) { + return "是"; + } else { + return "否"; + } + } + } + } + } + return ""; + } + // 文字效果 阴影→预设 + // 文字效果 阴影→效果 + public static String getTextBoxParagraphShadowEffect(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 文字效果 倒影→绘制 + public static String getTextBoxParagraphReflectionDraw(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTEffectList ctEffectList = rPr.getEffectLst(); + if (ctEffectList != null && ctEffectList.getReflection() != null) { + return "是"; + } else { + return "否"; + } + } + } + } + } + return ""; + } + + + // 文字效果 倒影→预设 + // 文字效果 倒影→效果 + public static String getTextBoxParagraphReflectionEffect(Shape sp, CTSlideTiming timing) { + if (sp.getTxBody() != null) { + for (CTTextParagraph paragraph : sp.getTxBody().getP()) { + for (Object obj : paragraph.getEGTextRun()) { + if (obj instanceof CTRegularTextRun ctRegularTextRun) { + CTTextCharacterProperties rPr = ctRegularTextRun.getRPr(); + CTEffectList ctEffectList = rPr.getEffectLst(); + CTReflectionEffect ctReflectionEffect = ctEffectList.getReflection(); + return SlideUtils.getShadowLikeDescription(ctReflectionEffect); + } + } + } + } + return ""; + } + // 文字效果 发光→绘制 + public static String getTextBoxParagraphGlowDraw(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 文字效果 发光→效果 + public static String getTextBoxParagraphGlowEffect(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + // 文字效果 转换→弯曲 + public static String getTextBoxParagraphConvertCurve(Shape sp, CTSlideTiming timing) { + return "待开发"; + } + + // 文字效果 单机鼠标 + public static String getTextBoxParagraphMouseClick(Shape sp, CTSlideTiming timing) { + System.out.println(timing); + return "待开发"; + } + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePic.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePic.java new file mode 100644 index 0000000..6c81612 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/ShapePic.java @@ -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 names = cb.getAttrNameLst().getAttrName(); + return (names != null && !names.isEmpty()) ? safe(names.get(0)) : ""; + } + + /* 统一解包 JAXBElement */ + private static Object unwrap(Object o) { + return (o instanceof JAXBElement) ? ((JAXBElement) o).getValue() : o; + } + + /* 安全字符串 */ + private static String safe(String s) { + return s == null ? "" : s.trim(); + } + + + // 动画-方向 + public static String getAnimateDirection(Pic sp, CTSlideTiming timing) { + String dir = getAnimation(sp, timing); + if (dir.equals("downRight")) { + return "右下"; + } + if (dir.equals("downLeft")) { + return "左下"; + } + if (dir.equals("upRight")) { + return "右上"; + } + if (dir.equals("upLeft")) { + return "左上"; + } + if (dir.equals("right")) { + return "向右"; + } + if (dir.equals("left")) { + return "向左"; + } + if (dir.equals("down")) { + return "向下"; + } + if (dir.equals("up")) { + return "向上"; + } + if (dir.equals("fromTop")) { + return "从顶部"; + } + if (dir.equals("fromBottom")) { + return "从底部"; + } + if (dir.equals("fromLeft")) { + return "从左侧"; + } + if (dir.equals("fromRight")) { + return "从右侧"; + } + return ""; + } + + // 动画 触发方式 + public static String getAnimateTrigger(Pic sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvPicPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getEvt().value(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return ""; + } + // 动画 持续时间 + public static String getAnimationDuration(Pic sp, CTSlideTiming timing) { + if (sp == null || timing == null || timing.getTnLst() == null) return "无动画"; + + final String spId = String.valueOf(sp.getNvPicPr().getCNvPr().getId()); + CTTimeNodeList root = timing.getTnLst(); + + // 先找“进入类效果(effect)” + String entrance = findFirstBehavior(root, spId, /*preferEntrance=*/true); + if (entrance != null) return entrance; + + // 找不到进入类,再找其它类型(运动、旋转、缩放、Set 等) + String any = findFirstBehavior(root, spId, /*preferEntrance=*/false); + return any != null ? any : "无动画"; + } + + // 动画 延迟 + public static String getAnimationDelay(Pic sp, CTSlideTiming timing) { + // 获取形状中的ID + if (sp == null) return "无动画"; + long spId = sp.getNvPicPr().getCNvPr().getId(); + // 获取动画效果 + CTTimeNodeList timeNodeList = timing.getTnLst(); + for (Object obj : timeNodeList.getParOrSeqOrExcl()) { + if (obj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel par = (CTTLTimeNodeParallel) obj; + CTTLCommonTimeNodeData node = par.getCTn(); + if (node.getChildTnLst().getParOrSeqOrExcl() != null) { + CTTimeNodeList childTimeNodeList = node.getChildTnLst(); + for (Object object : childTimeNodeList.getParOrSeqOrExcl()) { + if (object instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seq = (CTTLTimeNodeSequence) object; + if (seq.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seq.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeList = seq.getCTn().getChildTnLst(); + for (Object seqObj : seqNodeList.getParOrSeqOrExcl()) { + if (seqObj instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqs = (CTTLTimeNodeSequence) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } else { + if (seqObj instanceof CTTLTimeNodeParallel) { + CTTLTimeNodeParallel seqs = (CTTLTimeNodeParallel) seqObj; + if (seqs.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqs.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } else { + CTTimeNodeList seqNodeLists = seqs.getCTn().getChildTnLst(); + for (Object seqObjs : seqNodeLists.getParOrSeqOrExcl()) { + if (seqObjs instanceof CTTLTimeNodeSequence) { + CTTLTimeNodeSequence seqss = (CTTLTimeNodeSequence) seqObjs; + if (seqss.getCTn().getStCondLst() != null) { + CTTLTimeConditionList stCondLst = seqss.getCTn().getStCondLst(); + for (CTTLTimeCondition o : stCondLst.getCond()) { + if (o.getEvt() != null) { + return o.getDelay(); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return ""; + } + + /// 形状动作,鼠标单机-动作 + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideAllSetting.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideAllSetting.java new file mode 100644 index 0000000..97c2930 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideAllSetting.java @@ -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"; + } + + // 幻灯片切换 切换声音 + + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideConversion.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideConversion.java new file mode 100644 index 0000000..4ee3755 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideConversion.java @@ -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 SlideDataInfos(MultipartFile file) throws Exception { + List 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 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); + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideMaster.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideMaster.java new file mode 100644 index 0000000..d03c417 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideMaster.java @@ -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 slideMaster(List wpsSlideInfoVos, File file) throws IOException, Docx4JException { +// File files = new File("E:\\Project\\Exam\\Software\\Temp\\1.pptx"); + List 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 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 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; + + } + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideSetting.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideSetting.java new file mode 100644 index 0000000..0856713 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideSetting.java @@ -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 shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + for (Object shapeObj : shapes) { + if (shapeObj instanceof Shape) { + Shape shape = (Shape) shapeObj; + if (shape.getNvSpPr().getCNvPr().getName().equals("日期")) { + return "包含"; + } + } + } + return ""; + } + // 页眉页脚 包含→幻灯片编号 + public static String getSlideHeaderFooterSlideNumber(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + for (Object shapeObj : shapes) { + if (shapeObj instanceof Shape) { + Shape shape = (Shape) shapeObj; + if (shape.getNvSpPr().getCNvPr().getName().equals("编号")) { + return "包含"; + } + } + } + return ""; + } + // 页眉页脚 包含→页脚 + public static String getSlideHeaderFooterFooter(SlidePart slidePart, PresentationMLPackage presentation) throws Docx4JException { + GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree(); + List shapes = spTree.getSpOrGrpSpOrGraphicFrame(); + for (Object shapeObj : shapes) { + if (shapeObj instanceof Shape) { + Shape shape = (Shape) shapeObj; + if (shape.getNvSpPr().getCNvPr().getName().equals("页脚")) { + return "包含"; + } + } + } + return ""; + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideUtils.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideUtils.java new file mode 100644 index 0000000..334faa3 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/SlideUtils.java @@ -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 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(); + } +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/JudgementSlidesVO.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/JudgementSlidesVO.java new file mode 100644 index 0000000..1e9e3ea --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/JudgementSlidesVO.java @@ -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; + +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/SlideDataInfoVO.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/SlideDataInfoVO.java new file mode 100644 index 0000000..b63c788 --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/SlideDataInfoVO.java @@ -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; +} diff --git a/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/WpsSlideInfoVo.java b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/WpsSlideInfoVo.java new file mode 100644 index 0000000..5d3665d --- /dev/null +++ b/src/main/java/com/example/exam/exam/service/wpspptx/pptx4j/vo/WpsSlideInfoVo.java @@ -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; +}