Merge pull request '【修改】完善文字考点' (#1) from hyc into master
Reviewed-on: #1
This commit is contained in:
@@ -36,7 +36,7 @@ import java.util.List;
|
||||
@RequestMapping("/auto/wps")
|
||||
@Tag(name = "测试判分2 - wps相关操作")
|
||||
@Validated
|
||||
public class AutoWpsController {
|
||||
public class AutoWpsController {
|
||||
|
||||
@Resource
|
||||
JudgementWpsWordService judgementWpsWordService;
|
||||
@@ -82,7 +82,7 @@ public class AutoWpsController {
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping(value = "/docxMaster", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public CommonResult<List<JudgementWordsVO>> docxMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file) throws Exception {
|
||||
public CommonResult<List<JudgementWordsVO>> docxMaster(@RequestPart("data") String jsonData, @RequestPart("file") MultipartFile file, @RequestParam(value = "name", required = false) String name) throws Exception {
|
||||
// 手动解析JSON数组
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<WpsDocxInfoVo> wpsDocxInfoVos = objectMapper.readValue(
|
||||
@@ -90,7 +90,7 @@ public class AutoWpsController {
|
||||
new TypeReference<List<WpsDocxInfoVo>>() {
|
||||
}
|
||||
);
|
||||
return CommonResult.success(judgementWpsWordService.docxMaster(wpsDocxInfoVos, file));
|
||||
return CommonResult.success(judgementWpsWordService.docxMaster(wpsDocxInfoVos, file,name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -15,7 +15,7 @@ import java.util.List;
|
||||
*/
|
||||
public interface JudgementWpsWordService {
|
||||
|
||||
List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file) throws Exception;
|
||||
List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file,String name) throws Exception;
|
||||
|
||||
List<DocxDataInfoVO> docxDataInfo(MultipartFile file) throws Exception;
|
||||
}
|
||||
|
@@ -30,8 +30,8 @@ public class JudgementWpsWordServiceImpl implements JudgementWpsWordService {
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@Override
|
||||
public List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file) throws Exception {
|
||||
return DocxMaster.docxMaster(wpsDocxInfoVos, file);
|
||||
public List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file,String name) throws Exception {
|
||||
return DocxMaster.docxMaster(wpsDocxInfoVos, file,name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -23,62 +23,72 @@ public class DocxConversion {
|
||||
public static List<DocxDataInfoVO> DocxDataInfos(MultipartFile file) throws Exception {
|
||||
List<DocxDataInfoVO> dataInfoVOS = new ArrayList<>();
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
// 1. 直接使用 InputStream 构造 XWPFDocument(无需本地文件)
|
||||
XWPFDocument document = new XWPFDocument(inputStream);
|
||||
|
||||
// 2. 读取文档内容(示例:提取所有段落文本)
|
||||
StringBuilder content = new StringBuilder();
|
||||
document.getParagraphs().forEach(paragraph -> {
|
||||
content.append(paragraph.getText()).append("\n");
|
||||
});
|
||||
|
||||
|
||||
String xmlString = XmlUtil.getDocumentXml(document);
|
||||
String namespace = WpsWordNameSpaces.getNameSpace(xmlString);
|
||||
|
||||
XmlObject docXml = document.getDocument();
|
||||
// 1、获取文档段落W:P标签得数量,判断出一个有多少段
|
||||
|
||||
// 段落根节点
|
||||
XmlCursor wpCursor = docXml.newCursor();
|
||||
String wpPath = namespace + "/w:document/w:body/w:p";
|
||||
wpCursor.selectPath(wpPath);
|
||||
|
||||
int wpIndex = 0;
|
||||
// 段落
|
||||
String firstIdWp = getStringRandom();
|
||||
setWordDataInfo(firstIdWp, "", "段落", "w:p", "", false, dataInfoVOS);
|
||||
while (wpCursor.toNextSelection()) {
|
||||
wpIndex ++;
|
||||
// 段落属性
|
||||
if (!wpCursor.xmlText().contains("w:sectPr")) {
|
||||
// 获取文本
|
||||
XmlCursor wpTextXml = wpCursor.newCursor();
|
||||
wpTextXml.selectPath(namespace + ".//w:r/w:t");
|
||||
StringBuilder fullTextBuilder = new StringBuilder();
|
||||
while (wpTextXml.toNextSelection()) {
|
||||
String part = wpTextXml.getTextValue();
|
||||
if (part != null) {
|
||||
fullTextBuilder.append(part);
|
||||
}
|
||||
}
|
||||
String fullText = fullTextBuilder.toString().trim();
|
||||
if (!fullText.isEmpty()) {
|
||||
String secondIdWp = getStringRandom();
|
||||
String shortText = shorten(fullText); // 截断展示
|
||||
setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + shortText, "w:p", String.valueOf(wpIndex), true, dataInfoVOS);
|
||||
|
||||
while (wpCursor.toNextSelection()) {
|
||||
wpIndex++;
|
||||
if (!wpCursor.xmlText().contains("w:sectPr")) {
|
||||
// 1. 提取普通段落文本(排除文本框)
|
||||
String fullText = extractParagraphText(wpCursor, namespace);
|
||||
|
||||
// 2. 提取文本框文本
|
||||
List<String> textBoxSentences = processTextBoxParagraphs(wpCursor, namespace);
|
||||
|
||||
String secondIdWp = null;
|
||||
if (!fullText.isEmpty()) {
|
||||
// 过滤掉文本框句子
|
||||
StringBuilder filteredTextBuilder = new StringBuilder();
|
||||
String[] sentences = fullText.split("[。!?.!?]");
|
||||
for (String sentence : sentences) {
|
||||
sentence = sentence.trim();
|
||||
if (!sentence.isEmpty() && (textBoxSentences == null || !textBoxSentences.contains(sentence))) {
|
||||
if (filteredTextBuilder.length() > 0) {
|
||||
filteredTextBuilder.append("。"); // 可以用原文分隔符
|
||||
}
|
||||
filteredTextBuilder.append(sentence);
|
||||
}
|
||||
}
|
||||
String filteredFullText = filteredTextBuilder.toString();
|
||||
|
||||
// 生成段落节点,使用过滤后的文本
|
||||
secondIdWp = getStringRandom();
|
||||
setWordDataInfo(secondIdWp, firstIdWp, "段落" + wpIndex + ":" + shorten(filteredFullText), "w:p", String.valueOf(wpIndex), true, dataInfoVOS);
|
||||
|
||||
// 拆分句子
|
||||
int sentenceIndex = 0;
|
||||
for (String sentence : sentences) {
|
||||
sentence = sentence.trim();
|
||||
if (!sentence.isEmpty()) {
|
||||
// 跳过文本框中已经存在的句子
|
||||
if (textBoxSentences != null && textBoxSentences.contains(sentence)) {
|
||||
continue;
|
||||
}
|
||||
sentenceIndex++;
|
||||
String thirdIdWp = getStringRandom();
|
||||
setWordDataInfo(thirdIdWp, secondIdWp, "句子" + sentenceIndex + ":" + shorten(sentence), "w:t", String.valueOf(wpIndex), true, dataInfoVOS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 2、页面
|
||||
XmlCursor wSectPrCursor = docXml.newCursor();
|
||||
String wSectPrPath = namespace + "//w:sectPr"; // 查找所有节
|
||||
@@ -243,7 +253,18 @@ public class DocxConversion {
|
||||
dataInfoVOS
|
||||
);
|
||||
}
|
||||
|
||||
String firstIdFTbl = getStringRandom();
|
||||
setWordDataInfo(firstIdFTbl, "", "杂项", "file", "1", false, dataInfoVOS);
|
||||
String secondIdTbl = getStringRandom();
|
||||
setWordDataInfo(
|
||||
secondIdTbl,
|
||||
firstIdFTbl,
|
||||
"文件" ,
|
||||
"file",
|
||||
"1",
|
||||
true,
|
||||
dataInfoVOS
|
||||
);
|
||||
|
||||
|
||||
// // 5、域
|
||||
@@ -319,5 +340,52 @@ public class DocxConversion {
|
||||
return text; // 如果没匹配到,就返回原字符串
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取段落文本,包括 w:t
|
||||
*/
|
||||
private static String extractParagraphText(XmlCursor wpCursor, String namespace) {
|
||||
XmlCursor wpTextXml = wpCursor.newCursor();
|
||||
wpTextXml.selectPath(namespace + ".//w:r/w:t");
|
||||
|
||||
StringBuilder fullTextBuilder = new StringBuilder();
|
||||
while (wpTextXml.toNextSelection()) {
|
||||
String part = wpTextXml.getTextValue();
|
||||
if (part != null) {
|
||||
fullTextBuilder.append(part);
|
||||
}
|
||||
}
|
||||
return fullTextBuilder.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归处理文本框段落
|
||||
*/
|
||||
|
||||
|
||||
|
||||
private static List<String> processTextBoxParagraphs(XmlCursor wpCursor, String namespace) throws Exception {
|
||||
List<String> textBoxSentences = new ArrayList<>();
|
||||
XmlCursor tbCursor = wpCursor.newCursor();
|
||||
// 注意命名空间声明方式,避免 XPath 异常
|
||||
tbCursor.selectPath(namespace + ".//w:txbxContent/w:p");
|
||||
while (tbCursor.toNextSelection()) {
|
||||
String tbText = extractParagraphText(tbCursor, namespace);
|
||||
if (!tbText.isEmpty()) {
|
||||
// 按句子拆分
|
||||
String[] sentences = tbText.split("[。!?.!?]");
|
||||
for (String sentence : sentences) {
|
||||
sentence = sentence.trim();
|
||||
if (!sentence.isEmpty()) {
|
||||
textBoxSentences.add(sentence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tbCursor.dispose();
|
||||
return textBoxSentences;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import org.w3c.dom.Node;
|
||||
import pc.exam.pp.module.judgement.controller.admin.AutoWps.vo.WpsDocxInfoVo;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.endNote.EndNoteing;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.file.isFile;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.insert.InsertIng;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Paragraphs;
|
||||
@@ -72,7 +73,7 @@ public class DocxMaster {
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
public static List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file) throws Exception {
|
||||
public static List<JudgementWordsVO> docxMaster(List<WpsDocxInfoVo> wpsDocxInfoVos, MultipartFile file,String name) throws Exception {
|
||||
|
||||
// 一共分为 段落, 页面,图形
|
||||
String firstDuanLuoId = Convert.getStringRandom();
|
||||
@@ -626,6 +627,28 @@ public class DocxMaster {
|
||||
|
||||
|
||||
|
||||
if (firstName.contains("文件")) {
|
||||
try {
|
||||
|
||||
Class<?> clazz = isFile.class;
|
||||
Method method = clazz.getMethod(function);
|
||||
String value = (String) method.invoke(null );
|
||||
if (value != null) {
|
||||
judgementWordsVOS = setJudgementWord(
|
||||
judgementWordsVOS,
|
||||
docxFunction + "@" +name+"@"+ value,
|
||||
firstName + examName + name
|
||||
);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.err.println("方法不存在: " + function);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
break; // 找到指定表格就退出
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -90,19 +90,108 @@ public class Drawing {
|
||||
//布局 环绕方式
|
||||
public static String getLayoutHuanRao(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
// 是否衬于文字下方
|
||||
boolean behindDoc = anchor.isBehindDoc();
|
||||
if (behindDoc) return "衬于文字下方型";
|
||||
// 浮于文字上方型
|
||||
if (anchor.getWrapNone() != null) return "浮于文字上方型";
|
||||
if (anchor.getWrapSquare() != null) return "四周型";
|
||||
if (anchor.getWrapTight() != null) return "紧密型";
|
||||
if (anchor.getWrapThrough() != null) return "穿越型";
|
||||
if (anchor.getWrapTopAndBottom() != null) return "上下型";
|
||||
|
||||
return "无";
|
||||
String wrapType = null;
|
||||
|
||||
// 1. 优先判断环绕方式
|
||||
if (anchor.getWrapTight() != null) {
|
||||
wrapType = "紧密型";
|
||||
} else if (anchor.getWrapSquare() != null) {
|
||||
wrapType = "四周型";
|
||||
} else if (anchor.getWrapThrough() != null) {
|
||||
wrapType = "穿越型";
|
||||
} else if (anchor.getWrapTopAndBottom() != null) {
|
||||
wrapType = "上下型";
|
||||
} else if (anchor.getWrapNone() != null) {
|
||||
wrapType = "浮于文字上方型";
|
||||
}
|
||||
|
||||
// 2. 如果没有任何环绕方式,则根据 behindDoc 判断
|
||||
if (wrapType == null) {
|
||||
return anchor.isBehindDoc() ? "衬于文字下方型" : "无";
|
||||
}
|
||||
|
||||
// 3. 返回最终结果
|
||||
return wrapType;
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 对齐方式
|
||||
public static String getLayoutHorizontalAlign(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionH() == null) return "未知";
|
||||
if (anchor.getPositionH().getAlign() != null) {
|
||||
switch (anchor.getPositionH().getAlign()) {
|
||||
case LEFT:
|
||||
return "左对齐";
|
||||
case CENTER:
|
||||
return "居中";
|
||||
case RIGHT:
|
||||
return "右对齐";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 相对于
|
||||
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionH() == null) return "未知";
|
||||
if (anchor.getPositionH().getRelativeFrom() != null) {
|
||||
switch (anchor.getPositionH().getRelativeFrom()) {
|
||||
case COLUMN:
|
||||
return "列";
|
||||
case MARGIN:
|
||||
return "页边距";
|
||||
case PAGE:
|
||||
return "页面";
|
||||
case CHARACTER:
|
||||
return "字符";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 对齐方式
|
||||
public static String getLayoutVerticalAlign(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
||||
if (anchor.getPositionV().getAlign() != null) {
|
||||
switch (anchor.getPositionV().getAlign()) {
|
||||
case TOP:
|
||||
return "顶端对齐";
|
||||
case CENTER:
|
||||
return "居中";
|
||||
case BOTTOM:
|
||||
return "底端对齐";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 相对于
|
||||
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null || anchor.getPositionV() == null) return "未知";
|
||||
if (anchor.getPositionV().getRelativeFrom() != null) {
|
||||
switch (anchor.getPositionV().getRelativeFrom()) {
|
||||
case PAGE:
|
||||
return "页面";
|
||||
case MARGIN:
|
||||
return "页边距";
|
||||
case PARAGRAPH:
|
||||
return "段落";
|
||||
case LINE:
|
||||
return "行";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
|
||||
// 形状和样式
|
||||
public static List<JudgementWordsVO> getStyles(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
String name = "【第" + betoLong + "个图形】【图片】";
|
||||
@@ -1396,6 +1485,11 @@ public static String getSolidFillColor(List<JudgementWordsVO> judgementWordsVOS,
|
||||
return "无";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 获取倒影对象
|
||||
private static CTReflectionEffect getReflectionEffect(Anchor anchor) {
|
||||
if (anchor == null
|
||||
|
@@ -4,6 +4,7 @@ import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.*;
|
||||
import org.docx4j.dml.chartDrawing.CTPicture;
|
||||
import org.docx4j.dml.picture.Pic;
|
||||
@@ -11,6 +12,8 @@ import org.docx4j.dml.wordprocessingDrawing.CTWrapSquare;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
import org.docx4j.dml.wordprocessingDrawing.Inline;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.DocxSetInfo;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.paragraph.Convert;
|
||||
import pc.exam.pp.module.judgement.utils.wps_word.docx4j.vo.JudgementWordsVO;
|
||||
@@ -79,12 +82,206 @@ public class DrawingInline {
|
||||
}
|
||||
|
||||
//布局 环绕方式
|
||||
public static String getLayoutHuanRao(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
// Inline
|
||||
return null;
|
||||
public static String getLayoutHuanRao(List<JudgementWordsVO> judgementWordsVOS, Inline inline, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (inline == null) return "未知";
|
||||
|
||||
try {
|
||||
// 转为 W3C DOM
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(inline).getDocumentElement();
|
||||
|
||||
// 判断环绕方式
|
||||
NodeList wrapTight = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapTight");
|
||||
if (wrapTight.getLength() > 0) return "紧密型";
|
||||
|
||||
NodeList wrapSquare = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapSquare");
|
||||
if (wrapSquare.getLength() > 0) return "四周型";
|
||||
|
||||
NodeList wrapThrough = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapThrough");
|
||||
if (wrapThrough.getLength() > 0) return "穿越型";
|
||||
|
||||
NodeList wrapTopAndBottom = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapTopAndBottom");
|
||||
if (wrapTopAndBottom.getLength() > 0) return "上下型";
|
||||
|
||||
NodeList wrapNone = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "wrapNone");
|
||||
if (wrapNone.getLength() > 0) return "浮于文字上方型";
|
||||
|
||||
// 判断 behindDoc(默认为 false)
|
||||
NodeList behindDocNodes = el.getElementsByTagNameNS("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing", "behindDoc");
|
||||
if (behindDocNodes.getLength() > 0) {
|
||||
String val = behindDocNodes.item(0).getTextContent();
|
||||
if ("1".equals(val) || "true".equalsIgnoreCase(val)) {
|
||||
return "衬于文字下方型";
|
||||
}
|
||||
}
|
||||
|
||||
return "无";
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 对齐方式
|
||||
public static String getLayoutHorizontalAlign(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
// 转为 W3C DOM
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
|
||||
// 获取 <wp:positionH> 节点
|
||||
NodeList posHList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionH"
|
||||
);
|
||||
|
||||
if (posHList.getLength() > 0) {
|
||||
Element posH = (Element) posHList.item(0);
|
||||
NodeList alignList = posH.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"align"
|
||||
);
|
||||
|
||||
if (alignList.getLength() > 0) {
|
||||
String align = alignList.item(0).getTextContent();
|
||||
switch (align.toLowerCase()) {
|
||||
case "left":
|
||||
return "左对齐";
|
||||
case "center":
|
||||
return "居中";
|
||||
case "right":
|
||||
return "右对齐";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "未知";
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 水平 → 相对于
|
||||
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
// 转为 W3C DOM
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
|
||||
// 获取 <wp:positionH> 节点
|
||||
NodeList posHList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionH"
|
||||
);
|
||||
|
||||
if (posHList.getLength() > 0) {
|
||||
Element posH = (Element) posHList.item(0);
|
||||
NodeList relativeList = posH.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"relativeFrom"
|
||||
);
|
||||
|
||||
if (relativeList.getLength() > 0) {
|
||||
String relative = relativeList.item(0).getTextContent();
|
||||
switch (relative.toLowerCase()) {
|
||||
case "column":
|
||||
return "列";
|
||||
case "margin":
|
||||
return "页边距";
|
||||
case "page":
|
||||
return "页面";
|
||||
case "character":
|
||||
return "字符";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "未知";
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 对齐方式
|
||||
public static String getLayoutVerticalAlign(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
NodeList posVList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionV"
|
||||
);
|
||||
|
||||
if (posVList.getLength() > 0) {
|
||||
Element posV = (Element) posVList.item(0);
|
||||
NodeList alignList = posV.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"align"
|
||||
);
|
||||
|
||||
if (alignList.getLength() > 0) {
|
||||
String align = alignList.item(0).getTextContent();
|
||||
switch (align.toLowerCase()) {
|
||||
case "top": return "顶端对齐";
|
||||
case "center": return "居中";
|
||||
case "bottom": return "底端对齐";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 位置 → 垂直 → 相对于
|
||||
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
if (anchor == null) return "未知";
|
||||
|
||||
try {
|
||||
Element el = XmlUtils.marshaltoW3CDomDocument(anchor).getDocumentElement();
|
||||
NodeList posVList = el.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"positionV"
|
||||
);
|
||||
|
||||
if (posVList.getLength() > 0) {
|
||||
Element posV = (Element) posVList.item(0);
|
||||
NodeList relativeList = posV.getElementsByTagNameNS(
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"relativeFrom"
|
||||
);
|
||||
|
||||
if (relativeList.getLength() > 0) {
|
||||
String relative = relativeList.item(0).getTextContent();
|
||||
switch (relative.toLowerCase()) {
|
||||
case "page": return "页面";
|
||||
case "margin": return "页边距";
|
||||
case "paragraph": return "段落";
|
||||
case "line": return "行";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "未知";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
// 形状和样式
|
||||
public static List<JudgementWordsVO> getStyles(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
|
||||
|
@@ -0,0 +1,15 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_word.docx4j.file;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class isFile {
|
||||
|
||||
public static String FileExists() {
|
||||
return "是";
|
||||
|
||||
}
|
||||
|
||||
public static String FileNotExists() {
|
||||
return "否";
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
package pc.exam.pp.module.judgement.utils.wps_word.docx4j.section;
|
||||
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import org.docx4j.XmlUtils;
|
||||
import org.docx4j.dml.Graphic;
|
||||
import org.docx4j.dml.GraphicData;
|
||||
@@ -15,14 +17,26 @@ import org.docx4j.openpackaging.exceptions.Docx4JException;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.docx4j.openpackaging.parts.Part;
|
||||
import org.docx4j.openpackaging.parts.WordprocessingML.*;
|
||||
import org.docx4j.vml.CTFill;
|
||||
import org.docx4j.wml.*;
|
||||
import org.docx4j.wml.CTBackground;
|
||||
import org.docx4j.wml.CTBorder;
|
||||
import org.docx4j.wml.CTColumns;
|
||||
import org.docx4j.wml.CTDocGrid;
|
||||
import org.docx4j.wml.CTShd;
|
||||
import org.docx4j.wml.STPageOrientation;
|
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -1324,25 +1338,6 @@ public class SectionPage {
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ----- 公共方法:获取单个属性(示例) -----
|
||||
//旋转角度
|
||||
public static String getRotationDegree(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
@@ -1372,18 +1367,103 @@ public class SectionPage {
|
||||
public static String getOpacityPercent(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections);
|
||||
if (vmlXml == null) return null;
|
||||
Pattern pattern = Pattern.compile("opacity=\"([0-9a-fA-F]+)f\"");
|
||||
Pattern opacityPattern = Pattern.compile("<v:fill[^>]*opacity=\"([0-9\\.]+)\"");
|
||||
Matcher matcher = opacityPattern.matcher(vmlXml);
|
||||
if (matcher.find()) {
|
||||
String opacityStr = matcher.group(1);
|
||||
float opacityPercent = Float.parseFloat(opacityStr) * 100;
|
||||
return String.format("%.2f%%", opacityPercent);
|
||||
|
||||
// 1. 小数形式,例如 opacity="0.4"
|
||||
Pattern decimalPattern = Pattern.compile("<v:fill[^>]*opacity=\"([0-9\\.]+)\"");
|
||||
Matcher decimalMatcher = decimalPattern.matcher(vmlXml);
|
||||
if (decimalMatcher.find()) {
|
||||
try {
|
||||
float opacity = Float.parseFloat(decimalMatcher.group(1)) * 100;
|
||||
// 反转:Word显示的透明度 = 100 - 实际值
|
||||
opacity = 100 - opacity;
|
||||
// 限制在 0~100%
|
||||
opacity = Math.min(Math.max(opacity, 0), 100);
|
||||
return String.format("%d%%", Math.round(opacity));
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略,尝试解析十六进制
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 匹配十六进制或整数值
|
||||
Pattern hexPattern = Pattern.compile("opacity\\s*=\\s*\"([0-9a-fA-F]+)\"");
|
||||
Matcher hexMatcher = hexPattern.matcher(vmlXml);
|
||||
if (hexMatcher.find()) {
|
||||
String hexStr = hexMatcher.group(1).replaceFirst("(?i)^0x", "");
|
||||
|
||||
// 如果最后一个字符是 'f' 或 'F',去掉
|
||||
if (hexStr.endsWith("f") || hexStr.endsWith("F")) {
|
||||
hexStr = hexStr.substring(0, hexStr.length() - 1);
|
||||
}
|
||||
|
||||
try {
|
||||
int value;
|
||||
|
||||
// 如果是纯数字字符串,直接十进制解析
|
||||
if (hexStr.matches("\\d+")) {
|
||||
value = Integer.parseInt(hexStr);
|
||||
} else {
|
||||
// 否则按16进制解析
|
||||
value = Integer.parseInt(hexStr, 16);
|
||||
}
|
||||
|
||||
// Word 的公式: 透明度 = (value / 65535.0) * 100
|
||||
float percent =100- (value / 65535.0f) * 100;
|
||||
|
||||
// 限制在 0-100
|
||||
percent = Math.min(Math.max(percent, 0), 100);
|
||||
|
||||
return String.format("%d%%", Math.round(percent));
|
||||
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
//水印文字
|
||||
|
||||
|
||||
|
||||
//页面背景→ 填充方式
|
||||
public static String getPageFillType(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
SectPr sectPr = wordMLPackage.getMainDocumentPart().getJaxbElement().getBody().getSectPr();
|
||||
if (sectPr == null) return "无填充";
|
||||
|
||||
// 转为 XML
|
||||
String xml = org.docx4j.XmlUtils.marshaltoString(sectPr, true);
|
||||
|
||||
if (xml == null || xml.isEmpty()) return "无填充";
|
||||
|
||||
XPathFactory xPathfactory = XPathFactory.newInstance();
|
||||
XPath xpath = xPathfactory.newXPath();
|
||||
|
||||
// 检查是否有背景颜色或填充
|
||||
XPathExpression colorExpr = xpath.compile("//*[local-name()='bgColor' or local-name()='shd']");
|
||||
Node colorNode = (Node) colorExpr.evaluate(org.docx4j.XmlUtils.getNewDocumentBuilder().parse(
|
||||
new java.io.ByteArrayInputStream(xml.getBytes())), XPathConstants.NODE);
|
||||
if (colorNode != null) {
|
||||
return "纯色填充";
|
||||
}
|
||||
|
||||
// 检查是否有纹理或图案填充
|
||||
XPathExpression patternExpr = xpath.compile("//*[local-name()='fill' and @*]"); // 一般纹理/图案用 fill 属性
|
||||
Node patternNode = (Node) patternExpr.evaluate(org.docx4j.XmlUtils.getNewDocumentBuilder().parse(
|
||||
new java.io.ByteArrayInputStream(xml.getBytes())), XPathConstants.NODE);
|
||||
if (patternNode != null) {
|
||||
return "纹理/图案填充";
|
||||
}
|
||||
|
||||
// 检查是否有图片背景
|
||||
XPathExpression picExpr = xpath.compile("//*[local-name()='blip']"); // 图片一般是 <a:blip>
|
||||
Node picNode = (Node) picExpr.evaluate(org.docx4j.XmlUtils.getNewDocumentBuilder().parse(
|
||||
new java.io.ByteArrayInputStream(xml.getBytes())), XPathConstants.NODE);
|
||||
if (picNode != null) {
|
||||
return "图片填充";
|
||||
}
|
||||
|
||||
return "无填充";
|
||||
|
||||
}
|
||||
|
||||
//水印文字
|
||||
public static String getWatermarkText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List<SectionWrapper> sections) throws Exception {
|
||||
String vmlXml = getVMLWatermarkXml(paragraph, hfp, wordMLPackage, sections);
|
||||
if (vmlXml == null) return null;
|
||||
|
Reference in New Issue
Block a user