Apache POI XWPF读取Word文档时为何无法正确获取字号?
解决Apache POI XWPF获取缺失字号的问题
问题原因
- Word文档中,当文本片段的字号与上下文(或段落默认样式)一致时,会自动省略
<sz>和<szCs>标签。POI直接调用getFontSize()等方法时,因找不到对应标签,会返回默认的-1或null。 - 你提到的Normal样式里的
<sz w:val="24">是Word的半点数(1磅=2半点数),对应显示的12磅字号,但POI的getDefaultParagraphStyle()默认仅处理段落格式(如间距),不会直接提取其中的字符样式(字号属于字符样式范畴)。
解决方案
要正确获取继承的字号,可通过两种方式处理:
方法1:从默认段落样式中提取字号
直接解析Normal样式的字符属性,获取预设字号:
String fileName = "C:\\<yourFile>.docx"; try (XWPFDocument doc = new XWPFDocument(Files.newInputStream(Paths.get(fileName)))) { // 获取Normal默认样式 XWPFStyle normalStyle = doc.getStyles().getStyle("Normal"); Integer defaultFontSize = null; if (normalStyle != null) { CTRPr styleRPr = normalStyle.getCTStyle().getRPr(); if (styleRPr != null && styleRPr.isSetSz()) { // 半点数转磅值,除以2得到实际显示的字号 defaultFontSize = styleRPr.getSz().getVal().intValue() / 2; } } List<XWPFParagraph> paragraphs = doc.getParagraphs(); for (XWPFParagraph paragraph : paragraphs) { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { System.out.print(run.getText(0)); System.out.print("| name : " + run.getFontName()); // 优先用当前Run的字号,无效则用默认样式字号 int currentSize = run.getFontSize(); if (currentSize == -1) { System.out.print("| size : " + defaultFontSize); } else { System.out.print("| size : " + currentSize); } Double currentSizeD = run.getFontSizeAsDouble(); if (currentSizeD == null) { System.out.print("| sizeD : " + (double)defaultFontSize); } else { System.out.print("| sizeD : " + currentSizeD); } System.out.print("| sizeC : " + run.getComplexScriptFontSizeAsDouble()); System.out.println(); } } } catch (IOException e) { throw new RuntimeException(e); }
方法2:追溯上下文的有效字号
如果文档按显示特征分段,后续无字号标签的Run会继承前一个同特征Run的字号,可维护变量记录最近的有效字号:
String fileName = "C:\\<yourFile>.docx"; try (XWPFDocument doc = new XWPFDocument(Files.newInputStream(Paths.get(fileName)))) { // 初始化默认字号,从Normal样式获取 Integer lastValidFontSize = null; XWPFStyle normalStyle = doc.getStyles().getStyle("Normal"); if (normalStyle != null && normalStyle.getCTStyle().getRPr() != null && normalStyle.getCTStyle().getRPr().isSetSz()) { lastValidFontSize = normalStyle.getCTStyle().getRPr().getSz().getVal().intValue() / 2; } List<XWPFParagraph> paragraphs = doc.getParagraphs(); for (XWPFParagraph paragraph : paragraphs) { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { System.out.print(run.getText(0)); System.out.print("| name : " + run.getFontName()); int currentSize = run.getFontSize(); if (currentSize == -1) { System.out.print("| size : " + lastValidFontSize); } else { System.out.print("| size : " + currentSize); // 更新最近的有效字号 lastValidFontSize = currentSize; } Double currentSizeD = run.getFontSizeAsDouble(); if (currentSizeD == null) { System.out.print("| sizeD : " + (double)lastValidFontSize); } else { System.out.print("| sizeD : " + currentSizeD); lastValidFontSize = currentSizeD.intValue(); } System.out.print("| sizeC : " + run.getComplexScriptFontSizeAsDouble()); System.out.println(); } } } catch (IOException e) { throw new RuntimeException(e); }
补充说明
- POI的
getDefaultRunStyle()返回的是文档全局默认字符样式,而非段落样式中的字符格式,因此无法获取Normal样式里的字号。 - Word中字号以半点数存储,需除以2转换为用户可见的磅值。
内容的提问来源于stack exchange,提问作者user1368476
相关产品推荐
相关产品推荐

