【新增】 使用pptx4j 获取ppt文件的考点(形状)

This commit is contained in:
dlaren
2025-08-13 20:46:01 +08:00
parent 677f0fc733
commit cd7dec1ab3
6 changed files with 431 additions and 24 deletions

View File

@@ -4,13 +4,18 @@ import lombok.Data;
@Data
public class WpsSlideInfoVo {
// 幻灯片位置(第几张幻灯片)
private String slideIndex;
// 形状位置只有在shape 形状(不包含图片) shapePic 形状图片
private String shapeIndex;
// 形状类型 slide 幻灯片设置 shape 形状(不包含图片) shapePic 形状图片 presentation 总幻灯片设置
private String type;
// 大类名称
private String firstName;
// 序号
private String index;
// 方法名称
private String function;

View File

@@ -19,7 +19,7 @@ public class JudgementWpsPptxServiceImpl implements JudgementWpsPptxService {
@Override
public List<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, MultipartFile file) throws Exception {
return SlideMaster.slideMaster(file);
return SlideMaster.slideMaster(wpsSlideInfoVos, file);
}
/**

View File

@@ -0,0 +1,247 @@
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
import org.docx4j.dml.*;
public class Shape {
// 大小 - 高度
public static String getShapeSizeHeight(org.pptx4j.pml.Shape sp) {
CTTransform2D xfrm = sp.getSpPr().getXfrm();
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈360000 EMU
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
double heightPt = emuToPt(ext.getCy());
return String.valueOf(heightPt);
}
// 大小 - 宽度
public static String getShapeSizeWidthPt(org.pptx4j.pml.Shape sp) {
CTTransform2D xfrm = sp.getSpPr().getXfrm();
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈360000 EMU
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
double widthPt = emuToPt(ext.getCx());
return String.valueOf(widthPt);
}
// 大小 - 锁定纵横比
public static String getShapeSizeLockAspectRatio(org.pptx4j.pml.Shape sp) {
CTTransform2D xfrm = sp.getSpPr().getXfrm();
// 获取位置和尺寸(单位 EMU1英寸=914400 EMU1cm≈360000 EMU
CTPositiveSize2D ext = xfrm.getExt(); // 尺寸
Boolean lockAspectRatio = null;
if (sp.getSpPr().getPrstGeom() != null &&
sp.getSpPr().getPrstGeom().getAvLst() != null) {
// 这个部分一般不会直接提供锁定横纵比,要看 spPr.getExtLst()
if (sp.getSpPr().getExtLst() != null) {
// pptx4j 没有直接的 getter要自己解析 XML
String xml = sp.getSpPr().getExtLst().toString();
if (xml.contains("lockAspectRatio=\"1\"") || xml.contains("lockAspectRatio=\"true\"")) {
lockAspectRatio = true;
} else if (xml.contains("lockAspectRatio=\"0\"") || xml.contains("lockAspectRatio=\"false\"")) {
lockAspectRatio = false;
}
}
}
return String.valueOf(lockAspectRatio);
}
// EMU 转 pt
private static double emuToPt(long emu) {
return emu / 12700.0;
}
// 位置 水平位置
public static String getShapePositionLeft(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp) {
CTShapeProperties spPr = sp.getSpPr();
return SlideUtils.getFillType(spPr);
}
// 形状填充 纯色填充-颜色
public static String getShapeFillSolidColor(org.pptx4j.pml.Shape sp) {
CTShapeProperties spPr = sp.getSpPr();
if (spPr != null && spPr.getSolidFill() != null) {
return SlideUtils.getSolidFillColor(spPr.getSolidFill());
}
return "";
}
// 形状填充 渐变填充-效果
public static String getShapeFillGradientType(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp){
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(org.pptx4j.pml.Shape sp) {
CTShapeProperties spPr = sp.getSpPr();
if (spPr != null && spPr.getPattFill() != null) {
return SlideUtils.printPatternFill(spPr.getPattFill());
}
return "";
}
// 形状填充 图案填充-效果
public static String getShapeFillPatternType(org.pptx4j.pml.Shape sp) {
CTShapeProperties spPr = sp.getSpPr();
if (spPr != null && spPr.getPattFill() != null) {
return spPr.getPattFill().getPrst().value();
}
return "";
}
// 形状填充 幻灯片背景填充-效果
public static String getShapeFillSlideBackgroundType(org.pptx4j.pml.Shape sp) {
return "";
}
// 形状填充 填充透明度
public static String getShapeFillOpacity(org.pptx4j.pml.Shape sp) {
return "";
}
// 形状线条 绘制
public static String getShapeLineDraw(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp) {
return "";
}
// 形状线条 复合类型
public static String getStrokeCompound(org.pptx4j.pml.Shape sp) {
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(org.pptx4j.pml.Shape sp) {
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";
}
}

View File

@@ -26,7 +26,7 @@ public class SlideConversion {
for (XSLFSlide slides : pptxXml.getSlides()){
index += 1;
String secondId = getStringRandom();
setSlideDataInfo(secondId, firstId, ""+index+"", "p:sld", String.valueOf(index), true, dataInfoVOS);
setSlideDataInfo(secondId, firstId, ""+index+"", "sld", String.valueOf(index), true, dataInfoVOS);
// 第三层
String thirdId = getStringRandom();
int indexCnvPr = 1;
@@ -39,7 +39,7 @@ public class SlideConversion {
if (cNvPrXml.toNextSelection()) {
String name = cNvPrXml.getTextValue();
String fourId = getStringRandom();
setSlideDataInfo(fourId, secondId, "形状"+indexCnvPr+"->"+name, "slides", String.valueOf(indexCnvPr), true, dataInfoVOS);
setSlideDataInfo(fourId, secondId, "形状"+indexCnvPr+"->"+name, "shape", String.valueOf(indexCnvPr), true, dataInfoVOS);
indexCnvPr += 1;
}
}
@@ -49,15 +49,15 @@ public class SlideConversion {
int indexCnvPrPic = 1;
while (picCursor.toNextSelection()) {
String fourId = getStringRandom();
setSlideDataInfo(fourId, secondId, "图像"+indexCnvPrPic+"->图像", "p:pic", String.valueOf(indexCnvPrPic), true, dataInfoVOS);
setSlideDataInfo(fourId, secondId, "图像"+indexCnvPrPic+"->图像", "shapePic", String.valueOf(indexCnvPrPic), true, dataInfoVOS);
indexCnvPrPic += 1;
}
}
// 设置
String firstIds = getStringRandom();
setSlideDataInfo(firstIds, "", "设置", "p:presentation", String.valueOf(0), false, dataInfoVOS);
setSlideDataInfo(firstIds, "", "设置", "presentation", String.valueOf(0), false, dataInfoVOS);
String secondIds = getStringRandom();
setSlideDataInfo(secondIds, firstIds, "幻灯片设置", "p:presentation", String.valueOf(0), true, dataInfoVOS);
setSlideDataInfo(secondIds, firstIds, "幻灯片设置", "presentation", String.valueOf(0), true, dataInfoVOS);
// 母版
// String dFirstId = getStringRandom();

View File

@@ -3,44 +3,86 @@ package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.PresentationMLPackage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
import org.docx4j.wml.P;
import org.pptx4j.pml.GroupShape;
import org.pptx4j.pml.Shape;
import org.springframework.web.multipart.MultipartFile;
import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsSlideInfoVo;
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.vo.JudgementSlidesVO;
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Paragraphs;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class SlideMaster {
public static List<JudgementSlidesVO> slideMaster(MultipartFile file) throws IOException, Docx4JException {
public static List<JudgementSlidesVO> slideMaster(List<WpsSlideInfoVo> wpsSlideInfoVos, MultipartFile file) throws IOException, Docx4JException {
List<JudgementSlidesVO> judgementSlidesVOS = new ArrayList<>();
// 1、获取想要判断的文件地址文件流
try (InputStream inputStream = file.getInputStream()) {
// 加载 .pptx 文件
PresentationMLPackage ppt =
(PresentationMLPackage) OpcPackage.load(inputStream);
// 你可以在这里处理 ppt比如读取文字、页数等
List<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();
int slideIndex = 1;
for (SlidePart slidePart : slideParts) {
// 获取幻灯片内容
System.out.println("========== 幻灯片 " + slideIndex++ + " ==========");
// 遍历 shape tree 中的 spshape元素
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
for (Object shapeObj : shapes) {
if (shapeObj instanceof Shape) {
Shape shape = (Shape) shapeObj;
System.out.println("Shape name: " + shape.getNvSpPr().getCNvPr().getName());
for (WpsSlideInfoVo wpsSlideInfoVo : wpsSlideInfoVos) {
// 幻灯片位置(第几张幻灯片)
String slideIndex = wpsSlideInfoVo.getSlideIndex();
String shapeIndex = wpsSlideInfoVo.getShapeIndex();
// 形状类型 slide 幻灯片设置 shape 形状(不包含图片) shapePic 形状图片 presentation 总幻灯片设置
String type = wpsSlideInfoVo.getType();
// 大类名称
String firstName = wpsSlideInfoVo.getFirstName();
// 方法名称
String function = wpsSlideInfoVo.getFunction();
// 考点名称
String examName = wpsSlideInfoVo.getExamName();
// 考点代码
String examCode = wpsSlideInfoVo.getExamCode();
// 方式方法
String method = wpsSlideInfoVo.getMethod();
if (type.equals("shape")) {
// 查询幻灯片图片
List<SlidePart> slideParts = ppt.getMainPresentationPart().getSlideParts();
int slideIndexFoFile = 1;
for (SlidePart slidePart : slideParts) {
// 获取幻灯片内容
slideIndexFoFile++;
if (slideIndex.equals(String.valueOf(slideIndexFoFile))) {
// 查询幻灯片
// 遍历 shape tree 中的 spshape元素
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
int shapeIndexFoFile = 1;
for (Object shapeObj : shapes) {
shapeIndexFoFile++;
if (shapeObj instanceof Shape) {
Shape shape = (Shape) shapeObj;
if (shapeIndex.equals(String.valueOf(shapeIndexFoFile))) {
// 查询出具体想要查询哪个段落的数据
// 目标对象
Shape shapeFunction = new Shape();
// 获取参数中类型的定义
Class<?>[] paramTypes = {Shape.class};
// 带参数的方法调用示例
Method methodWithArgs = shapeFunction.getClass().getMethod(function, paramTypes);
// 实际参数值
Object[] arguments = {shape};
String value = (String) methodWithArgs.invoke(shapeFunction, arguments);
System.out.println(value);
}
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -0,0 +1,113 @@
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
import org.docx4j.dml.*;
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
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(); // 兜底:返回原枚举字符串
}
}
}