Java 在dotx/docx文件中编辑头

Java 在dotx/docx文件中编辑头,java,apache-poi,apache-poi-4,Java,Apache Poi,Apache Poi 4,我目前正试图从现有的dotx格式模板生成一个新的docx文件。我想更改标题中的firstname、lastname等,但由于某些原因我无法访问它们。。。 我的做法如下: public void generateDocX(Long id) throws IOException, InvalidFormatException { //Get user per id EmployeeDTO employeeDTO = employeeService.getEmployee(id);

我目前正试图从现有的dotx格式模板生成一个新的docx文件。我想更改标题中的firstname、lastname等,但由于某些原因我无法访问它们。。。 我的做法如下:

 public void generateDocX(Long id) throws IOException, InvalidFormatException {

    //Get user per id
    EmployeeDTO employeeDTO = employeeService.getEmployee(id);

    //Location where the new docx file will be saved
    FileOutputStream outputStream = new FileOutputStream(new File("/home/user/Documents/project/src/main/files/" + employeeDTO.getId() + "header.docx"));

    //Get the template for generating the new docx file
    File template = new File("/home/user/Documents/project/src/main/files/template.dotx");
    OPCPackage pkg = OPCPackage.open(template);
    XWPFDocument document = new XWPFDocument(pkg);

    for (XWPFHeader header : document.getHeaderList()) {
        List<XWPFParagraph> paragraphs = header.getParagraphs();
        System.out.println("Total paragraphs in header are: " + paragraphs.size());
        System.out.println("Total elements in the header are: " + header.getBodyElements().size());
        for (XWPFParagraph paragraph : paragraphs) {
            System.out.println("Paragraph text is: " + paragraph.getText());
            List<XWPFRun> runs = paragraph.getRuns();
            for (XWPFRun run : runs) {
                String runText = run.getText(run.getTextPosition());
                System.out.println("Run text is: " + runText);
            }
        }
    }

    //Write the changes to the new docx file and close the document
    document.write(outputStream);
    document.close();
}
控制台中的输出为1、null或空字符串。。。我已经尝试了好几种方法,但是没有任何运气

下面是template.dotx中的内容

并且只获取直接在IBody中的段落或正文元素。但是你的段落不是直接在那里,而是在一个单独的文本框或文本框中。这就是为什么他们不能通过这种方式得到

由于*.docx是一个ZIP归档文件,包含用于文档、页眉和页脚的jgxml文件,因此可以通过创建一个选择所有w:rxml元素的XmlCursor来获取一个IBody的所有文本运行。对于XWPFHeader,这可能看起来是这样的:

 private List<XmlObject> getAllCTRs(XWPFHeader header) {
  CTHdrFtr ctHdrFtr = header._getHdrFtr();
  XmlCursor cursor = ctHdrFtr.newCursor();
  cursor.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:r");
  List<XmlObject> ctrInHdrFtr = new ArrayList<XmlObject>();
  while (cursor.hasNextSelection()) {
   cursor.toNextSelection();
   XmlObject obj = cursor.getObject();
   ctrInHdrFtr.add(obj);
  }
  return ctrInHdrFtr;
 }
现在,我们有了该IBody中所有XML元素的列表,它们是Word中的文本运行元素

有了这些,我们可以像这样从他们身上获得文本:

 private void printAllTextInTextRunsOfIBody(IBody iBody) throws Exception {
  List<XmlObject> ctrInIBody = getAllCTRs(iBody);
  for (XmlObject obj : ctrInIBody) {
   CTR ctr = CTR.Factory.parse(obj.xmlText());
   for (CTText ctText : ctr.getTList()) {
    String text = ctText.getStringValue();
    System.out.println(text);
   }
  }
 }
顺便说一句:由于您的文档是从*.dotx打开的,因此需要将内容类型从wordprocessingml.template更改为wordprocessingml.document。否则Word将不会打开生成的*.docx文档。看

由于我对替换占位符文本的方法持怀疑态度,我更喜欢的方法是填写表单。看见当然,这样的表单字段不能在页眉或页脚中使用。因此,页眉或页脚可以从整体上从头开始创建。

只获取直接位于该IBody中的段落或正文元素。但是你的段落不是直接在那里,而是在一个单独的文本框或文本框中。这就是为什么他们不能通过这种方式得到

由于*.docx是一个ZIP归档文件,包含用于文档、页眉和页脚的jgxml文件,因此可以通过创建一个选择所有w:rxml元素的XmlCursor来获取一个IBody的所有文本运行。对于XWPFHeader,这可能看起来是这样的:

 private List<XmlObject> getAllCTRs(XWPFHeader header) {
  CTHdrFtr ctHdrFtr = header._getHdrFtr();
  XmlCursor cursor = ctHdrFtr.newCursor();
  cursor.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:r");
  List<XmlObject> ctrInHdrFtr = new ArrayList<XmlObject>();
  while (cursor.hasNextSelection()) {
   cursor.toNextSelection();
   XmlObject obj = cursor.getObject();
   ctrInHdrFtr.add(obj);
  }
  return ctrInHdrFtr;
 }
现在,我们有了该IBody中所有XML元素的列表,它们是Word中的文本运行元素

有了这些,我们可以像这样从他们身上获得文本:

 private void printAllTextInTextRunsOfIBody(IBody iBody) throws Exception {
  List<XmlObject> ctrInIBody = getAllCTRs(iBody);
  for (XmlObject obj : ctrInIBody) {
   CTR ctr = CTR.Factory.parse(obj.xmlText());
   for (CTText ctText : ctr.getTList()) {
    String text = ctText.getStringValue();
    System.out.println(text);
   }
  }
 }
顺便说一句:由于您的文档是从*.dotx打开的,因此需要将内容类型从wordprocessingml.template更改为wordprocessingml.document。否则Word将不会打开生成的*.docx文档。看


由于我对替换占位符文本的方法持怀疑态度,我更喜欢的方法是填写表单。看见当然,这样的表单字段不能在页眉或页脚中使用。所以页眉或页脚可以从头开始创建。

我认为实际上,firstname、lastname等所在的框是一个框架内容,或者至少Libre Office是这么说的,当我点击它时。我认为实际上,firstname所在的框,lastname等是一个框架内容,或者至少Libre Office是这么说的,当我点击它时..谢谢你的回复!我已经尝试了你提供的代码,但由于某种原因什么都没有发生。。。实际上,打印头printalTextIntextRunSofHeader运行中的文本的方法根本不打印任何内容。我发现第二个for循环实际上从未执行过。。有没有进一步的建议,或者您认为自己重新创建模板会更好?我不确定我是否会做得更好,但我可以试试@IvanNickSim:也许文本框甚至不在标题中,而是在文档中,只放在标题上?请参阅我的答案中的补充。我将在一分钟内尝试您的更改。。我已经检查了docx文件中的内容,并用一张图片更新了我的帖子。@Alex非常感谢你,伙计!在您所做的更改之后,现在名、姓和职称都已成功更改!我想我也必须对页脚和其他页眉做同样的处理,因为它们也是不同的文档。。我说得对吗?@IvanNickSim:是的,你说得对。由于您已经解压了*.dotx,您已经看到了不同的文档部分。以下部分包含文本运行:document.xml是文档正文,headerN.xml和footerN.xml是不同的页眉和页脚默认值,甚至,首先,endnotes.xml包含尾注文本,footnotes.xml包含脚注文本。感谢您的回复!我已经尝试了你提供的代码,但由于某种原因什么都没有发生。。。实际上,打印头printalTextIntextRunSofHeader运行中的文本的方法根本不打印任何内容。我发现第二个for循环实际上从未执行过。。有没有进一步的建议,或者您认为自己重新创建模板会更好?我不确定我是否会做得更好,但我可以试试@IvanNickSim:也许文本框甚至不在标题中,而是在文档中,只放在标题上?请参阅我的答案中的补充。我将在一分钟内尝试您的更改。。我已经检查了docx文件中的内容,并用
一张照片里面是什么…@Alex非常感谢你,伙计!在您所做的更改之后,现在名、姓和职称都已成功更改!我想我也必须对页脚和其他页眉做同样的处理,因为它们也是不同的文档。。我说得对吗?@IvanNickSim:是的,你说得对。由于您已经解压了*.dotx,您已经看到了不同的文档部分。以下部分包含文本运行:document.xml是文档正文,headerN.xml和footerN.xml是不同的页眉和页脚默认值,甚至,首先,endnotes.xml包含尾注文本,footnotes.xml包含脚注文本。