如何使用 poi 将外部页眉页脚文件(.xml 格式)添加到现有的 .docx 文档

How to add an external header footer file (.xml format) to an existing .docx document using poi

其实我想给文档加一个上表头,但是因为文档表头的格式要求比较高,也比较标准,所以想试试把表头文件直接插入文档。我认为直接用标准头文件引用它会比从 poi 创建头文件更好。



我有一个xml类型的带头文件,文件名为'headerExternal.xml',内容如下:

    <?xml version="1.0" encoding="utf-8" standalone="yes"?><w:hdr xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:p>
        <w:pPr>
            <w:jc w:val="left" />
        </w:pPr>
        <w:r>
            <w:rPr>
                <w:b />
                <w:color w:val="FF00FF" />
                <w:sz w:val="30" />
            </w:rPr>
            <w:t xml:space="preserve">page 1</w:t>
        </w:r>
    </w:p>
</w:hdr>

我想将 headerExternal.xml 插入 .docx 文件并遵循 ooxml 规范

原.docx文件: enter image description here

插入后的docx文档: enter image description here



我试过直接用poi提供的hdrDocument解析headerExternal.xml文件,如下:

HdrDocument hdrDocument = HdrDocument.Factory.parse(new File("headerExternal.xm"));

但是我不知道如何将这个对象绑定到 XWPFDocument 对象,所以我目前没有取得任何进展。



我尝试了Axel Richter提供的方法,发现它们确实解决了我的问题。

但我也发现,在文档中添加一大堆完整的< w:hdr >标签确实不是一个好主意,因为它会导致文档第一次浏览时缺少提示样式。

这是一个XY problem。可以使用 OPCPackageheaderExternal.xml 放入 *.docx ZIP 存档中。但是 *.docx ZIP 存档中的 headerExternal.xml 永远不会用作 Word 中的 header。它只会成为该 ZIP 存档中无用的附加文件。并且在Word下次保存文件的时候会被移除。没有办法使用 XWPFHeader.

但是如果您真的想使用 *.xml 文件作为 XWPFHeader 的模板,那么可以使用 XWPFHeaderFooter.setHeaderFooter(org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr headerFooter) 来完成。这需要一个 org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr,它可以从 *.xml 文件创建,如下所示:

...
File headerContent = new File("./headerExternal.xml");

org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr ctHdrFtr =
 org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument.Factory.parse(headerContent).getHdr();
...

完整示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.xwpf.usermodel.*;

public class DocxWordHeaderFromFile {

 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument(new FileInputStream("./WordDocument.docx"));

  File headerContent = new File("./headerExternal.xml");
  
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr ctHdrFtr = 
   org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument.Factory.parse(headerContent).getHdr();

  for (XWPFHeader header : document.getHeaderList()) {
   header.setHeaderFooter(ctHdrFtr);
  }
 
  FileOutputStream out = new FileOutputStream("./WordDocumentNew.docx");
  document.write(out);
  out.close();
  document.close();

 }
}

根据 Axel Richter 对 的回答,我找到了我想要的答案

回忆一下我之前的需求:

I wanted to take out the footer.xml or header.xml files from the .docx document zip package and put them directly into the output document so that we could avoid complicated information such as various styles.

如果你想达到这样的效果,你应该这样做:

1.Paste你要的部分来自headerfooterxml文件(因为我的开发环境是中文的,有些属性是中文的字段,但影响不大):

<w:p>
    <w:pPr>
        <w:ind w:right="360" w:firstLine="720"/>
        <w:jc w:val="center"/>
        <w:outlineLvl w:val="0"/>
        <w:rPr>
        <w:rFonts w:hint="eastAsia" w:ascii="黑体" w:eastAsia="隶书"/>
        </w:rPr>
    </w:pPr>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="eastAsia" w:eastAsia="隶书"/>
            <w:bCs/>
            <w:sz w:val="36"/>
        </w:rPr>
        <w:t>This a </w:t>
    </w:r>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="eastAsia" w:eastAsia="隶书"/>
            <w:b/>
            <w:sz w:val="44"/>
        </w:rPr>
        <w:t xml:space="preserve"> </w:t>
    </w:r>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="eastAsia" w:ascii="黑体" w:eastAsia="黑体"/>
            <w:bCs/>
        </w:rPr>
        <w:t>footer</w:t>
    </w:r>
</w:p>

note:不需要整个文件,注意不要包含最顶层的约束信息



2.Call poi的api创建页脚对象

XWPFFooter xwpfFooterHome = xwpfDocument.createFooter(HeaderFooterType.FIRST);

3.The 上面复制的字符串用作生成 CTHdrFtr 实例对象的参数

ctHdrFtrFooterHome = CTHdrFtr.Factory.parse(footerhomeString);

4.There 是 xwpfFooterHome 中的一个方法,它设置在 Step 3

中创建的 CTHdrFtr 对象
xwpfFooterHome.setHeaderFooter(ctHdrFtrFooterHome);


所以完整代码如下(注意在<w:p>标签后面加上xmlns:http://schemas.openxmlformats.org/wordprocessingml/2006/main):

        String footerhomeString = "<w:p xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:pPr><w:ind w:right=\"360\" w:firstLine=\"720\"/><w:jc w:val=\"center\"/><w:outlineLvl w:val=\"0\"/><w:rPr><w:rFonts w:hint=\"eastAsia\" w:ascii=\"黑体\" w:eastAsia=\"隶书\"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"隶书\"/><w:bCs/><w:sz w:val=\"36\"/></w:rPr><w:t>中国南车集团株洲电力机车有限公司</w:t></w:r><w:r><w:rPr><w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"隶书\"/><w:b/><w:sz w:val=\"44\"/></w:rPr><w:t xml:space=\"preserve\"> </w:t></w:r><w:r><w:rPr><w:rFonts w:hint=\"eastAsia\" w:ascii=\"黑体\" w:eastAsia=\"黑体\"/><w:bCs/></w:rPr><w:t>发 布</w:t></w:r></w:p>";

        XWPFFooter xwpfFooterHome = xwpfDocument.createFooter(HeaderFooterType.FIRST);
        CTHdrFtr ctHdrFtrFooterHome = null;
        try {
            ctHdrFtrFooterHome = CTHdrFtr.Factory.parse(footerhomeString);
        } catch (XmlException e) {
            e.printStackTrace();
        }
        xwpfFooterHome.setHeaderFooter(ctHdrFtrFooterHome);