Compare commits
9 Commits
7ec4b60140
...
607db65e35
Author | SHA1 | Date | |
---|---|---|---|
![]() |
607db65e35 | ||
![]() |
e7c411d014 | ||
![]() |
753397e9f5 | ||
![]() |
ee443494fc | ||
![]() |
0c7765b8aa | ||
![]() |
da0338111a | ||
![]() |
d46347914b | ||
![]() |
660494cd1c | ||
![]() |
02eb70d15e |
@@ -1,8 +0,0 @@
|
||||
=== 学生提交内容(stu 目录) ===
|
||||
-> 文件夹
|
||||
文件名1.html -> 大小: 397103B, 隐藏: false, 可读: true, 可写: true
|
||||
文件名2.html -> 大小: 397103B, 隐藏: false, 可读: true, 可写: true
|
||||
=== 学生提交内容得分点 ===
|
||||
考点文件名1.html -> 得分权值:1-> 是否得分:true
|
||||
考点文件名2.html -> 得分权值:1-> 是否得分:true
|
||||
考点百度一下,你就知道 -> 得分权值:1-> 是否得分:false
|
@@ -63,6 +63,10 @@ public class ErrorQuestionListVo {
|
||||
*/
|
||||
private String specialtyName;
|
||||
|
||||
/**
|
||||
* 解析
|
||||
*/
|
||||
private String analysis;
|
||||
|
||||
/**
|
||||
* 试题答案
|
||||
|
@@ -33,6 +33,7 @@ public class ExamErrorQuestionServiceImpl implements ExamErrorQuestionService {
|
||||
item.setPointNames(examQuestion.getPointNamesVo());
|
||||
item.setCourseName(examQuestion.getCourseName());
|
||||
item.setSpecialtyName(examQuestion.getSpecialtyName());
|
||||
item.setAnalysis(examQuestion.getAnalysis());
|
||||
item.setAnswerList(examQuestion.getAnswerList());
|
||||
item.setContent(examQuestion.getContent());
|
||||
item.setFileUploads(examQuestion.getFileUploads());
|
||||
|
@@ -116,63 +116,61 @@ public class AutoToolsController {
|
||||
securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
|
||||
// 获取登录用户
|
||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||
// 通过token获取用户redis信息,获取refreshToken
|
||||
OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get("oauth2_access_token:" + token), OAuth2AccessTokenDO.class);
|
||||
int startTime = 0;
|
||||
if (oAuth2AccessTokenDO != null) {
|
||||
String refreshToken = oAuth2AccessTokenDO.getRefreshToken();
|
||||
// 查找对应的task
|
||||
EducationPaperParam educationPaperParam = educationPaperParamService.selectEducationPaperParamByTaskId(stuInTheExam.getTaskId());
|
||||
String isSession = educationPaperParam.getIsSession();
|
||||
if (isSession.equals("0")) {
|
||||
// 设置了场次
|
||||
// 剩余时间需要继续计算
|
||||
EducationPaperPerson educationPaperPerson = educationPaperPersonMapper.selectByTaskIdAndPersonId(stuInTheExam.getTaskId(), String.valueOf(loginUser.getId()));
|
||||
EducationPaperSession educationPaperSession = educationPaperSessionMapper.selectEducationPaperSessionBySessionId(educationPaperPerson.getSessionId());
|
||||
//结束时间
|
||||
Date endTime = educationPaperSession.getEndTime();
|
||||
Date nowTime = new Date();
|
||||
// 将 Date 转换为 Instant
|
||||
Instant endInstant = endTime.toInstant();
|
||||
Instant nowInstant = nowTime.toInstant();
|
||||
// 计算秒数差
|
||||
long diffInSeconds = Duration.between(nowInstant, endInstant).getSeconds();
|
||||
startTime = (int) diffInSeconds;
|
||||
} else {
|
||||
// 没有设置场次
|
||||
// 查找对应的task
|
||||
EducationPaperParam educationPaperParam = educationPaperParamService.selectEducationPaperParamByTaskId(stuInTheExam.getTaskId());
|
||||
String isSession = educationPaperParam.getIsSession();
|
||||
if (isSession.equals("0")) {
|
||||
// 设置了场次
|
||||
// 剩余时间需要继续计算
|
||||
EducationPaperPerson educationPaperPerson = educationPaperPersonMapper.selectByTaskIdAndPersonId(stuInTheExam.getTaskId(), String.valueOf(loginUser.getId()));
|
||||
EducationPaperSession educationPaperSession = educationPaperSessionMapper.selectEducationPaperSessionBySessionId(educationPaperPerson.getSessionId());
|
||||
//结束时间
|
||||
Date endTime = educationPaperSession.getEndTime();
|
||||
Date nowTime = new Date();
|
||||
// 将 Date 转换为 Instant
|
||||
Instant endInstant = endTime.toInstant();
|
||||
Instant nowInstant = nowTime.toInstant();
|
||||
// 计算秒数差
|
||||
long diffInSeconds = Duration.between(nowInstant, endInstant).getSeconds();
|
||||
startTime = (int) diffInSeconds;
|
||||
} else {
|
||||
// 没有设置场次
|
||||
// 剩余时间直接读取数据
|
||||
String isTime = educationPaperParam.getIsTime();
|
||||
if (isTime.equals("0")) {
|
||||
// 设置了时间
|
||||
// 剩余时间直接读取数据
|
||||
String isTime = educationPaperParam.getIsTime();
|
||||
if (isTime.equals("0")) {
|
||||
// 设置了时间
|
||||
// 剩余时间直接读取数据
|
||||
long seconds = educationPaperParam.getExamTime().toLocalTime().toSecondOfDay();
|
||||
startTime = (int) seconds;
|
||||
} else {
|
||||
// 没有设置时间 默认1小时 = 3600秒
|
||||
startTime = 3600;
|
||||
}
|
||||
long seconds = educationPaperParam.getExamTime().toLocalTime().toSecondOfDay();
|
||||
startTime = (int) seconds;
|
||||
} else {
|
||||
// 没有设置时间 默认1小时 = 3600秒
|
||||
startTime = 3600;
|
||||
}
|
||||
// 判断是否存在对应的场次
|
||||
// 定时上传文件时间
|
||||
String time = educationPaperParam.getUploadTime();
|
||||
// 将分钟继续转换成秒
|
||||
stuInTheExam.setTimes(Integer.parseInt(time) * 60);
|
||||
// 倒计时
|
||||
AtomicInteger countdown = new AtomicInteger(startTime);
|
||||
// 创建初始返回数据
|
||||
StuTheExamInfo stuTheExamInfo = new StuTheExamInfo();
|
||||
// 返回数据-剩余时间
|
||||
stuTheExamInfo.setTime(formatLongDuration(countdown.get()));
|
||||
// 返回数据-上传文件状态 0:上传;1:不上传
|
||||
stuTheExamInfo.setUpload(1);
|
||||
// 返回数据-上传文件状态 0:结束;1:不结束
|
||||
stuTheExamInfo.setEndStatus(1);
|
||||
// 返回数据-网络状态
|
||||
stuTheExamInfo.setNetwork("强");
|
||||
// 创建对应的线程池
|
||||
taskManager.startTask(stuInTheExam, stuTheExamInfo, refreshToken, countdown, new AtomicInteger(0));
|
||||
return CommonResult.success(refreshToken);
|
||||
}
|
||||
// 判断是否存在对应的场次
|
||||
// 定时上传文件时间
|
||||
String time = educationPaperParam.getUploadTime();
|
||||
// 将分钟继续转换成秒
|
||||
stuInTheExam.setTimes(Integer.parseInt(time) * 60);
|
||||
// 倒计时
|
||||
AtomicInteger countdown = new AtomicInteger(startTime);
|
||||
// 创建初始返回数据
|
||||
StuTheExamInfo stuTheExamInfo = new StuTheExamInfo();
|
||||
// 返回数据-剩余时间
|
||||
stuTheExamInfo.setTime(formatLongDuration(countdown.get()));
|
||||
// 返回数据-上传文件状态 0:上传;1:不上传
|
||||
stuTheExamInfo.setUpload(1);
|
||||
// 返回数据-上传文件状态 0:结束;1:不结束
|
||||
stuTheExamInfo.setEndStatus(1);
|
||||
// 返回数据-网络状态
|
||||
stuTheExamInfo.setNetwork("强");
|
||||
// 创建对应的线程池
|
||||
if (loginUser != null) {
|
||||
taskManager.startTask(stuInTheExam, stuTheExamInfo, token, countdown, new AtomicInteger(0), String.valueOf(loginUser.getId()));
|
||||
}
|
||||
System.out.println("--------------token------------------" + token);
|
||||
return CommonResult.success(token);
|
||||
}
|
||||
return CommonResult.success("未登录");
|
||||
}
|
||||
@@ -185,17 +183,12 @@ public class AutoToolsController {
|
||||
@GetMapping("/stopExam")
|
||||
public CommonResult<Boolean> stopExam() {
|
||||
HttpServletRequest request = ServletUtils.getRequest();
|
||||
String token = null;
|
||||
String userId = null;
|
||||
if (request != null) {
|
||||
token = SecurityFrameworkUtils.obtainAuthorization(request,
|
||||
securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
|
||||
OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get("oauth2_access_token:" + token), OAuth2AccessTokenDO.class);
|
||||
if (oAuth2AccessTokenDO != null) {
|
||||
// 删除对应的线程池
|
||||
String refreshToken = oAuth2AccessTokenDO.getRefreshToken();
|
||||
taskManager.stopTask(refreshToken);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
userId = String.valueOf(SecurityFrameworkUtils.getLoginUserId());
|
||||
// 删除对应的线程池
|
||||
taskManager.stopTask(userId);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
return CommonResult.success(false);
|
||||
}
|
||||
|
@@ -27,28 +27,30 @@ public class TaskManager {
|
||||
private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
|
||||
|
||||
/** 开始任务(每秒执行一次) */
|
||||
public void startTask(StuInTheExam stuInTheExam, StuTheExamInfo stuTheExamInfo, String token, AtomicInteger countdown, AtomicInteger counter) {
|
||||
public void startTask(StuInTheExam stuInTheExam, StuTheExamInfo stuTheExamInfo, String token, AtomicInteger countdown, AtomicInteger counter, String userId) {
|
||||
// 判断 token 的线程是否存在,存在则不进行任何动作
|
||||
if (tasks.containsKey(token)) {
|
||||
log.info("任务 {} 已存在,未重复启动", token);
|
||||
return;
|
||||
if (tasks.containsKey(userId)) {
|
||||
log.info("任务 {} 已存在,未重复启动", userId);
|
||||
// TODO 删除,重置倒计时
|
||||
tasks.remove(userId);
|
||||
// return;
|
||||
}
|
||||
tasks.computeIfAbsent(token, k -> {
|
||||
tasks.computeIfAbsent(userId, k -> {
|
||||
Runnable task = safe(() -> {
|
||||
int current = counter.incrementAndGet();
|
||||
int remaining = countdown.decrementAndGet();
|
||||
log.debug("任务 {} tick at {}", token, System.currentTimeMillis());
|
||||
log.debug("任务 {} tick at {}", userId, System.currentTimeMillis());
|
||||
|
||||
if (current == stuInTheExam.getTimes()) {
|
||||
stuTheExamInfo.setUpload(0);
|
||||
counter.set(0);
|
||||
}
|
||||
if (remaining <= 0) {
|
||||
ScheduledFuture<?> future = tasks.remove(token);
|
||||
ScheduledFuture<?> future = tasks.remove(userId);
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
stuTheExamInfo.setEndStatus(0);
|
||||
log.info("任务 {} 已完成并取消", token);
|
||||
log.info("任务 {} 已完成并取消", userId);
|
||||
}
|
||||
}
|
||||
stuTheExamInfo.setTime(formatLongDuration(remaining));
|
||||
@@ -56,18 +58,18 @@ public class TaskManager {
|
||||
});
|
||||
return scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);
|
||||
});
|
||||
log.info("任务 {} 已启动", token);
|
||||
log.info("任务 {} 已启动", userId);
|
||||
}
|
||||
|
||||
/** 结束任务 */
|
||||
public void stopTask(String token) {
|
||||
ScheduledFuture<?> future = tasks.get(token);
|
||||
public void stopTask(String userId) {
|
||||
ScheduledFuture<?> future = tasks.get(userId);
|
||||
if (future != null) {
|
||||
future.cancel(false);
|
||||
tasks.remove(token);
|
||||
log.info("任务 {} 已停止", token);
|
||||
tasks.remove(userId);
|
||||
log.info("任务 {} 已停止", userId);
|
||||
} else {
|
||||
log.warn("任务 {} 不存在", token);
|
||||
log.warn("任务 {} 不存在", userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,8 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import org.docx4j.dml.*;
|
||||
import org.docx4j.jaxb.XPathBinderAssociationIsPartialException;
|
||||
import org.pptx4j.pml.*;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.HexColorUtils;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToCmConverter;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToEmuConverter;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.Transition;
|
||||
@@ -14,6 +12,24 @@ import java.util.List;
|
||||
|
||||
public class ShapePage {
|
||||
|
||||
// 页眉页脚 文本
|
||||
public static String getShapeHeaderFooterText(org.pptx4j.pml.Shape sp, CTSlideTiming timing) {
|
||||
CTTextBody ctTextBody = sp.getTxBody();
|
||||
String text = "";
|
||||
if (ctTextBody != null) {
|
||||
List<CTTextParagraph> pList = ctTextBody.getP();
|
||||
for (CTTextParagraph p : pList) {
|
||||
List<Object> objectList = p.getEGTextRun();
|
||||
for (Object o : objectList) {
|
||||
if (o instanceof CTRegularTextRun) {
|
||||
text += ((CTRegularTextRun) o).getT() + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// 大小 - 高度
|
||||
public static String getShapeSizeHeight(org.pptx4j.pml.Shape sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
@@ -159,7 +175,12 @@ public class ShapePage {
|
||||
|
||||
// 形状填充 填充透明度
|
||||
public static String getShapeFillOpacity(org.pptx4j.pml.Shape sp, CTSlideTiming timing) {
|
||||
return "待开发";
|
||||
CTShapeProperties ctShapeProperties = sp.getSpPr();
|
||||
if (ctShapeProperties.getSolidFill() != null) {
|
||||
CTPositiveFixedPercentage ctPositiveFixedPercentage = (CTPositiveFixedPercentage) ctShapeProperties.getSolidFill().getPrstClr().getEGColorTransform().get(0).getValue();
|
||||
return (100 - (ctPositiveFixedPercentage.getVal() / 1000)) + "%";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 形状线条 绘制
|
||||
@@ -172,7 +193,7 @@ public class ShapePage {
|
||||
// 宽度(EMU -> pt)
|
||||
double widthPt = ln.getW() / 12700.0;
|
||||
System.out.println("形状: " + sp.getNvSpPr().getCNvPr().getName());
|
||||
System.out.println("线宽: " + widthPt + " pt");
|
||||
System.out.println("线宽: " + widthPt + "磅");
|
||||
|
||||
// 颜色
|
||||
if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) {
|
||||
@@ -201,9 +222,19 @@ public class ShapePage {
|
||||
if (spPr != null && spPr.getLn() != null) {
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
// 颜色
|
||||
if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) {
|
||||
if (ln.getSolidFill() != null) {
|
||||
CTSRgbColor rgb = ln.getSolidFill().getSrgbClr();
|
||||
return rgb.getVal();
|
||||
if (rgb != null) {
|
||||
return rgb.getVal();
|
||||
}
|
||||
CTScRgbColor scrgb = ln.getSolidFill().getScrgbClr();
|
||||
if (scrgb != null) {
|
||||
return "RGB(" + scrgb.getR() + "," + scrgb.getG() + "," + scrgb.getB() + ")";
|
||||
}
|
||||
CTSchemeColor ctSchemeColor = ln.getSolidFill().getSchemeClr();
|
||||
if (ctSchemeColor != null) {
|
||||
return ctSchemeColor.getVal().value();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@@ -379,7 +410,7 @@ public class ShapePage {
|
||||
|
||||
// —— 2) 偏移描述:dist EMU -> pt;<1pt 视作“接触”,否则 "Xpt 偏移" —— //
|
||||
double distPt = emuToPt(r.getDist()); // 可能为 0
|
||||
String offsetLabel = distPt < 1.0 ? "接触" : (SlideUtils.fmt1(distPt) + "pt 偏移");
|
||||
String offsetLabel = distPt < 1.0 ? "接触" : (SlideUtils.fmt1(distPt) + "磅 偏移");
|
||||
|
||||
return coverageLabel + " " + offsetLabel;
|
||||
}
|
||||
@@ -453,6 +484,7 @@ public class ShapePage {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String getAniationInfo(List<Object> objects, List<Object> upObjects, String spId, String parameter) {
|
||||
boolean isDur = false;
|
||||
boolean isDirection = false;
|
||||
@@ -466,7 +498,34 @@ public class ShapePage {
|
||||
// 判断方向
|
||||
if (isDirection) {
|
||||
if (object instanceof CTTLAnimateEffectBehavior) {
|
||||
return ((CTTLAnimateEffectBehavior) object).getFilter();
|
||||
return Transition.effectWithDirToChinese(((CTTLAnimateEffectBehavior) object).getFilter());
|
||||
} else {
|
||||
// 获取行动轨迹
|
||||
for (Object ob : objects) {
|
||||
if (ob instanceof CTTLAnimateBehavior) {
|
||||
List<CTTLTimeAnimateValue> cttlTimeAnimateValues = ((CTTLAnimateBehavior) ob).getTavLst().getTav();
|
||||
for (CTTLTimeAnimateValue cttlTimeAnimateValue : cttlTimeAnimateValues) {
|
||||
String value = cttlTimeAnimateValue.getVal().getStrVal().getVal();
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("0-#ppt_h/") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("-#ppt_h")) {
|
||||
return "自顶部";
|
||||
}
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("#ppt_h+") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("+#ppt_h")) {
|
||||
return "自底部";
|
||||
}
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("+#ppt_w") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("#ppt_w+")) {
|
||||
return "自右侧";
|
||||
}
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("0-#ppt_w/") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("-#ppt_w") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("-N")) {
|
||||
return "自左侧";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (object instanceof CTTLSetBehavior) {
|
||||
@@ -828,7 +887,7 @@ public class ShapePage {
|
||||
// 遍历段落中的所有文本运行
|
||||
Object obj = paragraph.getEGTextRun().get(0);
|
||||
if (obj instanceof CTRegularTextRun ctRegularTextRun) {
|
||||
return ctRegularTextRun.getRPr().getSolidFill().getSrgbClr().getVal();
|
||||
return HexColorUtils.hexToColorString(ctRegularTextRun.getRPr().getSolidFill().getSrgbClr().getVal());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -977,7 +1036,9 @@ public class ShapePage {
|
||||
// 遍历所有段落
|
||||
for (CTTextParagraph paragraph : textBody.getP()) {
|
||||
if (paragraph.getPPr().getIndent() != null) {
|
||||
return paragraph.getPPr().getIndent() / 12700.0 + " pt"; // EMU 转 pt
|
||||
// 结果保留1位小数点
|
||||
String formattedNumber = String.format("%.2f", (double) paragraph.getPPr().getIndent() / 360000);
|
||||
return formattedNumber + "厘米"; // EMU 转 pt
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -991,7 +1052,7 @@ public class ShapePage {
|
||||
// 遍历所有段落
|
||||
for (CTTextParagraph paragraph : textBody.getP()) {
|
||||
if (paragraph.getPPr().getSpcBef() != null) {
|
||||
return paragraph.getPPr().getSpcBef().getSpcPts().getVal() / 100 + " pt"; // EMU 转 pt
|
||||
return paragraph.getPPr().getSpcBef().getSpcPts().getVal() / 100 + "磅"; // EMU 转 pt
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1004,8 +1065,8 @@ public class ShapePage {
|
||||
CTTextBody textBody = sp.getTxBody();
|
||||
// 遍历所有段落
|
||||
for (CTTextParagraph paragraph : textBody.getP()) {
|
||||
if (paragraph.getPPr().getSpcBef() != null) {
|
||||
return paragraph.getPPr().getSpcAft().getSpcPts().getVal() / 100 + " pt"; // EMU 转 pt
|
||||
if (paragraph.getPPr().getSpcAft() != null) {
|
||||
return paragraph.getPPr().getSpcAft().getSpcPts().getVal() / 100 + "磅"; // EMU 转 pt
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1037,7 +1098,7 @@ public class ShapePage {
|
||||
if (paragraph.getPPr().getLnSpc().getSpcPct() != null) {
|
||||
return (double) paragraph.getPPr().getLnSpc().getSpcPct().getVal() / 100000 + "倍行距"; // EMU 转 pt
|
||||
} else if (paragraph.getPPr().getLnSpc().getSpcPts() != null) {
|
||||
return (double) paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 10 + "磅固定行距"; // EMU 转 pt
|
||||
return (double) paragraph.getPPr().getLnSpc().getSpcPts().getVal() / 100 + "磅"; // EMU 转 pt
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1201,7 +1262,7 @@ public class ShapePage {
|
||||
long widthEmu = ctLineProperties.getW();
|
||||
// 转换为磅(1磅≈12700EMU)
|
||||
double widthPt = widthEmu / 12700.0;
|
||||
return widthPt + "pt";
|
||||
return widthPt + "磅";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1221,7 +1282,7 @@ public class ShapePage {
|
||||
double fontSizePt = fontSize / 100.0;
|
||||
// 估算轮廓高度(通常为字体高度的10-15%)
|
||||
double outlineHeightPt = fontSizePt * 0.12; // 12%估算
|
||||
return outlineHeightPt + "pt";
|
||||
return outlineHeightPt + "磅";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import org.docx4j.dml.*;
|
||||
import org.pptx4j.pml.*;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.PtToCmConverter;
|
||||
@@ -11,6 +10,96 @@ import java.util.List;
|
||||
|
||||
public class ShapePic {
|
||||
|
||||
// 图片 插入
|
||||
public static String getAddPic(Pic sp, CTSlideTiming timing) {
|
||||
return "是";
|
||||
}
|
||||
|
||||
// 媒体 放映时隐藏
|
||||
public static String getMediaHide(Pic sp, CTSlideTiming timing) {
|
||||
// 获取ID
|
||||
long id = sp.getNvPicPr().getCNvPr().getId();
|
||||
CTTimeNodeList ctTimeNodeList = timing.getTnLst();
|
||||
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
|
||||
for (Object o : parOrSeqOrExcl) {
|
||||
if (o instanceof CTTLTimeNodeParallel) {
|
||||
CTTimeNodeList ctTimeNodes = ((CTTLTimeNodeParallel) o).getCTn().getChildTnLst();
|
||||
for (Object o1 : ctTimeNodes.getParOrSeqOrExcl()) {
|
||||
if (o1 instanceof CTTLMediaNodeVideo) {
|
||||
// 获取spId
|
||||
String spId = ((CTTLMediaNodeVideo) o1).getCMediaNode().getTgtEl().getSpTgt().getSpid();
|
||||
if (String.valueOf(id).equals(spId)) {
|
||||
return ((CTTLMediaNodeVideo) o1).getCMediaNode().isShowWhenStopped() ? "否" : "是";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 媒体 播放完返回开头
|
||||
public static String getMediaLoop(Pic sp, CTSlideTiming timing) {
|
||||
// 获取ID
|
||||
long id = sp.getNvPicPr().getCNvPr().getId();
|
||||
CTTimeNodeList ctTimeNodeList = timing.getTnLst();
|
||||
List<Object> parOrSeqOrExcl = ctTimeNodeList.getParOrSeqOrExcl();
|
||||
for (Object o : parOrSeqOrExcl) {
|
||||
if (o instanceof CTTLTimeNodeParallel) {
|
||||
CTTimeNodeList ctTimeNodes = ((CTTLTimeNodeParallel) o).getCTn().getChildTnLst();
|
||||
for (Object o1 : ctTimeNodes.getParOrSeqOrExcl()) {
|
||||
if (o1 instanceof CTTLMediaNodeVideo) {
|
||||
// 获取spId
|
||||
String spId = ((CTTLMediaNodeVideo) o1).getCMediaNode().getTgtEl().getSpTgt().getSpid();
|
||||
if (String.valueOf(id).equals(spId)) {
|
||||
STTLTimeNodeFillType sttlTimeNodeFillType = ((CTTLMediaNodeVideo) o1).getCMediaNode().getCTn().getFill();
|
||||
String value = sttlTimeNodeFillType.value();
|
||||
return value.equals("remove") ? "是" : "否";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 自选图形 - 类型
|
||||
public static String getShapeType(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties ctShapeProperties = sp.getSpPr();
|
||||
CTPresetGeometry2D prst = ctShapeProperties.getPrstGeom();
|
||||
if (prst != null && prst.getPrst() != null) {
|
||||
STShapeType t = prst.getPrst();
|
||||
switch (t) {
|
||||
case HEXAGON:
|
||||
return "六边形";
|
||||
case RECT:
|
||||
return "矩形";
|
||||
case ROUND_RECT:
|
||||
return "圆角矩形";
|
||||
case ELLIPSE:
|
||||
return "椭圆";
|
||||
case TRIANGLE:
|
||||
return "三角形";
|
||||
case DIAMOND:
|
||||
return "菱形";
|
||||
case PARALLELOGRAM:
|
||||
return "平行四边形";
|
||||
case TRAPEZOID:
|
||||
return "梯形";
|
||||
case OCTAGON:
|
||||
return "八边形";
|
||||
case PENTAGON:
|
||||
return "五边形";
|
||||
default:
|
||||
return "预设形状:" + t.value();
|
||||
}
|
||||
}
|
||||
if (ctShapeProperties.getCustGeom() != null) {
|
||||
return "自定义几何(custGeom)";
|
||||
}
|
||||
return "未指定几何";
|
||||
}
|
||||
|
||||
// 大小 - 高度
|
||||
public static String getShapeSizeHeight(Pic sp, CTSlideTiming timing) {
|
||||
CTTransform2D xfrm = sp.getSpPr().getXfrm();
|
||||
@@ -180,9 +269,19 @@ public class ShapePic {
|
||||
if (spPr != null && spPr.getLn() != null) {
|
||||
CTLineProperties ln = spPr.getLn();
|
||||
// 颜色
|
||||
if (ln.getSolidFill() != null && ln.getSolidFill().getSrgbClr() != null) {
|
||||
if (ln.getSolidFill() != null) {
|
||||
CTSRgbColor rgb = ln.getSolidFill().getSrgbClr();
|
||||
return rgb.getVal();
|
||||
if (rgb != null) {
|
||||
return rgb.getVal();
|
||||
}
|
||||
CTScRgbColor scrgb = ln.getSolidFill().getScrgbClr();
|
||||
if (scrgb != null) {
|
||||
return "RGB(" + scrgb.getR() + "," + scrgb.getG() + "," + scrgb.getB() + ")";
|
||||
}
|
||||
CTSchemeColor ctSchemeColor = ln.getSolidFill().getSchemeClr();
|
||||
if (ctSchemeColor != null) {
|
||||
return ctSchemeColor.getVal().value();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@@ -258,6 +357,7 @@ public class ShapePic {
|
||||
// 3) 未设置 → 视为实线
|
||||
return "solid";
|
||||
}
|
||||
|
||||
// 形状线条 开始箭头类型
|
||||
public static String getShapeLineStartArrow(Pic sp, CTSlideTiming timing) {
|
||||
CTShapeProperties spPr = sp.getSpPr();
|
||||
@@ -269,14 +369,17 @@ public class ShapePic {
|
||||
}
|
||||
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 "待开发";
|
||||
@@ -349,12 +452,12 @@ public class ShapePic {
|
||||
} else if (coverage >= 0.45) {
|
||||
coverageLabel = "半倒影";
|
||||
} else {
|
||||
coverageLabel = "短倒影";
|
||||
coverageLabel = "紧密倒影";
|
||||
}
|
||||
|
||||
// —— 2) 偏移描述:dist EMU -> pt;<1pt 视作“紧贴”,否则 "Xpt 偏移" —— //
|
||||
// —— 2) 偏移描述:dist EMU -> pt;<1pt 视作“接触”,否则 "Xpt 偏移" —— //
|
||||
double distPt = emuToPt(r.getDist()); // 可能为 0
|
||||
String offsetLabel = distPt < 1.0 ? "紧贴" : (SlideUtils.fmt1(distPt) + "pt 偏移");
|
||||
String offsetLabel = distPt < 1.0 ? "接触" : (SlideUtils.fmt1(distPt) + "pt 偏移");
|
||||
|
||||
return coverageLabel + " " + offsetLabel;
|
||||
}
|
||||
@@ -383,14 +486,17 @@ public class ShapePic {
|
||||
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 "待开发";
|
||||
@@ -425,6 +531,7 @@ public class ShapePic {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String getAniationInfo(List<Object> objects, List<Object> upObjects, String spId, String parameter) {
|
||||
boolean isDur = false;
|
||||
boolean isDirection = false;
|
||||
@@ -438,7 +545,33 @@ public class ShapePic {
|
||||
// 判断方向
|
||||
if (isDirection) {
|
||||
if (object instanceof CTTLAnimateEffectBehavior) {
|
||||
return ((CTTLAnimateEffectBehavior) object).getFilter();
|
||||
return Transition.effectWithDirToChinese(((CTTLAnimateEffectBehavior) object).getFilter());
|
||||
} else {
|
||||
// 获取行动轨迹
|
||||
for (Object ob : objects) {
|
||||
if (ob instanceof CTTLAnimateBehavior) {
|
||||
List<CTTLTimeAnimateValue> cttlTimeAnimateValues = ((CTTLAnimateBehavior) ob).getTavLst().getTav();
|
||||
for (CTTLTimeAnimateValue cttlTimeAnimateValue : cttlTimeAnimateValues) {
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("0-#ppt_h/") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("-#ppt_h")) {
|
||||
return "自顶部";
|
||||
}
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("#ppt_h+") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("+#ppt_h")) {
|
||||
return "自底部";
|
||||
}
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("+#ppt_w") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("#ppt_w+")) {
|
||||
return "自右侧";
|
||||
}
|
||||
if (cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("0-#ppt_w/") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("-#ppt_w") ||
|
||||
cttlTimeAnimateValue.getVal().getStrVal().getVal().contains("-N")) {
|
||||
return "自左侧";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (object instanceof CTTLSetBehavior) {
|
||||
|
@@ -1,12 +1,10 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.apache.poi.xslf.usermodel.XMLSlideShow;
|
||||
import org.apache.poi.xslf.usermodel.XSLFSlide;
|
||||
import org.apache.xmlbeans.XmlCursor;
|
||||
import org.docx4j.openpackaging.packages.OpcPackage;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.NotesMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
|
||||
import org.pptx4j.pml.CTSlideTiming;
|
||||
import org.pptx4j.pml.GroupShape;
|
||||
import org.pptx4j.pml.Pic;
|
||||
import org.pptx4j.pml.Shape;
|
||||
@@ -35,25 +33,27 @@ public class SlideConversion {
|
||||
String firstId = getStringRandom();
|
||||
setSlideDataInfo(firstId, "", "幻灯片", "slide", "", false, "", dataInfoVOS);
|
||||
for (SlidePart slidePart : slideParts) {
|
||||
slideIndexFoFile ++;
|
||||
// 第三层
|
||||
String secondId = getStringRandom();
|
||||
setSlideDataInfo(secondId, firstId, "第"+slideIndexFoFile+"页", "sld", String.valueOf(slideIndexFoFile), false, "", dataInfoVOS);
|
||||
String thirdSettingId = getStringRandom();
|
||||
String thirdId = getStringRandom();
|
||||
slideIndexFoFile++;
|
||||
setSlideDataInfo(secondId, firstId, "第" + slideIndexFoFile + "页", "sld", String.valueOf(slideIndexFoFile), false, "", dataInfoVOS);
|
||||
setSlideDataInfo(thirdSettingId, secondId, "幻灯片设置", "slideSetting", String.valueOf(slideIndexFoFile) + "_0", true, "第" + slideIndexFoFile + "页", dataInfoVOS);
|
||||
setSlideDataInfo(thirdId, secondId, "形状", "shapeMaster", "", false, "", dataInfoVOS);
|
||||
// 获取形状
|
||||
GroupShape spTree = slidePart.getJaxbElement().getCSld().getSpTree();
|
||||
List<Object> shapes = spTree.getSpOrGrpSpOrGraphicFrame();
|
||||
int slideIndexFoShape = 0;
|
||||
// 第三层
|
||||
String thirdId = getStringRandom();
|
||||
setSlideDataInfo(thirdId, secondId, "幻灯片设置", "slideSetting", String.valueOf(slideIndexFoFile) + "_0", true, "第"+slideIndexFoFile+"页", dataInfoVOS);
|
||||
for (Object shapeObj : shapes) {
|
||||
slideIndexFoShape ++;
|
||||
slideIndexFoShape++;
|
||||
// 形状
|
||||
String fourId = getStringRandom();
|
||||
if (shapeObj instanceof Shape) {
|
||||
setSlideDataInfo(fourId, secondId, "形状"+slideIndexFoShape+"->"+((Shape) shapeObj).getNvSpPr().getCNvPr().getName(), "shape", String.valueOf(slideIndexFoFile) + "_" + String.valueOf(slideIndexFoShape), true, "第"+slideIndexFoFile+"页", dataInfoVOS);
|
||||
setSlideDataInfo(fourId, thirdId, "形状" + slideIndexFoShape + "->" + ((Shape) shapeObj).getNvSpPr().getCNvPr().getName(), "shape", String.valueOf(slideIndexFoFile) + "_" + String.valueOf(slideIndexFoShape), true, "第" + slideIndexFoFile + "页", dataInfoVOS);
|
||||
}
|
||||
if (shapeObj instanceof Pic) {
|
||||
setSlideDataInfo(fourId, secondId, "形状"+slideIndexFoShape+"->"+((Pic) shapeObj).getNvPicPr().getCNvPr().getName(), "shape", String.valueOf(slideIndexFoFile) + "_" + String.valueOf(slideIndexFoShape), true, "第"+slideIndexFoFile+"页", dataInfoVOS);
|
||||
setSlideDataInfo(fourId, thirdId, "形状" + slideIndexFoShape + "->" + ((Pic) shapeObj).getNvPicPr().getCNvPr().getName(), "shape", String.valueOf(slideIndexFoFile) + "_" + String.valueOf(slideIndexFoShape), true, "第" + slideIndexFoFile + "页", dataInfoVOS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,61 @@ public class SlideConversion {
|
||||
String secondSetId = getStringRandom();
|
||||
setSlideDataInfo(firstSetId, "", "设置", "slideAllSetting", "", false, "", dataInfoVOS);
|
||||
setSlideDataInfo(secondSetId, firstSetId, "幻灯片设置", "slideAllSetting", "0_0", true, "设置", dataInfoVOS);
|
||||
|
||||
// 幻灯片母版
|
||||
String firstModelId = getStringRandom();
|
||||
String secondModelId = getStringRandom();
|
||||
SlideMasterPart slideMasterPart = SlideMasterUtils.getSlideMaster(ppt);
|
||||
setSlideDataInfo(firstModelId, "", "母版", "slideModel", "", false, "", dataInfoVOS);
|
||||
// 获取母版的形状
|
||||
if (slideMasterPart != null) {
|
||||
setSlideDataInfo(secondModelId, firstModelId, "幻灯片母版", "shapeMaster", "", false, "", dataInfoVOS);
|
||||
List<Object> masterShapeList = slideMasterPart.getJaxbElement().getCSld().getSpTree().getSpOrGrpSpOrGraphicFrame();
|
||||
int slideIndexForModel = 0;
|
||||
String thirdModelId = getStringRandom();
|
||||
String thirdModelSettingId = getStringRandom();
|
||||
for (Object shapeObj : masterShapeList) {
|
||||
String fourModelId = getStringRandom();
|
||||
if (slideIndexForModel == 0) {
|
||||
setSlideDataInfo(thirdModelSettingId, secondModelId, "幻灯片设置", "shapeMaster", "", true, "", dataInfoVOS);
|
||||
setSlideDataInfo(thirdModelId, secondModelId, "形状", "shapeMaster", "", false, "", dataInfoVOS);
|
||||
}
|
||||
slideIndexForModel++;
|
||||
if (shapeObj instanceof Shape) {
|
||||
String valueName = ((Shape) shapeObj).getNvSpPr().getCNvPr().getName();
|
||||
setSlideDataInfo(fourModelId, thirdModelId, "形状" + slideIndexForModel + "->" + valueName, "slideModel", "0_" + String.valueOf(slideIndexForModel), true, "第" + slideIndexForModel + "页", dataInfoVOS);
|
||||
}
|
||||
if (shapeObj instanceof Pic) {
|
||||
String valueName = ((Pic) shapeObj).getNvPicPr().getCNvPr().getName();
|
||||
setSlideDataInfo(fourModelId, thirdModelId, "形状" + slideIndexForModel + "->" + valueName, "slideModel", "0_" + String.valueOf(slideIndexForModel), true, "第" + slideIndexForModel + "页", dataInfoVOS);
|
||||
}
|
||||
}
|
||||
}
|
||||
String secondModelNodeId = getStringRandom();
|
||||
// 备注母版
|
||||
NotesMasterPart notesMasterPart = SlideMasterUtils.getNotesMaster(ppt);
|
||||
if (notesMasterPart != null) {
|
||||
setSlideDataInfo(secondModelNodeId, firstModelId, "备注母版", "shapeNotesMaster", "", false, "", dataInfoVOS);
|
||||
List<Object> masterShapeList = notesMasterPart.getJaxbElement().getCSld().getSpTree().getSpOrGrpSpOrGraphicFrame();
|
||||
int slideIndexForModel = 0;
|
||||
String thirdModelId = getStringRandom();
|
||||
String thirdModelSettingId = getStringRandom();
|
||||
for (Object shapeObj : masterShapeList) {
|
||||
String fourModelId = getStringRandom();
|
||||
if (slideIndexForModel == 0) {
|
||||
setSlideDataInfo(thirdModelSettingId, secondModelNodeId, "幻灯片设置", "shapeNotesMaster", "", true, "", dataInfoVOS);
|
||||
setSlideDataInfo(thirdModelId, secondModelNodeId, "形状", "shapeNotesMaster", "", false, "", dataInfoVOS);
|
||||
}
|
||||
slideIndexForModel++;
|
||||
if (shapeObj instanceof Shape) {
|
||||
String valueName = ((Shape) shapeObj).getNvSpPr().getCNvPr().getName();
|
||||
setSlideDataInfo(fourModelId, thirdModelId, "形状" + slideIndexForModel + "->" + valueName, "shapeNotesMaster", "0_" + String.valueOf(slideIndexForModel), true, "第" + slideIndexForModel + "页", dataInfoVOS);
|
||||
}
|
||||
if (shapeObj instanceof Pic) {
|
||||
String valueName = ((Pic) shapeObj).getNvPicPr().getCNvPr().getName();
|
||||
setSlideDataInfo(fourModelId, thirdModelId, "形状" + slideIndexForModel + "->" + valueName, "shapeNotesMaster", "0_" + String.valueOf(slideIndexForModel), true, "第" + slideIndexForModel + "页", dataInfoVOS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -85,7 +139,7 @@ public class SlideConversion {
|
||||
|
||||
// 想数组中添加数据
|
||||
public static void setSlideDataInfo(String id, String parentId, String text,
|
||||
String type, String index, boolean isTrue, String belongToName, List<SlideDataInfoVO> dataInfoVOS) throws Exception {
|
||||
String type, String index, boolean isTrue, String belongToName, List<SlideDataInfoVO> dataInfoVOS) throws Exception {
|
||||
SlideDataInfoVO dataInfo = new SlideDataInfoVO();
|
||||
dataInfo.setId(id);
|
||||
dataInfo.setParentId(parentId);
|
||||
|
@@ -1,20 +1,11 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.CTBlipFillProperties;
|
||||
import org.docx4j.openpackaging.exceptions.Docx4JException;
|
||||
import org.docx4j.openpackaging.packages.OpcPackage;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.JaxbXmlPart;
|
||||
import org.docx4j.openpackaging.parts.Part;
|
||||
import org.docx4j.openpackaging.parts.Parts;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.NotesMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
|
||||
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
|
||||
import org.docx4j.relationships.Relationship;
|
||||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster;
|
||||
import org.pptx4j.pml.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsSlideInfoVo;
|
||||
@@ -25,7 +16,6 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SlideMaster {
|
||||
|
||||
|
@@ -1,20 +1,17 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.CTBlipFillProperties;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.JaxbXmlPart;
|
||||
import org.docx4j.openpackaging.parts.Part;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.NotesMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart;
|
||||
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
|
||||
import org.docx4j.relationships.Relationship;
|
||||
import org.pptx4j.jaxb.Context;
|
||||
import org.pptx4j.pml.CTBackground;
|
||||
import org.pptx4j.pml.Presentation;
|
||||
import org.pptx4j.pml.PresentationPr;
|
||||
import org.pptx4j.pml.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SlideMasterUtils {
|
||||
|
||||
@@ -58,6 +55,42 @@ public class SlideMasterUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取备注母版 NotesMasterPart(若文件未显式定义备注母版则返回 null)
|
||||
*/
|
||||
public static NotesMasterPart getNotesMaster(PresentationMLPackage ppt) {
|
||||
// 2. 获取 Presentation 对象
|
||||
MainPresentationPart mpp = ppt.getMainPresentationPart();
|
||||
Presentation pres = (Presentation) mpp.getJaxbElement();
|
||||
RelationshipsPart rels = ppt.getMainPresentationPart().getRelationshipsPart();
|
||||
// 获取母版ID
|
||||
CTNotesMasterIdList idList = pres.getNotesMasterIdLst();
|
||||
CTNotesMasterIdListEntry smId = idList.getNotesMasterId();
|
||||
String rId = null;
|
||||
try {
|
||||
rId = smId.getId(); // 新一点的模型常见
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
if (rId == null) {
|
||||
try {
|
||||
// 某些版本字段名为 id2(即 r:id)
|
||||
rId = (String) CTNotesMasterIdList.class.getMethod("getId2").invoke(smId);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
Relationship rel = rels.getRelationshipByID(rId);
|
||||
// 再通过关系拿到具体的 Part
|
||||
Part part = rels.getPart(rel);
|
||||
if (part instanceof NotesMasterPart) {
|
||||
NotesMasterPart notes = (NotesMasterPart) part;
|
||||
return notes;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// /ppt/presProps.xml 的关系类型
|
||||
private static final String REL_TYPE_PRES_PROPS =
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps";
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.dml.CTPositiveSize2D;
|
||||
import org.docx4j.dml.CTTransform2D;
|
||||
import org.docx4j.openpackaging.exceptions.Docx4JException;
|
||||
import org.docx4j.openpackaging.packages.PresentationMLPackage;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlideLayoutPart;
|
||||
@@ -9,12 +7,9 @@ import org.docx4j.openpackaging.parts.PresentationML.SlideMasterPart;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
|
||||
import org.pptx4j.pml.CTBackground;
|
||||
import org.pptx4j.pml.CTBackgroundProperties;
|
||||
import org.pptx4j.pml.CTSlideTiming;
|
||||
import org.pptx4j.pml.*;
|
||||
import org.docx4j.dml.*;
|
||||
import pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils.Transition;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
// 单个幻灯片设置
|
||||
@@ -102,7 +97,30 @@ public class SlideSetting {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 幻灯片切换 单机鼠标换片
|
||||
public static String getSlideTransitionMouse(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slide = (Sld) slidePart.getContents();
|
||||
CTSlideTransition ctSlideTransition = slide.getTransition();
|
||||
if (ctSlideTransition != null) {
|
||||
return ctSlideTransition.isAdvClick() ? "是" : "否";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
// 幻灯片切换 效果
|
||||
public static String getSlideTransitionEffect(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slide = (Sld) slidePart.getContents();
|
||||
CTSlideTransition ctSlideTransition = slide.getTransition();
|
||||
if (ctSlideTransition != null) {
|
||||
String value = ctSlideTransition.getBlinds().getDir().value();
|
||||
if (value.equals("vert")) {
|
||||
return "百叶窗->垂直";
|
||||
}
|
||||
if (value.equals("horz")) {
|
||||
return "百叶窗->水平";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
// 幻灯片切换 切换速度
|
||||
public static String getSlideTransitionSpeed(SlidePart slidePart, PresentationMLPackage presentation, SlideMasterPart slideMasterPart) throws Docx4JException {
|
||||
Sld slide = (Sld) slidePart.getContents();
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j;
|
||||
|
||||
import org.docx4j.dml.*;
|
||||
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
|
@@ -0,0 +1,116 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class HexColorUtils {
|
||||
|
||||
// 常见颜色映射表(可按需扩展)
|
||||
private static final Map<String, String> COLOR_NAME_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
// -------------------- 基础色 --------------------
|
||||
COLOR_NAME_MAP.put("#000000", "黑色");
|
||||
COLOR_NAME_MAP.put("#FFFFFF", "白色");
|
||||
COLOR_NAME_MAP.put("#FF0000", "红色");
|
||||
COLOR_NAME_MAP.put("#00FF00", "亮绿色");
|
||||
COLOR_NAME_MAP.put("#0000FF", "蓝色");
|
||||
COLOR_NAME_MAP.put("#FFFF00", "黄色");
|
||||
COLOR_NAME_MAP.put("#00FFFF", "青色");
|
||||
COLOR_NAME_MAP.put("#FF00FF", "洋红色");
|
||||
|
||||
// -------------------- Office 常见标准色 --------------------
|
||||
COLOR_NAME_MAP.put("#0070C0", "蓝色");
|
||||
COLOR_NAME_MAP.put("#00B050", "绿色");
|
||||
COLOR_NAME_MAP.put("#7030A0", "紫罗兰色");
|
||||
COLOR_NAME_MAP.put("#C00000", "深红色");
|
||||
COLOR_NAME_MAP.put("#FFC000", "橙黄色");
|
||||
|
||||
// -------------------- 灰阶 --------------------
|
||||
COLOR_NAME_MAP.put("#808080", "灰色");
|
||||
COLOR_NAME_MAP.put("#C0C0C0", "银色");
|
||||
COLOR_NAME_MAP.put("#A9A9A9", "深灰色");
|
||||
COLOR_NAME_MAP.put("#D3D3D3", "浅灰色");
|
||||
COLOR_NAME_MAP.put("#F5F5F5", "白烟色");
|
||||
|
||||
// -------------------- 常见网页颜色 --------------------
|
||||
COLOR_NAME_MAP.put("#800000", "栗色");
|
||||
COLOR_NAME_MAP.put("#808000", "橄榄色");
|
||||
COLOR_NAME_MAP.put("#800080", "紫色");
|
||||
COLOR_NAME_MAP.put("#008080", "水鸭色");
|
||||
COLOR_NAME_MAP.put("#FFA500", "橙色");
|
||||
COLOR_NAME_MAP.put("#A52A2A", "棕色");
|
||||
COLOR_NAME_MAP.put("#F5F5DC", "米色");
|
||||
COLOR_NAME_MAP.put("#FFC0CB", "粉红色");
|
||||
COLOR_NAME_MAP.put("#ADD8E6", "浅蓝色");
|
||||
COLOR_NAME_MAP.put("#90EE90", "浅绿色");
|
||||
COLOR_NAME_MAP.put("#FFD700", "金色");
|
||||
COLOR_NAME_MAP.put("#FA8072", "鲑鱼色");
|
||||
COLOR_NAME_MAP.put("#FF4500", "橙红色");
|
||||
COLOR_NAME_MAP.put("#4682B4", "钢蓝色");
|
||||
COLOR_NAME_MAP.put("#2E8B57", "海绿色");
|
||||
COLOR_NAME_MAP.put("#B22222", "耐火砖红");
|
||||
COLOR_NAME_MAP.put("#6B8E23", "橄榄褐色");
|
||||
COLOR_NAME_MAP.put("#DAA520", "秋麒麟色");
|
||||
COLOR_NAME_MAP.put("#5F9EA0", "军服蓝");
|
||||
COLOR_NAME_MAP.put("#D2691E", "巧克力色");
|
||||
|
||||
// -------------------- WPS/Excel 扩展色 --------------------
|
||||
COLOR_NAME_MAP.put("#1F497D", "深蓝色");
|
||||
COLOR_NAME_MAP.put("#4F81BD", "中蓝色");
|
||||
COLOR_NAME_MAP.put("#9BBB59", "草绿色");
|
||||
COLOR_NAME_MAP.put("#8064A2", "紫罗兰色");
|
||||
COLOR_NAME_MAP.put("#F79646", "杏橙色");
|
||||
COLOR_NAME_MAP.put("#C0504D", "砖红色");
|
||||
|
||||
COLOR_NAME_MAP.put("#31869B", "蓝绿色");
|
||||
COLOR_NAME_MAP.put("#E46C0A", "深橙色");
|
||||
COLOR_NAME_MAP.put("#77933C", "苔藓绿");
|
||||
COLOR_NAME_MAP.put("#963634", "褐红色");
|
||||
COLOR_NAME_MAP.put("#4BACC6", "湖蓝色");
|
||||
COLOR_NAME_MAP.put("#604A7B", "深紫色");
|
||||
|
||||
// -------------------- 柔和色调 --------------------
|
||||
COLOR_NAME_MAP.put("#B6DDE8", "浅湖蓝色");
|
||||
COLOR_NAME_MAP.put("#E6B8B7", "浅褐红色");
|
||||
COLOR_NAME_MAP.put("#D8E4BC", "浅草绿色");
|
||||
COLOR_NAME_MAP.put("#CCC0DA", "浅紫色");
|
||||
COLOR_NAME_MAP.put("#F2DCDB", "浅玫瑰色");
|
||||
COLOR_NAME_MAP.put("#B8CCE4", "浅蓝灰色");
|
||||
}
|
||||
|
||||
/**
|
||||
* 把十六进制颜色转成 "颜色名 RGB(r,g,b)" 格式的字符串
|
||||
* @param hex 十六进制颜色,例如 "00B050" 或 "#00B050"
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
public static String hexToColorString(String hex) {
|
||||
if (hex == null || hex.isEmpty()) {
|
||||
return "无效颜色";
|
||||
}
|
||||
|
||||
// 去掉 # 前缀
|
||||
if (hex.startsWith("#")) {
|
||||
hex = hex.substring(1);
|
||||
}
|
||||
|
||||
// 转换为 Color
|
||||
Color color = new Color(Integer.parseInt(hex, 16));
|
||||
int r = color.getRed();
|
||||
int g = color.getGreen();
|
||||
int b = color.getBlue();
|
||||
|
||||
// 转回统一的十六进制形式(大写,带 #)
|
||||
String hexKey = String.format("#%02X%02X%02X", r, g, b);
|
||||
|
||||
// 查找颜色名
|
||||
String name = COLOR_NAME_MAP.getOrDefault(hexKey, "未知颜色");
|
||||
name = name.equals("未知颜色") ? "" : name;
|
||||
return String.format("%s RGB(%d,%d,%d)", name, r, g, b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -24,19 +24,4 @@ public class PtToCmConverter {
|
||||
public static double cmToPoints(double cm) {
|
||||
return cm / PT_TO_CM;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 示例转换
|
||||
double points = 10.0;
|
||||
double cm = pointsToCm(points);
|
||||
|
||||
System.out.println(points + " pt = " + cm + " cm");
|
||||
System.out.println("反向转换: " + cm + " cm = " + cmToPoints(cm) + " pt");
|
||||
|
||||
// 常见值的转换示例
|
||||
System.out.println("\n常见值转换:");
|
||||
System.out.println("8 pt = " + pointsToCm(8) + " cm");
|
||||
System.out.println("12 pt = " + pointsToCm(12) + " cm");
|
||||
System.out.println("72 pt = " + pointsToCm(72) + " cm (1 inch)");
|
||||
}
|
||||
}
|
@@ -1,6 +1,13 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_pptx.pptx4j.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class Transition {
|
||||
|
||||
private static final Pattern P = Pattern.compile("\\(([^)]+)\\)");
|
||||
public static String getCnName(String fileName) {
|
||||
if (fileName == null) return "未知";
|
||||
|
||||
@@ -121,4 +128,79 @@ public class Transition {
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDirection(String spec) {
|
||||
if (spec == null) return null;
|
||||
Matcher m = P.matcher(spec);
|
||||
return m.find() ? m.group(1).trim() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将方向英文/短码汉化;可直接传入诸如 "downRight"、"rd"、"rightDown"、"vert"、"in" 等。
|
||||
* @param raw 方向原始字符串(不区分大小写,允许下划线/中横线/驼峰)
|
||||
* @return 对应的中文(若无法识别,原样返回)
|
||||
*/
|
||||
public static String toChinese(String raw) {
|
||||
if (raw == null || raw.isEmpty()) return raw;
|
||||
String s = normalize(raw);
|
||||
|
||||
switch (s) {
|
||||
// 单向
|
||||
case "l": case "left": return "向左";
|
||||
case "r": case "right": return "向右";
|
||||
case "u": case "up": return "向上";
|
||||
case "d": case "down": return "向下";
|
||||
|
||||
// 斜向
|
||||
case "lu": case "upleft": case "leftup": return "左上";
|
||||
case "ru": case "upright": case "rightup": return "右上";
|
||||
case "ld": case "downleft": case "leftdown": return "左下";
|
||||
case "rd": case "downright": case "rightdown": return "右下";
|
||||
|
||||
// 其他常见关键字
|
||||
case "vert": case "vertical": return "垂直";
|
||||
case "horz": case "horizontal": return "水平";
|
||||
case "in": return "向内";
|
||||
case "out": return "向外";
|
||||
|
||||
default:
|
||||
return raw; // 不识别则原样返回,避免误判
|
||||
}
|
||||
}
|
||||
|
||||
/** 归一化:去空格/分隔符、转小写、转驼峰组合为连续词 */
|
||||
private static String normalize(String s) {
|
||||
s = s.trim().toLowerCase(Locale.ROOT);
|
||||
s = s.replaceAll("[_\\-\\s]", ""); // 去掉下划线/中横线/空格
|
||||
return s;
|
||||
}
|
||||
|
||||
/** 示例:把效果+方向组合成人类可读中文,比如 "strips(downRight)" → "条纹(右下)" */
|
||||
public static String effectWithDirToChinese(String dir) {
|
||||
return toChinese(getDirection(dir));
|
||||
}
|
||||
|
||||
/** 常见效果英文→中文(可按需扩展) */
|
||||
public static String effectToChinese(String effect) {
|
||||
if (effect == null) return "效果";
|
||||
String e = effect.trim().toLowerCase(Locale.ROOT);
|
||||
switch (e) {
|
||||
case "strips": return "条纹";
|
||||
case "blinds": return "百叶窗";
|
||||
case "wipe": return "擦除";
|
||||
case "push": return "推出";
|
||||
case "split": return "分割";
|
||||
case "checkerboard":return "棋盘格";
|
||||
case "cover": return "覆盖";
|
||||
case "uncover": return "揭开";
|
||||
case "randombars": return "随机条";
|
||||
case "shape": return "形状";
|
||||
case "circle": return "圆形";
|
||||
case "diamond": return "菱形";
|
||||
case "plus": return "十字形";
|
||||
case "fade": return "淡入";
|
||||
case "appear": return "出现";
|
||||
default: return effect; // 未收录则原样
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.drawing;
|
||||
|
||||
import jakarta.xml.bind.*;
|
||||
import lombok.val;
|
||||
import org.apache.xmlbeans.XmlCursor;
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.*;
|
||||
import org.docx4j.dml.chartDrawing.CTPicture;
|
||||
@@ -24,6 +25,7 @@ import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.StringReader;
|
||||
@@ -1328,10 +1330,10 @@ public static String getSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
|
||||
return "是";
|
||||
}
|
||||
|
||||
// 2. 预设类型(shadow preset)
|
||||
// 2. 预设类型
|
||||
public static String getShadowPreset(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
CTReflectionEffect shadow = getReflectionEffect(anchor);
|
||||
if (shadow == null) return "无";
|
||||
if (shadow == null) return "无预设";
|
||||
|
||||
// 例如根据blurRad、dist、dir组合判断
|
||||
BigInteger blurRad = BigInteger.valueOf(shadow.getBlurRad());
|
||||
|
@@ -8,6 +8,7 @@ import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.*;
|
||||
import org.docx4j.dml.chartDrawing.CTPicture;
|
||||
import org.docx4j.dml.picture.Pic;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Anchor;
|
||||
import org.docx4j.dml.wordprocessingDrawing.CTWrapSquare;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
@@ -753,8 +754,80 @@ public class DrawingInline {
|
||||
|
||||
// 获取“预设”
|
||||
public static String getShadowPresetType(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
CTOuterShadowEffect shadow = getOuterShadow(anchor);
|
||||
CTInnerShadowEffect innerShadow= getInnerShadow(anchor);
|
||||
CTPresetShadowEffect presetShadow = getPresetShadow(anchor);
|
||||
if (shadow != null ) {
|
||||
|
||||
return "无预设";
|
||||
Long blurRadObj = shadow.getBlurRad();
|
||||
Long distObj = shadow.getDist();
|
||||
Long dirObj = (long) shadow.getDir();
|
||||
|
||||
long blurRad = blurRadObj != null ? blurRadObj : 0L;
|
||||
long dist = distObj != null ? distObj : 0L;
|
||||
long dir = dirObj != null ? dirObj : 0L;
|
||||
// 转换为角度
|
||||
double angle = dir / 60000.0;
|
||||
|
||||
// 1️⃣ 偏移阴影(外部)
|
||||
if (angle == 45 ) return "偏移右下";
|
||||
if (angle == 90 ) return "偏移下";
|
||||
if (angle == 135) return "偏移左下";
|
||||
if (angle == 0&&dist!=0 ) return "偏移右";
|
||||
if (angle == 180 ) return "偏移左";
|
||||
if (angle ==315 ) return "偏移右上";
|
||||
if (angle == 270 ) return "偏移上";
|
||||
if (angle == 225) return "偏移左上";
|
||||
if (dist==0) return "中";
|
||||
|
||||
}
|
||||
if (innerShadow!=null){
|
||||
|
||||
Long blurRadObj = innerShadow.getBlurRad();
|
||||
Long distObj = innerShadow.getDist();
|
||||
Long dirObj = (long) innerShadow.getDir();
|
||||
|
||||
long blurRad = blurRadObj != null ? blurRadObj : 0L;
|
||||
long dist = distObj != null ? distObj : 0L;
|
||||
long dir = dirObj != null ? dirObj : 0L;
|
||||
// 转换为角度
|
||||
double angle = dir / 60000.0;
|
||||
// 2️⃣ 内部阴影(dist 较小,blurRad 中等)
|
||||
if (angle == 45 ) return "内部右下";
|
||||
if (angle == 90 ) return "内部下";
|
||||
if (angle == 135) return "内部左下";
|
||||
if (angle == 00&&dist!=0 ) return "内部右";
|
||||
if (angle == 180 ) return "内部左";
|
||||
if (angle ==315 ) return "内部右上";
|
||||
if (angle == 270 ) return "内部上";
|
||||
if (angle == 225) return "内部左上";
|
||||
if (dist==0) return "中";
|
||||
}
|
||||
|
||||
if (presetShadow!=null){
|
||||
Long blurRadObj = shadow.getBlurRad();
|
||||
Long distObj = shadow.getDist();
|
||||
Long dirObj = (long) shadow.getDir();
|
||||
|
||||
long blurRad = blurRadObj != null ? blurRadObj : 0L;
|
||||
long dist = distObj != null ? distObj : 0L;
|
||||
long dir = dirObj != null ? dirObj : 0L;
|
||||
// 转换为角度
|
||||
double angle = dir / 60000.0;
|
||||
|
||||
|
||||
// 3️⃣ 透视阴影(dist 和 blurRad 都较大)
|
||||
if (angle == 225) return "透视左上";
|
||||
if (angle ==315 ) return "透视右上";
|
||||
if (angle == 90 ) return "透视下";
|
||||
if (angle == 135) return "透视左下";
|
||||
if (angle == 45 ) return "透视右下";
|
||||
if (dist==0) return "中";
|
||||
}
|
||||
|
||||
|
||||
|
||||
return "无";
|
||||
}
|
||||
|
||||
|
||||
@@ -1324,7 +1397,34 @@ public static String getSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
|
||||
}
|
||||
|
||||
|
||||
private static CTInnerShadowEffect getInnerShadow(Inline anchor) {
|
||||
if (anchor == null || anchor.getDocPr() == null) return null;
|
||||
|
||||
if (anchor.getGraphic() != null &&
|
||||
anchor.getGraphic().getGraphicData() != null &&
|
||||
anchor.getGraphic().getGraphicData().getPic() != null) {
|
||||
|
||||
CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr();
|
||||
if (spPr != null && spPr.getEffectLst() != null && spPr.getEffectLst().getInnerShdw() != null) {
|
||||
return spPr.getEffectLst().getInnerShdw();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static CTPresetShadowEffect getPresetShadow(Inline anchor) {
|
||||
if (anchor == null || anchor.getGraphic() == null) return null;
|
||||
|
||||
Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0);
|
||||
if (graphicData instanceof JAXBElement) {
|
||||
graphicData = ((JAXBElement<?>) graphicData).getValue();
|
||||
}
|
||||
|
||||
if (graphicData instanceof CTPresetShadowEffect) {
|
||||
return (CTPresetShadowEffect) graphicData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static String getShapeName(String shapeType) {
|
||||
|
@@ -2,6 +2,7 @@ package pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph;
|
||||
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.model.structure.SectionWrapper;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
|
||||
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
|
||||
@@ -10,6 +11,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextAlignment;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author REN
|
||||
@@ -790,59 +792,90 @@ public class Paragraphs {
|
||||
|
||||
// 分栏 - 分隔线
|
||||
public static String getParagraphColIsSep(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||
PPr pPr = paragraph.getPPr();
|
||||
SectPr sectPr = null;
|
||||
sectPr = pPr.getSectPr();
|
||||
// 优先取段落自身的节属性
|
||||
if (pPr != null && pPr.getSectPr() != null) {
|
||||
sectPr = pPr.getSectPr();
|
||||
}
|
||||
if (paragraph == null) return "无";
|
||||
|
||||
// 或最后一个段落中会有一个节结束的SectPr
|
||||
if (sectPr == null && paragraph.getContent().size() > 0) {
|
||||
Object lastObj = paragraph.getContent().get(paragraph.getContent().size() - 1);
|
||||
if (lastObj instanceof JAXBElement && ((JAXBElement<?>) lastObj).getValue() instanceof SectPr) {
|
||||
sectPr = (SectPr) ((JAXBElement<?>) lastObj).getValue();
|
||||
List<Object> allParagraphs = wordMLPackage.getMainDocumentPart().getContent();
|
||||
if (allParagraphs == null || allParagraphs.isEmpty()) return "无";
|
||||
|
||||
SectPr currentSectPr = null;
|
||||
|
||||
for (Object obj : allParagraphs) {
|
||||
P p = null;
|
||||
if (obj instanceof P) {
|
||||
p = (P) obj;
|
||||
} else if (obj instanceof JAXBElement && ((JAXBElement<?>) obj).getValue() instanceof P) {
|
||||
p = (P) ((JAXBElement<?>) obj).getValue();
|
||||
}
|
||||
|
||||
if (p == null) continue;
|
||||
|
||||
// 每遇到一个段落的 SectPr 就更新当前节
|
||||
PPr pPr = p.getPPr();
|
||||
if (pPr != null && pPr.getSectPr() != null) {
|
||||
currentSectPr = pPr.getSectPr();
|
||||
}
|
||||
|
||||
// 如果当前段落就是目标段落,返回该节分栏信息
|
||||
if (p == paragraph) {
|
||||
if (currentSectPr != null && currentSectPr.getCols() != null) {
|
||||
CTColumns cols = currentSectPr.getCols();
|
||||
Boolean sep = cols.isSep();
|
||||
return sep != null && sep ? "是" : "否";
|
||||
} else {
|
||||
return "无";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sectPr != null && sectPr.getCols() != null) {
|
||||
CTColumns cols = sectPr.getCols();
|
||||
return cols.isSep() ? "是" : "否";
|
||||
}
|
||||
return "无";
|
||||
}
|
||||
|
||||
// 分栏 - 各栏间距
|
||||
public static String getParagraphColSpace(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
|
||||
PPr pPr = paragraph.getPPr();
|
||||
SectPr sectPr = null;
|
||||
sectPr = pPr.getSectPr();
|
||||
// 优先取段落自身的节属性
|
||||
if (pPr != null && pPr.getSectPr() != null) {
|
||||
sectPr = pPr.getSectPr();
|
||||
}
|
||||
if (paragraph == null) return "无";
|
||||
|
||||
// 或最后一个段落中会有一个节结束的SectPr
|
||||
if (sectPr == null && paragraph.getContent().size() > 0) {
|
||||
Object lastObj = paragraph.getContent().get(paragraph.getContent().size() - 1);
|
||||
if (lastObj instanceof JAXBElement && ((JAXBElement<?>) lastObj).getValue() instanceof SectPr) {
|
||||
sectPr = (SectPr) ((JAXBElement<?>) lastObj).getValue();
|
||||
List<Object> allParagraphs = wordMLPackage.getMainDocumentPart().getContent();
|
||||
if (allParagraphs == null || allParagraphs.isEmpty()) return "无";
|
||||
|
||||
SectPr currentSectPr = null;
|
||||
|
||||
for (Object obj : allParagraphs) {
|
||||
P p = null;
|
||||
if (obj instanceof P) {
|
||||
p = (P) obj;
|
||||
} else if (obj instanceof JAXBElement && ((JAXBElement<?>) obj).getValue() instanceof P) {
|
||||
p = (P) ((JAXBElement<?>) obj).getValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (sectPr != null && sectPr.getCols() != null) {
|
||||
CTColumns cols = sectPr.getCols();
|
||||
if (cols.getCol() != null && !cols.getCol().isEmpty()) {
|
||||
int index = 1;
|
||||
String value = "";
|
||||
for (CTColumn col : cols.getCol()) {
|
||||
value += "第 " + index + " 栏间距 (twips): " + col.getSpace() + " ";
|
||||
index++;
|
||||
if (p == null) continue;
|
||||
|
||||
// 遇到段落中有SectPr则更新当前节
|
||||
PPr pPr = p.getPPr();
|
||||
if (pPr != null && pPr.getSectPr() != null) {
|
||||
currentSectPr = pPr.getSectPr();
|
||||
}
|
||||
|
||||
// 如果是目标段落,返回分栏间距
|
||||
if (p == paragraph) {
|
||||
if (currentSectPr != null && currentSectPr.getCols() != null) {
|
||||
CTColumns cols = currentSectPr.getCols();
|
||||
if (cols.getCol() != null && !cols.getCol().isEmpty()) {
|
||||
StringBuilder value = new StringBuilder();
|
||||
int index = 1;
|
||||
for (CTColumn col : cols.getCol()) {
|
||||
value.append("第 ").append(index).append(" 栏间距 (twips): ").append(col.getSpace()).append(" ");
|
||||
index++;
|
||||
}
|
||||
return value.toString();
|
||||
} else if (cols.getSpace() != null) {
|
||||
// 如果没有单独列配置,使用总栏间距
|
||||
return "栏间距 (twips): " + cols.getSpace();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
return "无";
|
||||
}
|
||||
}
|
||||
|
||||
return "无";
|
||||
}
|
||||
// 分栏 - 各栏宽度
|
||||
|
@@ -29,6 +29,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.xpath.XPath;
|
||||
@@ -1729,7 +1730,7 @@ public class SectionPage {
|
||||
// 以 Top 为例获取颜色
|
||||
CTBorder top = borders.getTop();
|
||||
if (top != null && top.getColor() != null) {
|
||||
return top.getColor();
|
||||
return ColorUtils.getChineseColorName(top.getColor());
|
||||
}
|
||||
}
|
||||
return "无";
|
||||
|
@@ -123,7 +123,28 @@ public class TableIng {
|
||||
tblCursor.selectPath(path);
|
||||
if (tblCursor.toNextSelection()) {
|
||||
String val = tblCursor.getAttributeText(new QName(W_NAMESPACE, "val"));
|
||||
return val != null ? val : null;
|
||||
if (val == null) return null;
|
||||
// 汉化
|
||||
switch (val) {
|
||||
case "single":
|
||||
return "单实线";
|
||||
case "dashed":
|
||||
return "虚线";
|
||||
case "dotted":
|
||||
return "点线";
|
||||
case "double":
|
||||
return "双实线";
|
||||
case "thick":
|
||||
return "粗线";
|
||||
case "thinThickSmallGap":
|
||||
return "细粗小间隔线";
|
||||
case "thickThinSmallGap":
|
||||
return "粗细小间隔线";
|
||||
case "nil":
|
||||
return "无边框";
|
||||
default:
|
||||
return val; // 保留原值
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
|
@@ -13,6 +13,7 @@ import org.docx4j.dml.*;
|
||||
import org.docx4j.dml.CTColor;
|
||||
import org.docx4j.dml.picture.Pic;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Anchor;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
import org.docx4j.jaxb.Context;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.docx4j.vml.CTTextbox;
|
||||
@@ -1360,22 +1361,75 @@ public class TextInfo {
|
||||
|
||||
//填充方式
|
||||
public static String getTianChongValue(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getGraphic() == null) return "无填充";
|
||||
|
||||
try {
|
||||
// 1. 获取 anchor 的 graphicData
|
||||
Object graphicData = anchor.getGraphic().getGraphicData().getAny().get(0);
|
||||
if (graphicData instanceof JAXBElement) {
|
||||
JAXBElement<?> jaxbElement = (JAXBElement<?>) graphicData;
|
||||
Object value = jaxbElement.getValue();
|
||||
if (value instanceof CTWordprocessingShape) {
|
||||
CTWordprocessingShape textInfo = (CTWordprocessingShape) value;
|
||||
if (textInfo.getSpPr() != null) {
|
||||
if (textInfo.getSpPr().getSolidFill() != null) {
|
||||
return "纯色填充";
|
||||
} else if (textInfo.getSpPr().getBlipFill() != null) {
|
||||
return "图片填充";
|
||||
} else if (textInfo.getSpPr().getGradFill() != null) {
|
||||
return "渐变填充";
|
||||
} else {
|
||||
return "无填充";
|
||||
if (!(graphicData instanceof JAXBElement)) return "无填充";
|
||||
|
||||
JAXBElement<?> jaxbElement = (JAXBElement<?>) graphicData;
|
||||
Object value = jaxbElement.getValue();
|
||||
|
||||
// 2. 确认是文本框形状
|
||||
if (!(value instanceof CTWordprocessingShape)) return "无填充";
|
||||
CTWordprocessingShape textShape = (CTWordprocessingShape) value;
|
||||
|
||||
// 3. 解析 <w:txbxContent> 中的段落和文字
|
||||
if (textShape.getTxbx() != null && textShape.getTxbx().getTxbxContent() != null) {
|
||||
List<Object> blockLevelElts = textShape.getTxbx().getTxbxContent().getEGBlockLevelElts();
|
||||
|
||||
for (Object block : blockLevelElts) {
|
||||
if (block instanceof P) {
|
||||
P paragraph = (P) block;
|
||||
for (Object runObj : paragraph.getContent()) {
|
||||
if (runObj instanceof R) {
|
||||
R run = (R) runObj;
|
||||
|
||||
// 4. 检查 run 的 rPr 中的颜色
|
||||
if (run.getRPr() != null) {
|
||||
RPr rPr = run.getRPr();
|
||||
|
||||
// 4.1 直接定义了颜色 -> 纯色填充
|
||||
if (rPr.getColor() != null && rPr.getColor().getVal() != null) {
|
||||
return "纯色填充";
|
||||
}
|
||||
|
||||
// 4.2 检查 Drawing 中的 <a:solidFill> 或 <a:gradFill>
|
||||
for (Object drawingObj : run.getContent()) {
|
||||
if (drawingObj instanceof Drawing) {
|
||||
Drawing drawing = (Drawing) drawingObj;
|
||||
for (Object inlineObj : drawing.getAnchorOrInline()) {
|
||||
if (inlineObj instanceof Inline) {
|
||||
Inline inline = (Inline) inlineObj;
|
||||
|
||||
// 解析图形属性
|
||||
if (inline.getGraphic() != null &&
|
||||
inline.getGraphic().getGraphicData() != null) {
|
||||
List<Object> anyList = inline.getGraphic().getGraphicData().getAny();
|
||||
for (Object any : anyList) {
|
||||
if (any instanceof JAXBElement) {
|
||||
JAXBElement<?> innerElement = (JAXBElement<?>) any;
|
||||
Object innerValue = innerElement.getValue();
|
||||
|
||||
// 解析 a:solidFill 或 a:gradFill
|
||||
if (innerValue instanceof CTShapeProperties) {
|
||||
CTShapeProperties shapeProps = (CTShapeProperties) innerValue;
|
||||
if (shapeProps.getSolidFill() != null) {
|
||||
return "纯色填充";
|
||||
} else if (shapeProps.getGradFill() != null) {
|
||||
return "渐变填充";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1383,78 +1437,177 @@ public class TextInfo {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// 默认无填充
|
||||
return "无填充";
|
||||
}
|
||||
|
||||
// 文本框 - 文字 - 纯色填充 → 颜色
|
||||
public static String getTextBoxTextSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
|
||||
Anchor anchor,
|
||||
int betoLong,
|
||||
WordprocessingMLPackage wordMLPackage) {
|
||||
try {
|
||||
// 1. 判空检查
|
||||
if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Object> anyList = anchor.getGraphic().getGraphicData().getAny();
|
||||
if (anyList == null || anyList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 遍历 GraphicData 下的内容
|
||||
for (Object obj : anyList) {
|
||||
Object value = (obj instanceof JAXBElement) ? ((JAXBElement<?>) obj).getValue() : obj;
|
||||
|
||||
// 只处理 CTWordprocessingShape
|
||||
if (!value.getClass().getName().endsWith("CTWordprocessingShape")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. 通过反射获取 getTxbx() 方法
|
||||
Method getTxbxMethod;
|
||||
try {
|
||||
getTxbxMethod = value.getClass().getMethod("getTxbx");
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.out.println("当前 CTWordprocessingShape 没有 getTxbx() 方法");
|
||||
continue;
|
||||
}
|
||||
|
||||
Object txbxObj = getTxbxMethod.invoke(value);
|
||||
if (txbxObj == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. 解析 wps:txbxContent
|
||||
Method getTxbxContentMethod = txbxObj.getClass().getMethod("getTxbxContent");
|
||||
Object txbxContent = getTxbxContentMethod.invoke(txbxObj);
|
||||
|
||||
if (txbxContent == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 5. 获取段落 <w:p>
|
||||
if (!(txbxContent instanceof ContentAccessor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ContentAccessor contentAccessor = (ContentAccessor) txbxContent;
|
||||
List<Object> paragraphs = contentAccessor.getContent();
|
||||
if (paragraphs == null || paragraphs.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object pObj : paragraphs) {
|
||||
Object paragraph = (pObj instanceof JAXBElement) ? ((JAXBElement<?>) pObj).getValue() : pObj;
|
||||
|
||||
if (!(paragraph instanceof P)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 6. 获取段落中的 run
|
||||
P p = (P) paragraph;
|
||||
List<Object> runs = p.getContent();
|
||||
for (Object rObj : runs) {
|
||||
Object runVal = (rObj instanceof JAXBElement) ? ((JAXBElement<?>) rObj).getValue() : rObj;
|
||||
if (!(runVal instanceof R)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
R run = (R) runVal;
|
||||
RPr runPr = run.getRPr();
|
||||
if (runPr != null && runPr.getColor() != null) {
|
||||
String colorVal = runPr.getColor().getVal(); // 十六进制字符串,例如 "4874CB"
|
||||
if (colorVal != null && colorVal.length() == 6) {
|
||||
try {
|
||||
int r = Integer.parseInt(colorVal.substring(0, 2), 16);
|
||||
int g = Integer.parseInt(colorVal.substring(2, 4), 16);
|
||||
int b = Integer.parseInt(colorVal.substring(4, 6), 16);
|
||||
String rgb = "RGB(" + r + "," + g + "," + b + ")";
|
||||
System.out.println("文本框文字颜色 = " + rgb);
|
||||
return rgb;
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("未找到文本框文字颜色");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 文本框-文字-纯色填充 → 透明度
|
||||
public static String getTextBoxSolidFillTransparency(
|
||||
List<JudgementWordsVO> judgementWordsVOS,
|
||||
org.docx4j.dml.wordprocessingDrawing.Anchor anchor,
|
||||
int betoLong,
|
||||
WordprocessingMLPackage wordMLPackage) {
|
||||
|
||||
if (anchor == null) return "无纯色填充";
|
||||
try {
|
||||
// 1. 包装成 JAXBElement 打印调试 XML
|
||||
JAXBElement<Anchor> jaxbElement = new JAXBElement<>(
|
||||
new javax.xml.namespace.QName(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"anchor"),
|
||||
Anchor.class,
|
||||
anchor
|
||||
);
|
||||
String xml = XmlUtils.marshaltoString(jaxbElement, true, true);
|
||||
// System.out.println("Anchor XML:\n" + xml);
|
||||
|
||||
|
||||
//纯色填充→透明度
|
||||
public static String getTextBoxSolidFillTransparency(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) {
|
||||
System.out.println("Anchor 或 Graphic 为空");
|
||||
return "无纯色填充";
|
||||
}
|
||||
|
||||
List<Object> anyList = anchor.getGraphic().getGraphicData().getAny();
|
||||
if (anyList == null || anyList.isEmpty()) {
|
||||
System.out.println("GraphicData 中没有任何对象");
|
||||
return "无纯色填充";
|
||||
}
|
||||
|
||||
for (Object obj : anyList) {
|
||||
Object value = obj instanceof JAXBElement ? ((JAXBElement<?>) obj).getValue() : obj;
|
||||
|
||||
// 文本框一般是 CTWordprocessingShape 类型
|
||||
if (value.getClass().getName().endsWith("CTWordprocessingShape")) {
|
||||
// 2. 直接解析 XML 字符串查找 w14:textFill/solidFill/schemeClr/alpha
|
||||
String alpha = "无纯色填充";
|
||||
// 正则匹配 w14:solidFill 下的 w14:alpha
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
|
||||
"<w14:textFill>\\s*<w14:solidFill>.*?<w14:alpha\\s+w14:val=\"(\\d+)\"\\s*/>.*?</w14:solidFill>",
|
||||
java.util.regex.Pattern.DOTALL
|
||||
);
|
||||
java.util.regex.Matcher matcher = pattern.matcher(xml);
|
||||
if (matcher.find()) {
|
||||
String alphaVal = matcher.group(1);
|
||||
// Word 中 alpha 取值范围 0~100000,对应 0~100%
|
||||
try {
|
||||
// 反射调用 getSpPr()
|
||||
Method getSpPrMethod = value.getClass().getMethod("getSpPr");
|
||||
Object spPrObj = getSpPrMethod.invoke(value);
|
||||
|
||||
if (spPrObj == null) {
|
||||
System.out.println("文本框 spPr 为 null");
|
||||
return "无纯色填充";
|
||||
}
|
||||
|
||||
// 反射调用 getSolidFill()
|
||||
Method getSolidFillMethod = spPrObj.getClass().getMethod("getSolidFill");
|
||||
Object solidFillObj = getSolidFillMethod.invoke(spPrObj);
|
||||
|
||||
if (solidFillObj == null) {
|
||||
System.out.println("spPr 中无纯色填充");
|
||||
return "无纯色填充";
|
||||
}
|
||||
|
||||
// 获取 srgbClr 或 schemeClr
|
||||
Method getSrgbClrMethod = solidFillObj.getClass().getMethod("getSrgbClr");
|
||||
Object srgbClrObj = getSrgbClrMethod.invoke(solidFillObj);
|
||||
|
||||
Method getSchemeClrMethod = solidFillObj.getClass().getMethod("getSchemeClr");
|
||||
Object schemeClrObj = getSchemeClrMethod.invoke(solidFillObj);
|
||||
|
||||
// 透明度提取逻辑
|
||||
String transparency = "0%"; // 默认完全不透明
|
||||
|
||||
if (srgbClrObj != null) {
|
||||
transparency = extractAlphaTransparency(srgbClrObj);
|
||||
return transparency;
|
||||
} else if (schemeClrObj != null) {
|
||||
transparency = extractAlphaTransparency(schemeClrObj);
|
||||
return transparency;
|
||||
} else {
|
||||
System.out.println("无srgbClr和schemeClr颜色");
|
||||
return transparency;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
System.out.println("未识别的类型: " + value.getClass().getName());
|
||||
int alphaInt = Integer.parseInt(alphaVal);
|
||||
int percent = Math.round(alphaInt / 1000.0f); // 四舍五入取整百分比
|
||||
alpha = percent + "%";
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
|
||||
return "无纯色填充";
|
||||
}
|
||||
|
||||
// 获取透明度方法(0-100%)
|
||||
private static int getAlphaFromColor(Object colorObj) {
|
||||
try {
|
||||
Method getAlpha = colorObj.getClass().getMethod("getAlpha");
|
||||
Object alpha = getAlpha.invoke(colorObj);
|
||||
if (alpha != null) {
|
||||
// alpha 为 CTPositiveFixedPercentage 或类似类型
|
||||
Method getVal = alpha.getClass().getMethod("getVal");
|
||||
Long val = (Long) getVal.invoke(alpha); // 0-100000
|
||||
return 100 - (int)(val / 1000); // 转换为百分比
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果没有 alpha,默认不透明
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user