Merge pull request '【修改】文字考点,' (#7) from hyc into master

Reviewed-on: #7
This commit is contained in:
hyc
2025-10-12 19:15:07 +08:00
9 changed files with 336 additions and 84 deletions

View File

@@ -143,7 +143,11 @@ public class MonitorServiceImpl implements MonitorService {
String key = "userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId();
MonitorDO info = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), MonitorDO.class);
if (info == null) {
return 0L;
info=monitorMapper.selectOne(
new QueryWrapper<MonitorDO>()
.eq("task_id", stuMonitorPaperVo.getTaskId())
.eq("stu_id", stuMonitorPaperVo.getStuId())
);
}
//获取属性 判断 专业/课程点位
Long loginTenantId = SecurityFrameworkUtils.getLoginTenantId();
@@ -266,7 +270,7 @@ public class MonitorServiceImpl implements MonitorService {
info.setTemporaryId(stuMonitorPaperVo.getTemporaryId());
info.setIp(stuMonitorPaperVo.getIp());
if (info.getRemainingTime() == null) {
stringRedisTemplate.opsForValue().set("userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId(), JsonUtils.toJsonString(info));
stringRedisTemplate.opsForValue().set(key, JsonUtils.toJsonString(info));
monitorMapper.updateById(info);
return 24 * 60 * 60L; // 1天
} else {
@@ -320,6 +324,7 @@ public class MonitorServiceImpl implements MonitorService {
info.setEndTime(end.format(fmt));
// 判分后更新记录
monitorMapper.updateById(info);
stringRedisTemplate.opsForValue().set(key, JsonUtils.toJsonString(info));
return finalRemaining;
} else {
return 0;
@@ -334,7 +339,7 @@ public class MonitorServiceImpl implements MonitorService {
LocalDateTime target = now.plusSeconds((long) examTime.toLocalTime().toSecondOfDay());
// 没有考场更新结束时间
info.setEndTime(target.format(fmt));
stringRedisTemplate.opsForValue().set("userCache:" + stuMonitorPaperVo.getTaskId() + ":" + stuMonitorPaperVo.getStuId(), JsonUtils.toJsonString(info));
stringRedisTemplate.opsForValue().set(key, JsonUtils.toJsonString(info));
monitorMapper.updateById(info);
return (long) examTime.toLocalTime().toSecondOfDay();
}

View File

@@ -810,7 +810,7 @@ public class ExamQuestionServiceImpl implements IExamQuestionService {
// 批量插入
examQuestionMapper.insertExamPsKeywordList(psKeywords);
}
examQuestionMapper.updateBatch(examQuestion);
examQuestionMapper.updateById(examQuestion);
}
}
}

View File

@@ -139,7 +139,7 @@
<select id="getSchoolNameTotal" resultType="java.lang.Long">
SELECT count(*)
FROM system_tenant
WHERE id != 1 AND deleted = 0
WHERE deleted = 0
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>

View File

@@ -121,6 +121,8 @@
<select id="selectExamSpecialtyBySpId" parameterType="Long" resultMap="ExamSpecialtyResult">
<include refid="selectExamSpecialtyVo"/>
where sp_id = #{spId}
and status = 0
and deleted = 0
</select>
<select id="selectExamSpecialtyBySpNameOne" parameterType="String" resultType="ExamSpecialty">
@@ -147,6 +149,8 @@
SELECT sp_id AS id, sp_name AS name, ancestors, parent_id, status, create_time
from exam_specialty
where parent_id = #{parentId}
and status = 0
and deleted = 0
</select>
<select id="getRoleById" resultType="java.lang.String">
select roles

View File

@@ -141,12 +141,20 @@ public class DocxMaster {
Object[] arguments = {paragraph, stylePart, wordMLPackage, ndp};
String value = (String) methodWithArgs.invoke(obj, arguments);
if (value!=null){
// 写入结果
judgementWordsVOS = setJudgementWord(
judgementWordsVOS,
docxFunction + "@" + value,
firstName + examName + value
);
if ("getParagraphListLvlText".equals(function)||("getParagraphListAbstractNumId").equals(function)||("getParagraphListLvlFuHao").equals(function)){
judgementWordsVOS = setJudgementWord(
judgementWordsVOS,
docxFunction + "@" + value,
firstName + examName + "【是否正确】"
);
}else {
// 写入结果
judgementWordsVOS = setJudgementWord(
judgementWordsVOS,
docxFunction + "@" + value,
firstName + examName + value
);
}
}
}
}

View File

@@ -78,7 +78,7 @@ public class Drawing {
String width = String.format("%.2fcm", widthCm);
return width;
}
//
//布局 锁定横纵比
public static String getLayoutLock(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
boolean lock = anchor.getCNvGraphicFramePr().getGraphicFrameLocks().isNoChangeAspect();
@@ -134,6 +134,15 @@ public class Drawing {
}
return "未知";
}
// 位置 → 水平 → 绝对位置
public static String getLayoutHorizontalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getPositionH() == null) return "未知";
long offsetEmu = anchor.getPositionH().getPosOffset(); // EMU单位
double offsetCm = offsetEmu / 360000.0;
return String.format("相对于右侧%.2fcm", offsetCm);
}
// 位置 → 水平 → 相对于
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
@@ -172,7 +181,15 @@ public class Drawing {
}
return "未知";
}
// 位置 → 垂直 → 绝对位置
public static String getLayoutVerticalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getPositionV() == null) return "未知";
long offsetEmu = anchor.getPositionV().getPosOffset(); // EMU单位
double offsetCm = offsetEmu / 360000.0;
return String.format("相对于下侧%.2fcm", offsetCm);
}
// 位置 → 垂直 → 相对于
public static String getLayoutVerticalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null || anchor.getPositionV() == null) return "未知";

View File

@@ -166,7 +166,14 @@ public class DrawingInline {
return "未知";
}
}
// 位置 → 水平 → 绝对位置
public static String getLayoutHorizontalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
return "";
}
// 位置 → 垂直 → 绝对位置
public static String getLayoutVerticalAbsolute(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
return "";
}
// 位置 → 水平 → 相对于
public static String getLayoutHorizontalRelativeTo(List<JudgementWordsVO> judgementWordsVOS, Inline anchor, int betoLong, WordprocessingMLPackage wordMLPackage) {
if (anchor == null) return "未知";

View File

@@ -102,45 +102,71 @@ public class Paragraphs {
}
/// 段落格式(缩进) 首行缩进 磅
public static String getParagraphIndFirstLine(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
PPrBase.Ind ind = pPr.getInd();
if (ind != null) {
if (ind.getFirstLine() != null) {
return ind.getFirstLine().intValue() + "";
}
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
Double firstLine = null;
if (pPr != null && pPr.getInd() != null && pPr.getInd().getFirstLine() != null) {
firstLine = pPr.getInd().getFirstLine().doubleValue();
} else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null) {
PPrBase.Ind indStyle = style.getPPr().getInd();
if (indStyle.getFirstLine() != null) {
return indStyle.getFirstLine().intValue() + "";
}
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null &&
style.getPPr().getInd().getFirstLine() != null) {
firstLine = style.getPPr().getInd().getFirstLine().doubleValue();
}
}
return "";
if (firstLine == null) return "";
// 1 字符 ≈ 200 twip即 10 磅)
double chars = firstLine / 240.0;
// 四舍五入到 0.5
double rounded = Math.round(chars * 2) / 2.0;
// 如果是整数,去掉小数点
if (rounded == (int) rounded) {
return (int) rounded + " 字符";
} else {
return rounded + " 字符";
}
}
/// 段落格式(缩进) 悬挂缩进 磅
public static String getParagraphIndHanging(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
PPrBase.Ind ind = pPr.getInd();
if (ind != null) {
if (ind.getHanging() != null) {
return ind.getHanging().intValue() + "";
}
Double hanging = null;
// 1⃣ 直接在段落中取
if (pPr != null && pPr.getInd() != null && pPr.getInd().getHanging() != null) {
hanging = pPr.getInd().getHanging().doubleValue();
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
// 2⃣ 样式中取
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null) {
PPrBase.Ind indStyle = style.getPPr().getInd();
if (indStyle.getHanging() != null) {
return indStyle.getHanging().intValue() + "";
}
if (style != null && style.getPPr() != null && style.getPPr().getInd() != null &&
style.getPPr().getInd().getHanging() != null) {
hanging = style.getPPr().getInd().getHanging().doubleValue();
}
}
return "";
if (hanging == null) return "";
// 1 字符 ≈ 200 twip10 磅)
double chars = hanging / 240.0;
// 四舍五入到 0.5
double rounded = Math.round(chars * 2) / 2.0;
// 输出为整数或 .5 字符
if (rounded == (int) rounded) {
return (int) rounded + " 字符";
} else {
return rounded + " 字符";
}
}
/// 段落格式(间距) 段前行
@@ -165,39 +191,75 @@ public class Paragraphs {
}
/// 段落格式(间距) 段前磅
public static String getParagraphSpacingBefore(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
if (pPr != null && pPr.getSpacing() != null) {
if(pPr.getSpacing().getBefore()!=null){
return String.valueOf(pPr.getSpacing().getBefore().intValue() / 20)+"";
}
Double before = null;
// 1⃣ 段落自身定义
if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getBefore() != null) {
before = pPr.getSpacing().getBefore().doubleValue();
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
// 2⃣ 从样式定义中继承
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null) {
PPrBase.Spacing spacing = style.getPPr().getSpacing();
return String.valueOf(style.getPPr().getSpacing().getBefore().intValue() / 20)+"";
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null &&
style.getPPr().getSpacing().getBefore() != null) {
before = style.getPPr().getSpacing().getBefore().doubleValue();
}
}
return "";
if (before == null) return "";
// 转换为磅1磅 = 20twip
double point = before / 20.0;
// 四舍五入到 0.5
double rounded = Math.round(point * 2) / 2.0;
// 输出格式
if (rounded == (int) rounded) {
return (int) rounded + "";
} else {
return rounded + "";
}
}
/// 段落格式(间距) 段后磅
public static String getParagraphSpacingAfter(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
if (paragraph == null) return "";
PPr pPr = paragraph.getPPr();
if (pPr != null && pPr.getSpacing() != null) {
if(pPr.getSpacing().getAfter()!=null){
return String.valueOf(pPr.getSpacing().getAfter().intValue() / 20)+"";
}
Double after = null;
// 1⃣ 段落自身定义
if (pPr != null && pPr.getSpacing() != null && pPr.getSpacing().getAfter() != null) {
after = pPr.getSpacing().getAfter().doubleValue();
}
if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
// 2⃣ 样式定义
else if (pPr != null && pPr.getPStyle() != null && stylePart != null) {
String styleId = pPr.getPStyle().getVal();
Style style = stylePart.getStyleById(styleId);
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null) {
PPrBase.Spacing spacing = style.getPPr().getSpacing();
return String.valueOf(style.getPPr().getSpacing().getAfter().intValue() / 20)+"";
if (style != null && style.getPPr() != null && style.getPPr().getSpacing() != null &&
style.getPPr().getSpacing().getAfter() != null) {
after = style.getPPr().getSpacing().getAfter().doubleValue();
}
}
return "";
if (after == null) return "";
// 转换为磅1 磅 = 20 twip
double point = after / 20.0;
// 四舍五入到 0.5 磅
double rounded = Math.round(point * 2) / 2.0;
// 输出格式:整数不带小数,半磅保留 .5
if (rounded == (int) rounded) {
return (int) rounded + "";
} else {
return rounded + "";
}
}
/// 段落格式(间距) 段后行
@@ -510,6 +572,57 @@ public class Paragraphs {
}
return "";
}
/// 段落 编号列表 编号序号
public static String getParagraphListLvlNumber(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) {
PPr pPr = paragraph.getPPr();
if (pPr == null || pPr.getNumPr() == null) return "";
BigInteger numId = pPr.getNumPr().getNumId() != null ? pPr.getNumPr().getNumId().getVal() : null;
BigInteger ilvl = pPr.getNumPr().getIlvl() != null ? pPr.getNumPr().getIlvl().getVal() : null;
if (numId == null || ilvl == null || ndp == null) return "";
// 获取文档所有段落
List<Object> paragraphs = wordMLPackage.getMainDocumentPart().getContent();
int count = 0;
for (Object obj : paragraphs) {
if (!(obj instanceof P)) continue;
P p = (P) obj;
if (p == paragraph) break; // 到当前段落停止计数
PPr pp = p.getPPr();
if (pp != null && pp.getNumPr() != null) {
BigInteger nid = pp.getNumPr().getNumId() != null ? pp.getNumPr().getNumId().getVal() : null;
BigInteger lvl = pp.getNumPr().getIlvl() != null ? pp.getNumPr().getIlvl().getVal() : null;
if (numId.equals(nid) && ilvl.equals(lvl)) count++;
}
}
// 获取 Lvl 对象
Numbering.Num num = ndp.getJaxbElement().getNum().stream()
.filter(n -> n.getNumId().equals(numId))
.findFirst().orElse(null);
if (num == null) return "";
BigInteger abstractNumId = num.getAbstractNumId().getVal();
Numbering.AbstractNum absNum = Convert.getAbstractNumById(ndp, abstractNumId);
if (absNum == null) return "";
Lvl lvl = absNum.getLvl().stream()
.filter(l -> l.getIlvl().equals(ilvl))
.findFirst().orElse(null);
if (lvl == null) return "";
// 获取编号模板和起始值
String lvlText = lvl.getLvlText() != null ? lvl.getLvlText().getVal() : "%1.";
BigInteger start = lvl.getStart() != null ? lvl.getStart().getVal() : BigInteger.ONE;
// 实际编号
int actualNumber = start.intValue() + count;
// 替换模板中的 %1、%2… 注意 ilvl 从 0 开始
String result = lvlText.replace("%" + (ilvl.intValue() + 1), String.valueOf(actualNumber));
return result;
}
// 段落边框 样式(带汉化)
public static String getParagraphBorderStyle(P paragraph, StyleDefinitionsPart stylePart,

View File

@@ -1,15 +1,15 @@
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 jakarta.xml.bind.JAXBException;
import org.docx4j.TextUtils;
import org.docx4j.XmlUtils;
import org.docx4j.dml.Graphic;
import org.docx4j.dml.GraphicData;
import org.docx4j.dml.wordprocessingDrawing.Anchor;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.mce.AlternateContent;
import org.docx4j.model.structure.DocumentModel;
import org.docx4j.model.fields.FieldUpdater;
import org.docx4j.model.structure.HeaderFooterPolicy;
import org.docx4j.model.structure.PageDimensions;
import org.docx4j.model.structure.SectionWrapper;
@@ -17,29 +17,27 @@ 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.vml.CTTextbox;
import org.docx4j.wml.*;
import org.docx4j.wml.CTBackground;
import org.docx4j.wml.CTBorder;
import org.docx4j.wml.CTColumn;
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 org.xml.sax.SAXException;
import pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils;
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.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -78,7 +76,9 @@ public class SectionPage {
long topTwips = pgMar.getTop().longValue(); // twips
double topPt = topTwips / 20.0; // 转换为磅
double topCm = topPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", topPt, topCm);
// return String.format("%.1f磅(%.2f厘米)", topPt, topCm);
return String.format("%.2f厘米", topCm);
}
}
return null;
@@ -95,7 +95,8 @@ public class SectionPage {
long bottomTwips = pgMar.getBottom().longValue(); // twips
double bottomPt = bottomTwips / 20.0; // 磅
double bottomCm = bottomPt * 0.0352778; // 厘米
return String.format("%.1f磅(%.2f厘米)", bottomPt, bottomCm);
// return String.format("%.1f磅(%.2f厘米)", bottomPt, bottomCm);
return String.format("%.2f厘米", bottomCm);
}
}
return null;
@@ -112,7 +113,8 @@ public class SectionPage {
long leftTwips = pgMar.getLeft().longValue(); // twips
double leftPt = leftTwips / 20.0; // 转换为磅
double leftCm = leftPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", leftPt, leftCm);
// return String.format("%.1f磅(%.2f厘米)", leftPt, leftCm);
return String.format("%.2f厘米", leftCm);
}
}
return null;
@@ -129,7 +131,8 @@ public class SectionPage {
long rightTwips = pgMar.getRight().longValue(); // twips
double rightPt = rightTwips / 20.0; // 转换为磅
double rightCm = rightPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm);
return String.format("%.2f厘米", rightCm);
// return String.format("%.1f磅(%.2f厘米)", rightPt, rightCm);
}
}
return null;
@@ -146,7 +149,8 @@ public class SectionPage {
long gutterTwips = pgMar.getGutter().longValue(); // twips
double gutterPt = gutterTwips / 20.0; // 转换为磅
double gutterCm = gutterPt * 0.0352778; // 转换为厘米
return String.format("%.1f磅(%.2f厘米)", gutterPt, gutterCm);
// return String.format("%.1f磅(%.2f厘米)", gutterPt, gutterCm);
return String.format("%.2f厘米", gutterCm);
}
}
return null;
@@ -1170,33 +1174,130 @@ public class SectionPage {
}
// 页脚(元素) 包含页码 返回 是/否
// 页脚(元素) 页码
public static String getHeaderContainsPageNumber(
P paragraph,
HeaderFooterPolicy hfp,
WordprocessingMLPackage wordMLPackage,
List<SectionWrapper> sections) {
List<SectionWrapper> sections) throws IOException, SAXException, Docx4JException, JAXBException {
if (hfp == null) return "无页码";
if (hfp == null) return "";
// 获取页脚对象列表
// 更新字段,保证获取显示值
FieldUpdater updater = new FieldUpdater(wordMLPackage);
updater.update(true);
FooterPart[] footerParts = new FooterPart[]{
hfp.getFirstFooter(),
hfp.getEvenFooter(),
hfp.getDefaultFooter()
};
Set<String> chineseNumbers = Set.of("","","","","","","","","","");
Set<String> capitalChineseNumbers = Set.of("","","","","","","","","","");
for (FooterPart fp : footerParts) {
if (fp == null) continue;
String xml = XmlUtils.marshaltoString(fp.getJaxbElement(), true, true);
System.out.println("Footer XML:\n" + xml);
// 判断是否包含页码域 (PAGE 或 NUMPAGES)
if (xml.contains("PAGE") || xml.contains("NUMPAGES")) {
return "";
org.w3c.dom.Document doc = XmlUtils.getNewDocumentBuilder()
.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
NodeList pNodes = doc.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "p");
for (int i = 0; i < pNodes.getLength(); i++) {
org.w3c.dom.Element pElem = (org.w3c.dom.Element) pNodes.item(i);
// 检查 PAGE / NUMPAGES
NodeList instrNodes = pElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "instrText");
boolean hasPAGE = false, hasNUMPAGES = false;
for (int j = 0; j < instrNodes.getLength(); j++) {
String instr = instrNodes.item(j).getTextContent();
if (instr.contains("PAGE")) hasPAGE = true;
if (instr.contains("NUMPAGES")) hasNUMPAGES = true;
}
// 拼接 run 内文本 + 字段文本
NodeList rNodes = pElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "r");
StringBuilder textBuilder = new StringBuilder();
for (int j = 0; j < rNodes.getLength(); j++) {
Element rElem = (Element) rNodes.item(j);
NodeList tNodes = rElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "t");
for (int k = 0; k < tNodes.getLength(); k++) {
textBuilder.append(tNodes.item(k).getTextContent());
}
NodeList instrNodesR = rElem.getElementsByTagNameNS(
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "instrText");
for (int k = 0; k < instrNodesR.getLength(); k++) {
textBuilder.append(instrNodesR.item(k).getTextContent());
}
}
String text = textBuilder.toString().trim();
System.out.println("页脚文本内容: " + text);
if (text.isEmpty()) continue;
boolean containsChineseNum = chineseNumbers.stream().anyMatch(text::contains);
boolean containsDigit = text.matches(".*\\d+.*");
// -------------------- 核心逻辑 --------------------
// 数字斜杠序列
if (text.matches(".*\\d+\\s*/\\s*(?:\\d+|PAGE|NUMPAGES).*")) {
return "1 / x";
}
if (containsChineseNum && hasPAGE && hasNUMPAGES) {
return "第一页 共x页"; // WPS 中文 + PAGE + NUMPAGES
}
if (containsDigit && hasPAGE && hasNUMPAGES) {
return "第1页 共x页"; // 普通数字 + PAGE + NUMPAGES
}
if (containsChineseNum && hasPAGE && !hasNUMPAGES) {
return "第一页"; // WPS 中文单页
}
// ✅ 关键部分:支持短横线页码形式
if (text.matches(".*[-]\\s*\\d+\\s*[-].*")) {
return "-1-, -2-, -3- ..."; // 短横线包裹数字
}
if (text.matches(".*[—]\\s*\\d+\\s*[—].*")) {
return "— 1 —, — 2 —, — 3 — ..."; // 数字横杠序列
}
// 半角数字序列
if (text.matches("\\d+\\s*PAGE.*\\d+\\s*PAGE.*")) {
return "1, 2, 3 ..."; // 数字序列
}
// 全角数字序列
if (text.matches(".*[-]+\\s*PAGE.*[-]+\\s*PAGE.*")) {
return " ...";
}
if (containsDigit && hasPAGE && !hasNUMPAGES) {
return "第1页"; // 普通数字单页
}
if (text.matches("[IVXLCDM]+.*")) {
return "I, II, III ..."; // 罗马数字
}
if (text.matches("[A-Z]+\\d*.*")) {
return "A, B, C ..."; // 大写字母
}
if (text.matches("[a-z]+\\d*.*")) {
return "a, b, c ..."; // 小写字母
}
}
}
return "";
return "无页码";
}
@@ -1204,9 +1305,6 @@ public class SectionPage {
// public static void readTextWatermark(
// P paragraph,
// HeaderFooterPolicy hfp,