使用Apache POI将HTML转换为Word文档:无法添加页眉页脚及@page CSS支持问题咨询
解决Apache POI转换HTML到Word时的页眉页脚问题
你好!针对你遇到的问题,我来逐一解答:
一、POI支持CSS的@page样式吗?
答案是不支持。你当前使用的POIFSFileSystem直接写入WordDocument的方式,是针对旧版.doc格式的底层二进制操作,这种方式完全没有实现CSS @page规则的解析能力——它只是简单把HTML内容打包成Word的底层格式,根本无法识别页面布局相关的CSS规则,所以自然没法通过@page来生成页眉页脚。而且这种方式的扩展性极差,几乎没法控制文档的页面元素。
二、怎么给生成的.doc文档添加页眉页脚?
别用那种底层的POIFS方式了,改用Apache POI专门处理.doc的HWPF组件(或者处理.docx的XWPF,推荐后者,支持更好)来实现。下面分两种格式给你具体方案:
方案1:针对.doc格式(HWPF)
首先要确保你引入了poi-scratchpad依赖(Maven示例):
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>5.2.5</version> <!-- 用你实际使用的POI版本 --> </dependency>
然后修改你的转换逻辑,用HWPF来构建文档并添加页眉页脚:
import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.usermodel.HeaderStories; import org.apache.poi.hwpf.usermodel.Range; import java.io.IOException; import java.io.OutputStream; public class HtmlToWordUtil { public void convertHtmlToWordWithHeaderFooter(String html, OutputStream outputStream) throws IOException { // 1. 创建一个空的.doc文档 HWPFDocument doc = new HWPFDocument(); Range contentRange = doc.getRange(); // 2. 把HTML内容插入到文档主体 // 注意:HWPF对复杂HTML样式支持有限,如果你的HTML有复杂样式,建议先用Jsoup预处理 contentRange.insertAfter(html); // 3. 添加页眉 HeaderStories headerStories = doc.getHeaderStories(); headerStories.setHeader("这里是自定义页眉内容"); // 4. 添加带页码的页脚 // 用Word的域来实现自动页码,PAGE是当前页,NUMPAGES是总页数 String footerText = "第 " + org.apache.poi.hwpf.usermodel.PageNumber.PAGE + " 页 / 共 " + org.apache.poi.hwpf.usermodel.PageNumber.NUMPAGES + " 页"; headerStories.setFooter(footerText); // 5. 写入输出流并关闭资源 doc.write(outputStream); doc.close(); outputStream.close(); } }
方案2:推荐用.docx格式(XWPF)
HWPF对HTML的支持比较有限,如果你业务允许,强烈推荐用.docx格式,XWPF的功能更完善,HTML转换和页眉页脚控制都更靠谱。
依赖只需要标准的poi和poi-ooxml:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.5</version> </dependency>
实现代码:
import org.apache.poi.xwpf.usermodel.*; import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; import java.io.OutputStream; public class HtmlToDocxUtil { public void convertHtmlToDocxWithHeaderFooter(String html, OutputStream outputStream) throws Exception { // 1. 创建空的.docx文档 XWPFDocument docx = new XWPFDocument(); // 2. 把HTML转换为docx内容(先解析HTML为DOM文档) Document htmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder() .parse(new ByteArrayInputStream(html.getBytes())); org.apache.poi.xwpf.converter.xhtml.XHTMLConverter.getInstance().convert(htmlDoc, docx, null); // 3. 添加页眉 XWPFHeader header = docx.createHeader(HeaderFooterType.DEFAULT); XWPFParagraph headerPara = header.createParagraph(); XWPFRun headerRun = headerPara.createRun(); headerRun.setText("这是Docx文档的自定义页眉"); headerPara.setAlignment(ParagraphAlignment.CENTER); // 居中显示 // 4. 添加带自动页码的页脚 XWPFFooter footer = docx.createFooter(HeaderFooterType.DEFAULT); XWPFParagraph footerPara = footer.createParagraph(); footerPara.setAlignment(ParagraphAlignment.CENTER); XWPFRun footerRun = footerPara.createRun(); footerRun.setText("第 "); // 插入当前页码域 footerRun.getCTR().addNewFldChar().setFldCharType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType.BEGIN); footerRun.getCTR().addNewInstrText().setStringValue(" PAGE "); footerRun.getCTR().addNewFldChar().setFldCharType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType.END); footerRun.setText(" 页 / 共 "); // 插入总页数域 footerRun.getCTR().addNewFldChar().setFldCharType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType.BEGIN); footerRun.getCTR().addNewInstrText().setStringValue(" NUMPAGES "); footerRun.getCTR().addNewFldChar().setFldCharType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType.END); footerRun.setText(" 页"); // 5. 写入输出流并关闭资源 docx.write(outputStream); docx.close(); outputStream.close(); } }
几个关键提醒:
- 你原来的POIFS方式太底层,完全没法控制页面元素,必须切换到HWPF/XWPF的高层API。
- 如果HTML有复杂样式,比如表格、自定义字体,建议先用Jsoup把HTML转换成标准XHTML,再进行转换,避免样式丢失。
- 页脚的页码必须用Word的域来实现,这样才能自动计算页码,而不是写死的文本。
内容的提问来源于stack exchange,提问作者Chanchal Sharma
相关产品推荐
相关产品推荐

