From f9b588119dd3e845fa265e346ad595b0e6f516e7 Mon Sep 17 00:00:00 2001 From: huababa1 <2037205722@qq.com> Date: Mon, 25 Aug 2025 21:03:08 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E4=BF=AE=E6=94=B9=E3=80=91=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eword=E8=80=83=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wps_excel/xlsx4j/chart/ChartHandler.java | 120 +++- .../wps_excel/xlsx4j/table/TableIng.java | 2 +- .../wps_word/docx4j/drawing/Drawing.java | 165 ++--- .../wps_word/docx4j/paragraph/Paragraphs.java | 24 +- .../wps_word/docx4j/section/SectionPage.java | 595 ++++++++++++++---- 5 files changed, 616 insertions(+), 290 deletions(-) diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java index c5c3bed1..e02dba2c 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/chart/ChartHandler.java @@ -16,6 +16,8 @@ import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphical import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTPivotTableDefinition; import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.XMLConstants; @@ -24,8 +26,10 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Iterator; @@ -329,22 +333,62 @@ public class ChartHandler { /** 图表-设计 - 图表样式 */ public String getChartStyle(XSSFChart chart, XSSFSheet sheet) { if (chart == null) return "无图表"; -// -// CTChart ctChart = chart.getCTChart(); -// if (ctChart == null) return "无样式"; -// -// CTPlotArea plotArea = ctChart.getPlotArea(); -// if (plotArea != null && plotArea.getExtLst() != null) { -// // 可以进一步解析 extLst 获取自定义样式信息 -// } -// -// // 尝试直接从 plotArea 或 chart XML 属性里获取 style -// if (ctChart.getDomNode().getAttributes().getNamedItem("style") != null) { -// String style = ctChart.getDomNode().getAttributes().getNamedItem("style").getNodeValue(); -// return "样式" + style; -// } + try { + Node chartNode = chart.getCTChartSpace().getDomNode(); - return "默认样式"; + // 先尝试取 c14:style + NodeList c14StyleNodes = ((Element) chartNode) + .getElementsByTagNameNS("http://schemas.microsoft.com/office/drawing/2007/8/2/chart", "style"); + + if (c14StyleNodes.getLength() > 0) { + String val = ((Element) c14StyleNodes.item(0)).getAttribute("val"); + int styleId = Integer.parseInt(val); + // 映射 101–114 → 1–14 + if (styleId >= 101 && styleId <= 114) { + styleId -= 100; + } + return "样式" + styleId; + } + + // 再尝试取 c:style + NodeList cStyleNodes = ((Element) chartNode) + .getElementsByTagNameNS("http://schemas.openxmlformats.org/drawingml/2006/chart", "style"); + + if (cStyleNodes.getLength() > 0) { + String val = ((Element) cStyleNodes.item(0)).getAttribute("val"); + int styleId = Integer.parseInt(val); + return "样式" + styleId; + } + + return "未定义样式"; + } catch (Exception e) { + e.printStackTrace(); + return "解析图表样式时出错: " + e.getMessage(); + } + } + + /** + * 打印图表对应的 XML + */ + public static void printChartXml(XSSFChart chart) { + if (chart == null) { + System.out.println("无图表"); + return; + } + + try { + PackagePart part = chart.getPackagePart(); + try (InputStream is = part.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("读取图表XML失败: " + e.getMessage()); + } } @@ -375,8 +419,18 @@ public class ChartHandler { return "无"; } + /** + * 获取所有可用图表样式名称列表 + */ + public static String[] getAllChartStyles() { + String[] styles = new String[14]; + for (int i = 0; i < 14; i++) { + styles[i] = "样式" + (i + 1); + } + return styles; + } - /** 获取图表宽度,返回"磅(厘米)" */ + /** 获取图表宽度,返回"磅(厘米)" */ //todo public static String getChartWidth(XSSFChart chart, XSSFSheet sheet) { if (chart == null || sheet == null) return "无"; @@ -520,23 +574,23 @@ public class ChartHandler { } - public void printChartXml(XSSFChart chart) { - if (chart == null) { - System.out.println("无图表"); - return; - } - - try { - PackagePart part = chart.getPackagePart(); - try (InputStream is = part.getInputStream()) { - String xml = new String(is.readAllBytes(), StandardCharsets.UTF_8); - System.out.println("===== chart.xml 内容 ====="); - System.out.println(xml); - } - } catch (Exception e) { - e.printStackTrace(); - } - } +// public void printChartXml(XSSFChart chart) { +// if (chart == null) { +// System.out.println("无图表"); +// return; +// } +// +// try { +// PackagePart part = chart.getPackagePart(); +// try (InputStream is = part.getInputStream()) { +// String xml = new String(is.readAllBytes(), StandardCharsets.UTF_8); +// System.out.println("===== chart.xml 内容 ====="); +// System.out.println(xml); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java index 38336eab..6fdbf2ae 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_excel/xlsx4j/table/TableIng.java @@ -67,7 +67,7 @@ public class TableIng { Node attr = node.getAttributes().getNamedItem("name"); if (attr != null) { // 适应不同格式 - String val = attr.getNodeValue().toLowerCase(); + String val = attr.getNodeValue().toLowerCase(); return val; } return "无"; diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java index b39eb018..2fc35bb3 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/drawing/Drawing.java @@ -232,7 +232,7 @@ public class Drawing { if (pic == null) { // 处理未找到图片的情况 - return null; + return "无"; } // 继续处理 pic,获取轮廓颜色 @@ -247,7 +247,7 @@ public class Drawing { } } - return null; + return "无"; } @@ -268,7 +268,7 @@ public class Drawing { if (pic == null) { // 处理找不到图片的情况 - return null; + return "无"; } if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { @@ -280,7 +280,7 @@ public class Drawing { } - return null; + return "无"; } @@ -300,7 +300,7 @@ public class Drawing { if (pic == null) { // 找不到图片对象,返回 null - return null; + return "无"; } if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { @@ -311,7 +311,7 @@ public class Drawing { } } - return null; + return "无"; } // 形状轮廓 虚线(短划线类型) @@ -329,7 +329,7 @@ public class Drawing { } if (pic == null) { - return null; + return "无"; } if (pic.getSpPr() != null && pic.getSpPr().getLn() != null) { @@ -339,7 +339,7 @@ public class Drawing { } } - return null; + return "无"; } @@ -369,14 +369,14 @@ public class Drawing { if (!(value instanceof Pic)) { System.out.println("value不是Pic类型,实际类型:" + value.getClass().getName()); - return null; + return "无"; } Pic pic = (Pic) value; if (pic.getSpPr() == null || pic.getSpPr().getLn() == null) { System.out.println("ln为空,无法获取轮廓透明度"); - return null; + return "无"; } CTLineProperties ln = pic.getSpPr().getLn(); @@ -384,7 +384,7 @@ public class Drawing { if (solidFill == null) { System.out.println("solidFill为空"); - return null; + return "无"; } // 优先处理 srgbClr @@ -458,7 +458,7 @@ public class Drawing { } } - return null; + return "无"; } @@ -601,7 +601,7 @@ public class Drawing { } } - return "未知"; + return "无"; } //模糊 @@ -768,7 +768,7 @@ public class Drawing { } catch (Exception e) { // ignore } - return null; + return "无"; } private static Integer tryGetVal(Object alphaObj) { @@ -893,23 +893,23 @@ public class Drawing { CTShapeProperties spPr = getShapeProperties(anchor); - if (spPr == null) return null; + if (spPr == null) return "无"; if (spPr.getSolidFill() != null) return "纯色填充"; if (spPr.getBlipFill() != null) return "图片填充"; if (spPr.getPattFill() != null) return "图案填充"; - return null; + return "无"; } // marshalShapeProperties(spPr); public static String getSolidFillColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTShapeProperties spPr = getShapeProperties(anchor); if (spPr == null) { - return null; + return "无"; } CTSolidColorFillProperties solidFill = spPr.getSolidFill(); if (solidFill == null) { - return null; + return "无"; } if (solidFill.getSrgbClr() != null) { @@ -924,16 +924,16 @@ public static String getSolidFillColor(List judgementWordsVOS, } System.out.println("全部颜色属性均为空!"); - return null; + return "无"; } // 纯色填充→透明度 public static String getPictureFillTransparency(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTShapeProperties spPr = getShapeProperties(anchor); - if (spPr == null) return null; + if (spPr == null) return "无"; CTSolidColorFillProperties solidFill = spPr.getSolidFill(); - if (solidFill == null) return null; + if (solidFill == null) return "无"; // 检查 srgbClr if (solidFill.getSrgbClr() != null) { @@ -1012,18 +1012,18 @@ public static String getSolidFillColor(List judgementWordsVOS, } if (!(value instanceof Pic)) { - return "无纹理填充透明度"; + return "无"; } Pic pic = (Pic) value; if (pic.getSpPr() == null || pic.getSpPr().getBlipFill() == null) { - return "无纹理填充透明度"; + return "无"; } Object blipObj = pic.getSpPr().getBlipFill().getBlip(); if (blipObj == null) { - return "无纹理填充透明度"; + return "无"; } // 调用 getAlphaBiLevelOrAlphaCeilingOrAlphaFloor 方法 @@ -1069,7 +1069,7 @@ public static String getSolidFillColor(List judgementWordsVOS, return "读取异常"; } - return "无纹理填充透明度"; + return "无"; } private static String getAlphaModFixTransparencyFromBlipFill(Object blipFill) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { @@ -1182,38 +1182,38 @@ public static String getSolidFillColor(List judgementWordsVOS, //图案填充→图案 public static String getPatternFillPattern(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTPatternFillProperties pattFill = getShapeProperties(anchor) == null ? null : getShapeProperties(anchor).getPattFill(); - if (pattFill == null) return "无图案填充"; + if (pattFill == null) return "无"; // 这里直接返回枚举toString或value(),也可以直接用toString() Object prst = pattFill.getPrst(); if (prst != null) { return prst.toString(); } - return "未知图案"; + return "无"; } //图案填充→前景色 public static String getPatternFillForeColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTPatternFillProperties pattFill = getShapeProperties(anchor) == null ? null : getShapeProperties(anchor).getPattFill(); - if (pattFill == null) return "无图案填充"; + if (pattFill == null) return "无"; if (pattFill.getFgClr() != null) { System.out.println(extractColor(pattFill.getFgClr())); return ColorUtils.getChineseColorName(extractColor(pattFill.getFgClr())); } - return "无前景色"; + return "无"; } //图案填充→背景色 public static String getPatternFillBackColor(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTPatternFillProperties pattFill = getShapeProperties(anchor) == null ? null : getShapeProperties(anchor).getPattFill(); - if (pattFill == null) return "无图案填充"; + if (pattFill == null) return "无"; if (pattFill.getBgClr() != null) { System.out.println(extractColor(pattFill.getBgClr())); return ColorUtils.getChineseColorName(extractColor(pattFill.getBgClr())); } - return "无背景色"; + return "无"; } //形状效果(倒影) @@ -1242,7 +1242,7 @@ public static String getSolidFillColor(List judgementWordsVOS, // 2. 预设类型(shadow preset) public static String getShadowPreset(List 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()); @@ -1265,17 +1265,17 @@ public static String getSolidFillColor(List judgementWordsVOS, || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null || anchor.getGraphic().getGraphicData().getPic() == null) { - return "无倒影"; + return "无"; } CTShapeProperties spPr = anchor.getGraphic().getGraphicData().getPic().getSpPr(); - if (spPr == null) return "无倒影"; + if (spPr == null) return "无"; CTEffectList effectList = spPr.getEffectLst(); - if (effectList == null) return "无倒影"; + if (effectList == null) return "无"; CTReflectionEffect reflection = effectList.getReflection(); - if (reflection == null) return "无倒影"; + if (reflection == null) return "无"; // 读取起始透明度 stA 和结束透明度 endA,类型是 Integer Integer stA = reflection.getStA(); // 起始透明度 @@ -1312,11 +1312,11 @@ public static String getSolidFillColor(List judgementWordsVOS, marshaller.marshal(value, writer); System.out.println(writer.toString()); CTReflectionEffect shadow = getReflectionEffect(anchor); - if (shadow == null) return "无大小"; + if (shadow == null) return "无"; // 这里使用 sy 代表倒影大小比例,单位是 1/100000 Integer sy = shadow.getEndPos(); - if (sy == null) return "无大小"; + if (sy == null) return "无"; // 转换为百分比,sy=100000表示100% // 计算绝对值,因为sy可能是负值(表示倒影翻转) @@ -1329,10 +1329,10 @@ public static String getSolidFillColor(List judgementWordsVOS, // 5. 模糊半径(单位磅) public static String getShadowBlurs(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTReflectionEffect reflection = getReflectionEffect(anchor); - if (reflection == null) return "无模糊"; + if (reflection == null) return "无"; Integer blurRad = Math.toIntExact(reflection.getBlurRad()); - if (blurRad == null) return "无模糊"; + if (blurRad == null) return "无"; // EMU转磅 double blurPt = blurRad * 72.0 / 914400.0; @@ -1342,10 +1342,10 @@ public static String getSolidFillColor(List judgementWordsVOS, // 6. 距离(单位磅) public static String getShadowDistances(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { CTReflectionEffect reflection = getReflectionEffect(anchor); - if (reflection == null) return "无距离"; + if (reflection == null) return "无"; Integer dist = Math.toIntExact(reflection.getDist()); - if (dist == null) return "无距离"; + if (dist == null) return "无"; // EMU转磅 double distPt = dist * 72.0 / 914400.0; @@ -1375,7 +1375,7 @@ public static String getSolidFillColor(List judgementWordsVOS, // 图片-形状效果→柔化边缘→大小(磅) public static String getSoftEdgeSize(List judgementWordsVOS, Anchor anchor, int betoLong, WordprocessingMLPackage wordMLPackage) { if (anchor == null || anchor.getGraphic() == null || anchor.getGraphic().getGraphicData() == null) - return "无柔化"; + return "无"; Object anyObj = anchor.getGraphic().getGraphicData().getAny().get(0); Object obj = (anyObj instanceof JAXBElement) ? ((JAXBElement) anyObj).getValue() : anyObj; @@ -1393,24 +1393,7 @@ public static String getSolidFillColor(List judgementWordsVOS, } catch (Exception e) { e.printStackTrace(); } - return "无柔化"; - } - - - - // ----- 辅助方法 ----- - private static BigInteger getReflectionSize(CTReflectionEffect reflection) { - if (reflection == null) return null; - try { - Method m = reflection.getClass().getMethod("getSz"); - Object val = m.invoke(reflection); - if (val instanceof BigInteger) { - return (BigInteger) val; - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; + return "无"; } // 获取倒影对象 @@ -1433,59 +1416,6 @@ public static String getSolidFillColor(List judgementWordsVOS, return reflection; } - - - - // 从shadow中获取透明度alpha,返回整数(0~100000) - private static Integer getAlphaVal(CTReflectionEffect reflection) { - if (reflection == null) return null; - - try { - // 先尝试用反射取所有get开头的方法,找透明度相关的 - for (Method method : reflection.getClass().getMethods()) { - String name = method.getName(); - if (name.startsWith("get") && - (name.toLowerCase().contains("alpha") || name.toLowerCase().contains("amt"))) { - Object val = method.invoke(reflection); - if (val != null) { - // 如果是BigInteger或Integer,返回 - if (val instanceof BigInteger) { - return ((BigInteger) val).intValue(); - } else if (val instanceof Integer) { - return (Integer) val; - } else if (val.getClass().getName().contains("CTPositiveFixedAngle") || - val.getClass().getName().contains("CTAlpha")) { - // 进一步递归找val属性 - Method getValMethod = val.getClass().getMethod("getVal"); - Object v = getValMethod.invoke(val); - if (v instanceof BigInteger) return ((BigInteger) v).intValue(); - if (v instanceof Integer) return (Integer) v; - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - // 找不到就返回null - return null; - } - - - - - - - - - - - - - - - public static CTShapeProperties getShapeProperties(Anchor anchor) { if (anchor == null || anchor.getGraphic() == null @@ -1523,13 +1453,6 @@ public static String getSolidFillColor(List judgementWordsVOS, return null; } - - - - - - - public static String extractColor(CTColor ctColor) { if (ctColor == null) return "无色"; diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java index 76e03c5d..77e411f2 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/paragraph/Paragraphs.java @@ -583,17 +583,35 @@ public class Paragraphs { return "无"; } - // 段落底纹 - 填充颜色 - public static String getParagraphShdFillColor(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { + // 段落底纹 - 填充颜色(转为 rgb(r,g,b)) + public static String getParagraphShdFillColor(P paragraph, + StyleDefinitionsPart stylePart, + WordprocessingMLPackage wordMLPackage, + NumberingDefinitionsPart ndp) { // 先查询自身段落数据 PPr pPr = paragraph.getPPr(); if (pPr != null && pPr.getShd() != null) { CTShd shd = pPr.getShd(); - return shd.getFill(); + String hex = shd.getFill(); // 例如 "FF0000" + + if (hex != null && !hex.equals("auto") && !hex.equals("none")) { + try { + // 有些颜色可能写成三位或八位,先规范化 + if (hex.length() == 6) { + int r = Integer.valueOf(hex.substring(0, 2), 16); + int g = Integer.valueOf(hex.substring(2, 4), 16); + int b = Integer.valueOf(hex.substring(4, 6), 16); + return String.format("RGB(%d,%d,%d)", r, g, b); + } + } catch (Exception e) { + return "无"; + } + } } return "无"; } + // 段落底纹 - 图案样式 public static String getParagraphShdStyle(P paragraph, StyleDefinitionsPart stylePart, WordprocessingMLPackage wordMLPackage, NumberingDefinitionsPart ndp) { // 先查询自身段落数据 diff --git a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java index e2163728..2f6b7484 100644 --- a/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java +++ b/exam-module-judgement/exam-module-judgement-biz/src/main/java/pc/exam/pp/module/judgement/utils/wps_word/docx4j/section/SectionPage.java @@ -6,24 +6,24 @@ 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.structure.HeaderFooterPolicy; import org.docx4j.model.structure.SectionWrapper; +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.wml.*; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageBorders; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz; - +import org.docx4j.wml.CTBorder; +import org.docx4j.wml.CTColumns; +import org.docx4j.wml.CTDocGrid; +import org.docx4j.wml.STPageOrientation; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; import javax.xml.namespace.QName; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -165,6 +165,149 @@ public class SectionPage { return linesPerPage + "行/页"; } + // 节 每页栏数 + public static String getSectionPageLanNumber(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (paragraph == null || paragraph.getPPr() == null) return null; + + SectPr sectPr = paragraph.getPPr().getSectPr(); + if (sectPr == null) return null; + + // 获取栏信息 + CTColumns cols = sectPr.getCols(); + if (cols == null) return "1栏/页"; // 默认单栏 + + // 获取栏数 + BigInteger num = cols.getNum(); + int colNum = (num != null ? num.intValue() : 1); + + return colNum + "栏/页"; + } + + // 获取节的字体方向(分为水平,垂直,中文旋转270°) + public static String getSectionTextDirection(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + if (paragraph == null || paragraph.getPPr() == null) return "未知"; + + SectPr sectPr = paragraph.getPPr().getSectPr(); + if (sectPr == null) return "无"; + + // 获取文字方向 + TextDirection textDir = sectPr.getTextDirection(); + if (textDir == null || textDir.getVal() == null) { + return "水平"; // 默认水平方向 + } + + String val = String.valueOf(textDir.getVal()); + switch (val) { + case "lrTb": + return "水平"; + case "tbRl": + return "垂直"; + case "lrTbV": + return "中文旋转270°"; + default: + return "未知(" + val + ")"; + } + } + // 每行字符数 +// 每行字符数 + 每页行数 +// public static String getSectionPageCharNumber(P paragraph, +// HeaderFooterPolicy hfp, +// WordprocessingMLPackage wordMLPackage, +// List sections) { +// try { +// if (sections == null || sections.isEmpty()) { +// return "【没有节信息】"; +// } +// +// // 这里只取第一个节(通常文档只有一个节,如果有多个可以 for 循环) +// SectionWrapper sectionWrapper = sections.get(0); +// SectPr sectPr = sectionWrapper.getSectPr(); +// if (sectPr == null) { +// return "【无 SectPr 信息】"; +// } +// System.out.println("=============="); +// +// +// +// // 页面属性 +// SectPr.PgSz pgSz = sectPr.getPgSz(); +// SectPr.PgMar pgMar = sectPr.getPgMar(); +// if (pgSz == null || pgMar == null) { +// return "【无页面设置】"; +// } +// +// int pageW = pgSz.getW().intValue(); // 页面宽度 (twip) +// int pageH = pgSz.getH().intValue(); // 页面高度 (twip) +// int left = pgMar.getLeft().intValue(); +// int right = pgMar.getRight().intValue(); +// int top = pgMar.getTop().intValue(); +// int bottom = pgMar.getBottom().intValue(); +// +// // 可用区域 +// int availableWidth = pageW - left - right; +// int availableHeight = pageH - top - bottom; +// +// // 网格设置 +// CTDocGrid grid = sectPr.getDocGrid(); +// if (grid == null) { +// return "【未设置文档网格】"; +// } +// +// StringBuilder sb = new StringBuilder("【页面设置(文档网格)】"); +// +// // 每行字符数 +// if (grid.getCharSpace() != null) { +// int charSpace = grid.getCharSpace().intValue(); +// if (charSpace > 0) { +// int charsPerLine = Math.round((float) availableWidth / charSpace); +// sb.append("【每行字符数】").append(charsPerLine).append(" 字/行"); +// } +// } +// +// // 每页行数 +// if (grid.getLinePitch() != null) { +// int linePitch = grid.getLinePitch().intValue(); +// if (linePitch > 0) { +// int linesPerPage = Math.round((float) availableHeight / linePitch); +// sb.append("【每页行数】").append(linesPerPage).append(" 行/页"); +// } +// } +// +// return sb.toString(); +// +// } catch (Exception e) { +// return "【计算异常】" + e.getMessage(); +// } +// } + + + + /** + * 分栏情况下取单栏宽度 + */ + private static int adjustForColumns(int totalWidthTwips, CTColumns cols) { + if (cols == null) return totalWidthTwips; + + // 显式设置了 优先用第一栏 + if (cols.getCol() != null && !cols.getCol().isEmpty()) { + CTColumn ctColumn = cols.getCol().get(0); + if (ctColumn.getW() != null && ctColumn.getW().intValue() > 0) { + return ctColumn.getW().intValue(); + } + } + + int num = cols.getNum() != null ? cols.getNum().intValue() : 1; + if (num <= 1) return totalWidthTwips; + + int space = cols.getSpace() != null ? cols.getSpace().intValue() : 720; // 默认 0.5" + int width = totalWidthTwips - (num - 1) * space; + if (width <= 0) return totalWidthTwips; + return width / num; + } + + + + // 节页面设置 - 纸张方向 public static String getSectionPageOrientation(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { PPr pPr = paragraph.getPPr(); @@ -286,20 +429,20 @@ public class SectionPage { // ----- 奇数页眉 ----- //奇数页眉文本 - public static String getOddHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).text : null; } //奇数页眉字体 - public static String getOddHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).font : null; } //奇数页眉字号 - public static String getOddHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { HeaderInfo hi = hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage) : null; return (hi != null && hi.fontSizePt != null) ? hi.fontSizePt + " pt" : null; } //奇数页眉颜色 - public static String getOddHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultHeader() == null) { return null; } @@ -308,13 +451,13 @@ public class SectionPage { } // 奇数页眉对齐方式 - public static String getOddHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { String align = hfp.getDefaultHeader() != null ? extractHeaderInfo(hfp.getDefaultHeader(),wordMLPackage).alignment : null; return convertAlignToChinese(align); } //加粗 - public static String getOddHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultHeader() == null) { return null; } @@ -322,7 +465,7 @@ public class SectionPage { return bold; } //倾斜 - public static String getOddHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultHeader() == null) { return null; } @@ -332,20 +475,20 @@ public class SectionPage { // ----- 偶数页眉 ----- //偶数页眉文本 - public static String getEvenHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).text : null; } //偶数页眉字体 - public static String getEvenHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).font : null; } //偶数页眉字号 - public static String getEvenHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { HeaderInfo hi = hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage) : null; return (hi != null && hi.fontSizePt != null) ? hi.fontSizePt + " pt" : null; } //偶数页眉颜色 - public static String getEvenHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenHeader() == null) { return null; } @@ -354,12 +497,12 @@ public class SectionPage { } // 偶数页眉对齐方式 - public static String getEvenHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { String align = hfp.getEvenHeader() != null ? extractHeaderInfo(hfp.getEvenHeader(),wordMLPackage).alignment : null; return convertAlignToChinese(align); } //加粗 - public static String getEvenHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenHeader() == null) { return null; } @@ -367,7 +510,7 @@ public class SectionPage { return bold; } //倾斜 - public static String getEvenHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenHeader() == null) { return null; } @@ -376,20 +519,20 @@ public class SectionPage { } // ----- 首页眉 ----- //首页眉文本 - public static String getFirstHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).text : null; } //首页眉字体 - public static String getFirstHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).font : null; } //首页眉字号 - public static String getFirstHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { HeaderInfo hi = hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage) : null; return (hi != null && hi.fontSizePt != null) ? hi.fontSizePt + " pt" : null; } //首页眉颜色 - public static String getFirstHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstHeader() == null) { return null; } @@ -401,12 +544,12 @@ public class SectionPage { } // 首页眉对齐方式 - public static String getFirstHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderAlignment(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { String align = hfp.getFirstHeader() != null ? extractHeaderInfo(hfp.getFirstHeader(),wordMLPackage).alignment : null; return convertAlignToChinese(align); } //加粗 - public static String getFirstHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstHeader() == null) { return null; } @@ -414,7 +557,7 @@ public class SectionPage { return bold; } //倾斜 - public static String getFirstHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstHeaderItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstHeader() == null) { return null; } @@ -450,32 +593,32 @@ public class SectionPage { } // 奇数页 //字体 - public static String getOddFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); return info == null ? null : info.font; } //字号 - public static String getOddFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); return info == null ? null : String.valueOf(info.fontSizePt); } //对齐方式 - public static String getOddFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage); return convertAlignToChinese(info.alignment); } //颜色 - public static String getOddFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) return null; String colorHex = extractFooterInfo(hfp.getDefaultFooter(),wordMLPackage).colorHex; return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); } - public static String getOddFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) return null; String text = extractFooterInfo(hfp.getDefaultFooter(), wordMLPackage).text; if (text==null){ @@ -487,7 +630,7 @@ public class SectionPage { //加粗 - public static String getOddFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) { return null; } @@ -495,7 +638,7 @@ public class SectionPage { return bold; } //倾斜 - public static String getOddFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getOddFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getDefaultFooter() == null) { return null; } @@ -507,36 +650,36 @@ public class SectionPage { // 偶数页 //字体 - public static String getEvenFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); return info == null ? null : info.font; } //字号 - public static String getEvenFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); return String.valueOf(info == null ? null : info.fontSizePt); } - public static String getEvenFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getEvenFooter(), wordMLPackage); return convertAlignToChinese(info.alignment); } - public static String getEvenFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getEvenFooter() != null ? extractFooterInfo(hfp.getEvenFooter(),wordMLPackage).text : null; } //颜色 - public static String getEvenFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenFooter() == null) return null; String colorHex = extractFooterInfo(hfp.getEvenFooter(),wordMLPackage).colorHex; return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); } //加粗 - public static String getEvenFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenFooter() == null) { return null; } @@ -544,7 +687,7 @@ public class SectionPage { return bold; } //倾斜 - public static String getEvenFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getEvenFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getEvenFooter() == null) { return null; } @@ -552,34 +695,34 @@ public class SectionPage { return italic; } // ===== 首页页脚 ===== - public static String getFirstFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterFont(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); return info == null ? null : info.font; } - public static String getFirstFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterFontSize(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); return info == null ? null : String.valueOf(info.fontSizePt); } - public static String getFirstFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterAlign(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstFooter() == null) return null; HeaderInfo info = extractFooterInfo(hfp.getFirstFooter(), wordMLPackage); return convertAlignToChinese(info.alignment); } - public static String getFirstFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterColor(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstFooter() == null) return null; String colorHex = extractFooterInfo(hfp.getFirstFooter(),wordMLPackage).colorHex; return pc.exam.pp.module.judgement.utils.wps_word.utils.ColorUtils.getChineseColorName(colorHex); } - public static String getFirstFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterText(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { return hfp.getFirstFooter() != null ? extractFooterInfo(hfp.getFirstFooter(),wordMLPackage).text : null; } //加粗 - public static String getFirstFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterBold(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstFooter() == null) { return null; } @@ -587,7 +730,7 @@ public class SectionPage { return bold; } //倾斜 - public static String getFirstFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + public static String getFirstFooterItalic(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) throws Docx4JException { if (hfp.getFirstFooter() == null) { return null; } @@ -641,25 +784,19 @@ public class SectionPage { } - public static HeaderInfo extractFooterInfo(FooterPart footerPart, WordprocessingMLPackage wordMLPackage) { + public static HeaderInfo extractFooterInfo(FooterPart footerPart, WordprocessingMLPackage wordMLPackage) throws Docx4JException { HeaderInfo info = new HeaderInfo(); StringBuilder textBuilder = new StringBuilder(); - List contents = footerPart.getContent(); - for (Object obj : contents) { - if (obj instanceof P) { - P paragraph = (P) obj; - PPr pPr = paragraph.getPPr(); - String styleId = (pPr != null && pPr.getPStyle() != null) ? pPr.getPStyle().getVal() : null; + if (footerPart == null || footerPart.getContents() == null) { + return info; + } + Ftr ftr = footerPart.getContents(); - // 对齐方式 - if (pPr != null && pPr.getJc() != null) { - info.alignment = pPr.getJc().getVal().value(); - } - - for (Object o : paragraph.getContent()) { - extractTextFromContent(o, textBuilder, info, styleId, wordMLPackage); - } + for (Object block : ftr.getContent()) { + Object u = XmlUtils.unwrap(block); + if (u instanceof P) { + parseParagraph((P) u, info, textBuilder, wordMLPackage); } } @@ -667,6 +804,208 @@ public class SectionPage { return info; } + private static void parseParagraph(P paragraph, HeaderInfo info, StringBuilder textBuilder, WordprocessingMLPackage pkg) { + // 对齐方式 + if (paragraph.getPPr() != null && paragraph.getPPr().getJc() != null) { + info.alignment = paragraph.getPPr().getJc().getVal().value(); + } + for (Object o : paragraph.getContent()) { + Object unwrapped = XmlUtils.unwrap(o); + + if (unwrapped instanceof R) { + R run = (R) unwrapped; + + // 先拿到 run 自己的样式(外层 w:rPr,可能是 9pt、无色) + applyRunStyle(run.getRPr(), info); + + for (Object rc : run.getContent()) { + Object inner = XmlUtils.unwrap(rc); + + if (inner instanceof Text) { + textBuilder.append(((Text) inner).getValue()); + } + // 关键:Run 里常见的是 AlternateContent,而不是直接 Drawing + else if (inner instanceof AlternateContent) { + extractFromAlternateContent((AlternateContent) inner, info, textBuilder, pkg); + } + else if (inner instanceof Drawing) { // 少量文档会直接是 Drawing + extractFromDrawing((Drawing) inner, info, textBuilder, pkg); + } + else if (inner instanceof Pict) { // 旧版 VML fallback + extractFromPict((Pict) inner, info, textBuilder, pkg); + } + } + } + // 有些极端情况下,文本框会直接挂在段落(不是 run),也处理一下 + else if (unwrapped instanceof AlternateContent) { + extractFromAlternateContent((AlternateContent) unwrapped, info, textBuilder, pkg); + } else if (unwrapped instanceof Drawing) { + extractFromDrawing((Drawing) unwrapped, info, textBuilder, pkg); + } else if (unwrapped instanceof Pict) { + extractFromPict((Pict) unwrapped, info, textBuilder, pkg); + } + } + } + + private static void extractFromAlternateContent( + AlternateContent ac, + HeaderInfo info, + StringBuilder textBuilder, + WordprocessingMLPackage pkg) { + + // 处理 Choice 部分 + for (AlternateContent.Choice choice : ac.getChoice()) { + for (Object any : choice.getAny()) { + Object child = XmlUtils.unwrap(any); + if (child instanceof Drawing) { + extractFromDrawing((Drawing) child, info, textBuilder, pkg); + } else if (child instanceof Pict) { + extractFromPict((Pict) child, info, textBuilder, pkg); + } else if (child instanceof P) { + // 如果里面直接有段落 + extractFromParagraph((P) child, info, textBuilder, pkg); + } + } + } + + // 处理 Fallback 部分 + AlternateContent.Fallback fb = ac.getFallback(); + if (fb != null) { + for (Object any : fb.getAny()) { + Object child = XmlUtils.unwrap(any); + if (child instanceof Drawing) { + extractFromDrawing((Drawing) child, info, textBuilder, pkg); + } else if (child instanceof Pict) { + extractFromPict((Pict) child, info, textBuilder, pkg); + } else if (child instanceof P) { + extractFromParagraph((P) child, info, textBuilder, pkg); + } + } + } + } + private static void extractFromParagraph(P p, HeaderInfo info, StringBuilder textBuilder, WordprocessingMLPackage pkg) { + if (p == null) return; + + List contents = p.getContent(); + for (Object o : contents) { + Object child = XmlUtils.unwrap(o); + + if (child instanceof R) { + R run = (R) child; + for (Object rChild : run.getContent()) { + Object rObj = XmlUtils.unwrap(rChild); + + // 普通文本 + if (rObj instanceof Text) { + textBuilder.append(((Text) rObj).getValue()); + } + // 域代码(页码等)也在 R 里面,但不用 InstrText,直接抓文字 + else if (rObj instanceof org.docx4j.wml.FldChar) { + // 可以加标记或忽略 + } + // 文本框里的段落 + else if (rObj instanceof P) { + extractFromParagraph((P) rObj, info, textBuilder, pkg); + } + // Drawing 或 Pict + else if (rObj instanceof Drawing) { + extractFromDrawing((Drawing) rObj, info, textBuilder, pkg); + } else if (rObj instanceof Pict) { + extractFromPict((Pict) rObj, info, textBuilder, pkg); + } + } + } + // 段落里嵌套文本框 + else if (child instanceof org.docx4j.wml.Drawing) { + extractFromDrawing((Drawing) child, info, textBuilder, pkg); + } else if (child instanceof Pict) { + extractFromPict((Pict) child, info, textBuilder, pkg); + } + } + } + + + + private static void extractFromDrawing(Drawing drawing, HeaderInfo info, StringBuilder textBuilder, WordprocessingMLPackage pkg) { + try { + // 为了兼容 wps:wsp / wps:txbx,直接把 Drawing 序列化,再找 里的 + String xml = XmlUtils.marshaltoString(drawing, true, true); + extractTxbxParagraphsFromXml(xml, info, textBuilder, pkg); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void extractFromPict(Pict pict, HeaderInfo info, StringBuilder textBuilder, WordprocessingMLPackage pkg) { + try { + // 旧版 VML 文本框:...... + String xml = XmlUtils.marshaltoString(pict, true, true); + extractTxbxParagraphsFromXml(xml, info, textBuilder, pkg); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** 从包含 的 XML 里把内部 反序列化出来再递归解析(获取真正字号/颜色/加粗等) */ + private static void extractTxbxParagraphsFromXml(String xml, HeaderInfo info, StringBuilder textBuilder, WordprocessingMLPackage pkg) throws Exception { + org.w3c.dom.Document doc = XmlUtils.getNewDocumentBuilder() + .parse(new java.io.ByteArrayInputStream(xml.getBytes(java.nio.charset.StandardCharsets.UTF_8))); + + // 只在 下面找 + final String W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + org.w3c.dom.NodeList txbxList = doc.getElementsByTagNameNS(W_NS, "txbxContent"); + + for (int i = 0; i < txbxList.getLength(); i++) { + org.w3c.dom.Node txbx = txbxList.item(i); + org.w3c.dom.NodeList children = txbx.getChildNodes(); + for (int j = 0; j < children.getLength(); j++) { + org.w3c.dom.Node node = children.item(j); + if (W_NS.equals(node.getNamespaceURI()) && "p".equals(node.getLocalName())) { + String pXml = XmlUtils.w3CDomNodeToString(node); + P innerP = (P) XmlUtils.unmarshalString(pXml); + // 这里会读取到真正的 rPr: + parseParagraph(innerP, info, textBuilder, pkg); + } + } + } + } + + private static void applyRunStyle(RPr rPr, HeaderInfo info) { + if (rPr == null) return; + + // 字体 + if (rPr.getRFonts() != null) { + if (rPr.getRFonts().getEastAsia() != null && !rPr.getRFonts().getEastAsia().isEmpty()) { + info.font = rPr.getRFonts().getEastAsia(); // 中文字体 + } else if (rPr.getRFonts().getAscii() != null && !rPr.getRFonts().getAscii().isEmpty()) { + info.font = rPr.getRFonts().getAscii(); // 英文字体 + } + } + + // 字号 + if (rPr.getSz() != null && rPr.getSz().getVal() != null) { + info.fontSizePt = rPr.getSz().getVal().doubleValue() / 2.0; + } + + // 颜色 + if (rPr.getColor() != null && rPr.getColor().getVal() != null && !rPr.getColor().getVal().isEmpty()) { + info.colorHex = rPr.getColor().getVal(); + } + + // 加粗 + if (rPr.getB() != null && rPr.getB().isVal()) { + info.bold = "是"; + } + + // 倾斜 + if (rPr.getI() != null && rPr.getI().isVal()) { + info.italic = "是"; + } + } + + + + /** * 递归解析内容,包括文本框里的段落 @@ -1129,8 +1468,11 @@ public class SectionPage { - //节 页面设置(纸张大小) 纸型 - public static String getPageSetType(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { + // 节 页面设置(纸张大小) 纸型 +// 节 页面设置(纸张大小) 纸型 + public static String getPageSetType(P paragraph, HeaderFooterPolicy hfp, + WordprocessingMLPackage wordMLPackage, + List sections) { // 获取段落所在节的 SectPr SectPr currentSectPr = null; @@ -1161,19 +1503,57 @@ public class SectionPage { SectPr.PgSz pgSz = currentSectPr.getPgSz(); BigInteger w = pgSz.getW(); BigInteger h = pgSz.getH(); - // 常用纸张判断 (宽 x 高, 单位 twip) - if (w != null && h != null) { - if ((w.equals(BigInteger.valueOf(11906)) && h.equals(BigInteger.valueOf(16838)))||(h.equals(BigInteger.valueOf(11906)) && w.equals(BigInteger.valueOf(16838)))) return "A4"; - if ((w.equals(BigInteger.valueOf(16838)) && h.equals(BigInteger.valueOf(23811))) ||((h.equals(BigInteger.valueOf(16838)) && w.equals(BigInteger.valueOf(23811))) )) return "A3"; - if ((w.equals(BigInteger.valueOf(8504)) && h.equals(BigInteger.valueOf(11906)))||(h.equals(BigInteger.valueOf(8504)) && w.equals(BigInteger.valueOf(11906)))) return "B5"; - if ((w.equals(BigInteger.valueOf(23811)) && h.equals(BigInteger.valueOf(33685)))||(h.equals(BigInteger.valueOf(23811)) && w.equals(BigInteger.valueOf(33685)))) return "A2"; - if ((w.equals(BigInteger.valueOf(11906)) && h.equals(BigInteger.valueOf(16838)))||(h.equals(BigInteger.valueOf(11906)) && w.equals(BigInteger.valueOf(16838)))) return "Letter"; // 可根据需要补充 + + if (w == null || h == null) { + return "未知纸张"; + } + + + // 允许 ±5 的误差 + final int tol = 5; + int width = w.intValue(); + int height = h.intValue(); + + System.out.println(width); + System.out.println(height); + + // 宽高标准值 (单位 twip) + Map standard = new HashMap<>(); + standard.put("A3", new int[]{16838, 23811}); + standard.put("A4", new int[]{11906, 16838}); + standard.put("A5", new int[]{8391, 11906}); + standard.put("B4", new int[]{14572, 20638}); + standard.put("B5", new int[]{10319, 14572}); + standard.put("16开", new int[]{10433, 14742}); // 印刷厂常用尺寸 184×260mm + standard.put("国标16开", new int[]{8338, 11906}); // 国标16开 147×210mm + standard.put("32开", new int[]{7371, 10433}); // 你提供的 + standard.put("大32开", new int[]{7938, 11510}); + standard.put("Statement", new int[]{7920, 12240}); + standard.put("Executive", new int[]{10440, 15120}); + standard.put("Letter(信纸)", new int[]{12240, 15840}); + standard.put("Tabloid", new int[]{15840, 24480}); + standard.put("Legal(法律纸)", new int[]{12240, 20160}); + + + for (Map.Entry entry : standard.entrySet()) { + int sw = entry.getValue()[0]; + int sh = entry.getValue()[1]; + // 纵向匹配 + if (Math.abs(width - sw) <= tol && Math.abs(height - sh) <= tol) { + return entry.getKey(); + } + // 横向匹配 + if (Math.abs(width - sh) <= tol && Math.abs(height - sw) <= tol) { + return entry.getKey() + " (横向)"; + } } return "自定义纸张"; } + + //宽度 public static String getPageSetWidth(P paragraph, HeaderFooterPolicy hfp, WordprocessingMLPackage wordMLPackage, List sections) { MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart(); @@ -1513,68 +1893,19 @@ public class SectionPage { } } } - public static HeaderInfo extractHeaderInfo(HeaderPart headerPart,WordprocessingMLPackage wordMLPackage) { + public static HeaderInfo extractHeaderInfo(HeaderPart headerPart,WordprocessingMLPackage wordMLPackage) throws Docx4JException { HeaderInfo info = new HeaderInfo(); StringBuilder textBuilder = new StringBuilder(); - List contents = headerPart.getContent(); - for (Object obj : contents) { - if (obj instanceof P) { - P paragraph = (P) obj; - // 先获取段落样式id - PPr pPr = paragraph.getPPr(); - String styleId = (pPr != null && pPr.getPStyle() != null) ? pPr.getPStyle().getVal() : null; - // 对齐方式 - if (paragraph.getPPr() != null && paragraph.getPPr().getJc() != null) { - info.alignment = paragraph.getPPr().getJc().getVal().value(); - } + if (headerPart == null || headerPart.getContents() == null) { + return info; + } + Hdr ftr = headerPart.getContents(); - for (Object o : paragraph.getContent()) { - if (o instanceof R) { - R run = (R) o; - - RPr rPr = run.getRPr(); - if (rPr != null) { - // 取字体 - if (info.font == null) { - info.font = getFontFromRunOrStyle(rPr, styleId, wordMLPackage); - } - -// if (info.fontSizePt == null) { -// info.fontSizePt = getFontSizeNameByRunAndStyles(run, wordMLPackage); -// if (info.fontSizePt == null) { -// // 样式查不到,就直接从Run获取字号数字 -// info.fontSizePt = getFontSizeFromRun(run); -// } -// -// } - // 取字号 - if (info.fontSizePt == null) { - info.fontSizePt = getFontSizeFromRunOrStyle(rPr, styleId, wordMLPackage); - } - - if (rPr.getColor() != null) { - info.colorHex = rPr.getColor().getVal(); - } - // 加粗 - info.bold = (rPr.getB() != null && Boolean.TRUE.equals(rPr.getB().isVal())) ? "是" : "否"; - - // 倾斜 - info.italic = (rPr.getI() != null && Boolean.TRUE.equals(rPr.getI().isVal())) ? "是" : "否"; - - } - - // 取文字内容 - for (Object rContent : run.getContent()) { - Object val = (rContent instanceof JAXBElement) - ? ((JAXBElement) rContent).getValue() - : rContent; - if (val instanceof Text) { - textBuilder.append(((Text) val).getValue()); - } - } - } - } + for (Object block : ftr.getContent()) { + Object u = XmlUtils.unwrap(block); + if (u instanceof P) { + parseParagraph((P) u, info, textBuilder, wordMLPackage); } }