提问者:小点点

ApachePOI-添加页面到docx没有头


日安!请帮我找到解决问题的方法:我需要在文档中添加一个新的QR页面,并将其保存在新目录中的新表单中。我设法做到了,除了一个细微差别。源文档有一个标题,在添加新页面时会自动添加,因为它是默认的。问题是,我不需要在添加了QR的页面中添加此标题,但我需要将其保存在所有其他页面中。我需要有一个没有标题的最后一页。如果我使用这个

XWPFHeaderFooterPolicy policy = document.getHeaderFooterPolicy();
policy.getHeader(XWPFHeaderFooterPolicy.DEFAULT).clearHeaderFooter();

这将删除所有页面上的标题,这是预期的。如果我使用这个:

XWPFParagraph paragraph0 = document.createParagraph();
XWPFParagraph paragraph1 = document.createParagraph();
// Working with paragraphs

XWPFHeaderFooterPolicy policy = document.getHeaderFooterPolicy();

XWPFParagraph[] pars = new XWPFParagraph[2];
pars[0] = paragraph0;
pars[0] = paragraph1;

XWPFHeader header = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, pars);
header.clearHeaderFooter();

这发生了完全相同的事情,即从所有页面中删除标头。

请帮我添加一个没有标题的段落的新页面,保留原始文档所有页面的标题。非常感谢!


共1个答案

匿名用户

Microsoft Word中没有仅针对最后一页的页眉/页脚。

在XWPF中POI页脚在最后一页不同,我已经展示了如何设置页脚在最后一页不同。页眉也是如此。

我在链接答案中的代码尽可能短,以显示原理。和这里答案中的所有代码一样,它不被认为可以作为每次复制/粘贴的有效代码使用。它被认为可以用于测试和理解。用于有效使用的代码当然必须包含更多的检查,以考虑真正的Word文档可能使用的所有可能的不同选项。但是这样的代码通常过于宽泛,无法理解原则。

根据您的问题(和评论),要求如下:

打开包含信息和两种类型标题的现有文档(FIRST

所以原理是一样的。我们需要:

  1. 为偶数页添加标题,稍后获取最后一节中页面的默认标题。
  2. 添加一个段落,其中包含上面第1节的部分设置和部分中断。
  3. 添加新部分2的内容。
  4. 获取文档中最后一部分的旧部分设置,其中也包含旧的页眉/页脚设置。
  5. 将第一个和默认页眉和所有页脚从最后一节移动到新的第1节。
  6. 为第1节设置“有一个标题页”以使第一个标题工作。从上一节(现在的第2节)中删除第一个和旧的默认标题引用,并将偶数标题的标题引用设置为最后一节的默认标题引用。
  7. 取消最后一节的“有一个标题页”。

下面的完整示例再次展示了这一点。它使用当前apache poi 5.2.2WordDocumentSource. docx进行了测试并工作,其中包含多个具有默认内容的页面以及第一页和默认页面的页眉/页脚。

import java.io.*;

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

public class WordAddLastPageWithoutHeader {
    
 private static void addLastPageInSeparateSection(XWPFDocument document) {
     
  XWPFParagraph paragraph;
  XWPFRun run;
  XWPFHeader header;
  
  //Adding a header for even pages, which gets the default header for page in last section later.
  header = document.createHeader(HeaderFooterType.EVEN);
  paragraph = header.createParagraph();
  run = paragraph.createRun();
  run.setText("Header EVEN = DEFAULT in last section"); // don't set text here if you wants the header of the page in last section empty.

  //Adding a paragraph with section settings for section 1 above and section break.
  paragraph = document.createParagraph();
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrSect1 = paragraph.getCTP().addNewPPr().addNewSectPr(); //we need ctSectPrSect1 later to set headers/footes
 
  //Adding the content for new section 2.
  paragraph = document.createParagraph();
  run = paragraph.createRun();  
  run.setText("Last page. Page in last section ...");
  //ToDo: further content of last page in separate section
  
  //Getting the old section settings for last section in document which contains the old header/footer settings too.
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1 ctDocument = document.getDocument();
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody ctBody = ctDocument.getBody();
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrLastSect = ctBody.getSectPr(); //there must be a SectPr already because of the header/footer settings

  //Moving the first and default headers and all footers from last section to new section 1.
  java.util.List<org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef> ctHdrFtrRefList = new java.util.ArrayList<org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef>();
  for (org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef ctHdrFtrRef : ctSectPrLastSect.getHeaderReferenceList()) {
   if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.FIRST)
    ctHdrFtrRefList.add(ctHdrFtrRef);
   if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.DEFAULT)
    ctHdrFtrRefList.add(ctHdrFtrRef);
  }
  if (ctHdrFtrRefList.size() > 0) {
   org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef[] ctHdrFtrRefs = ctHdrFtrRefList.toArray(new org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef[0]);
   ctSectPrSect1.setHeaderReferenceArray(ctHdrFtrRefs);
  }
  ctSectPrSect1.setFooterReferenceArray(ctSectPrLastSect.getFooterReferenceArray());
 
  //Moving the page settings from last section to new section 1. 
  if (ctSectPrLastSect.isSetPgSz()) ctSectPrSect1.setPgSz(ctSectPrLastSect.getPgSz());
  if (ctSectPrLastSect.isSetPgMar()) ctSectPrSect1.setPgMar(ctSectPrLastSect.getPgMar());
  if (ctSectPrLastSect.isSetPgNumType()) ctSectPrSect1.setPgNumType(ctSectPrLastSect.getPgNumType());
  if (ctSectPrLastSect.isSetCols()) ctSectPrSect1.setCols(ctSectPrLastSect.getCols());
  if (ctSectPrLastSect.isSetDocGrid()) ctSectPrSect1.setDocGrid(ctSectPrLastSect.getDocGrid());
  
  //Setting "there is a title page" for section 1 to make first header work.
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff ctOnOff = org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff.Factory.newInstance();
  ctOnOff.setVal(true);
  ctSectPrSect1.setTitlePg(ctOnOff);

  //Removing first and old default header references from last section (now section 2) 
  //and setting header reference of even header to be the default header reference for this last section.
  for (int i = ctSectPrLastSect.getHeaderReferenceArray().length-1; i >= 0; i--) {
   org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef ctHdrFtrRef = ctSectPrLastSect.getHeaderReferenceArray(i);
   if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.FIRST) {
    ctSectPrLastSect.removeHeaderReference(i);
    continue;
   }
   if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.DEFAULT) {
    ctSectPrLastSect.removeHeaderReference(i);
    continue;
   }
   if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.EVEN) {
    ctHdrFtrRef.setType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.DEFAULT); //change this from STHdrFtr.EVEN to STHdrFtr.DEFAULT  
   }
  }
  
  //Unsetting "there is a title page" for the last section.
  if (ctSectPrLastSect.isSetTitlePg()) ctSectPrLastSect.unsetTitlePg();
  
  //Unsetting page numbering type for the last section. So it continues the previous.
  if (ctSectPrLastSect.isSetPgNumType()) ctSectPrLastSect.unsetPgNumType();
 }
 
 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument(new FileInputStream("./WordDocumentSource.docx"));
  
  addLastPageInSeparateSection(document);
  
  FileOutputStream out = new FileOutputStream("./WordDocumentResult.docx");
  document.write(out);
  out.close();
  document.close();
 }
}

如果它不适合您,我需要能够在某处下载您使用的源Word文档。然后我可以得到代码无法使用该文档工作的确切原因。

也许将整个部分属性从下面的部分复制到新插入的部分会更好,而不是复制所有单个部分属性。此外,代码可以更多地划分为单个方法。这在下面的代码示例中完成。为了完整起见,我将保留旧示例。

import java.io.*;

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

public class WordAddLastPageWithoutHeader {
    
 static org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr getDocumentBodySectPr(XWPFDocument document) {
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1 ctDocument = document.getDocument();
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody ctBody = ctDocument.getBody();
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrDocumentBody = ctBody.getSectPr();
  return ctSectPrDocumentBody;   
 }
    
 static org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr getNextSectPr(XWPFParagraph paragraph) {
  // get the section settings of next section in document
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrNextSect = null;
  // maybe next section settings are in a paragraph
  XWPFDocument document = paragraph.getDocument(); 
  int pos = document.getPosOfParagraph(paragraph);
  for (int p = pos; p < document.getParagraphs().size(); p++) {
   paragraph = document.getParagraphArray(p);
   if (paragraph.getCTP().getPPr() != null) {
    ctSectPrNextSect = paragraph.getCTP().getPPr().getSectPr();   
   }
   if (ctSectPrNextSect != null) break;
  }
  // if not in a paragraph next section settings are in documetn body
  if (ctSectPrNextSect == null) { 
   ctSectPrNextSect = getDocumentBodySectPr(document);
  }
  return ctSectPrNextSect;   
 }
    
 static XWPFParagraph addSectionbreak(XWPFDocument document) {
  XWPFParagraph paragraph = null;;
  // add a paragraph for section settings for new section above and section break.
  paragraph = document.createParagraph();
  // get next section properties, which were section properties for previous section above
  org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrNextSect = getNextSectPr(paragraph);
  // set a copy of section properties for previous section above as section properties for new section
  if (ctSectPrNextSect != null) {
   org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrNewSect = (org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr)ctSectPrNextSect.copy();
   paragraph.getCTP().addNewPPr().setSectPr(ctSectPrNewSect);  
   return paragraph;  
  }
  return null;
 }
    
 private static void addLastPageInSeparateSection(XWPFDocument document) {
     
  XWPFParagraph paragraph;
  XWPFRun run;
  XWPFHeader header;
  
  // add a header for even pages, which gets the default header for page in last section later
  header = document.createHeader(HeaderFooterType.EVEN);
  for (int p = header.getParagraphs().size()-1; p >=0; p--) {
   paragraph = header.getParagraphArray(p);
   header.removeParagraph(paragraph);
  }
  paragraph = header.createParagraph();
  run = paragraph.createRun();
  run.setText("Header EVEN = DEFAULT in last section"); // don't set text here if you wants the header of the page in last section empty.
  
  paragraph = addSectionbreak(document);
  
  if (paragraph != null) {
   org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrNewSect = paragraph.getCTP().getPPr().getSectPr();
   // remove even header and footer from new section
   for (int i = ctSectPrNewSect.getHeaderReferenceArray().length-1; i >= 0; i--) {
    org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef ctHdrFtrRef = ctSectPrNewSect.getHeaderReferenceArray(i);
    if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.EVEN) {
     ctSectPrNewSect.removeHeaderReference(i);
     continue;
    }
   }
   for (int i = ctSectPrNewSect.getFooterReferenceArray().length-1; i >= 0; i--) {
    org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef ctHdrFtrRef = ctSectPrNewSect.getFooterReferenceArray(i);
    if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.EVEN) {
     ctSectPrNewSect.removeFooterReference(i);
     continue;
    }
   }
   // remove first and default header references from last section (now section below the new section) 
   // and set header reference of even header to be the default header reference for this last section
   org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr ctSectPrDocumentBody = getDocumentBodySectPr(document);
   if (ctSectPrDocumentBody != null) {
    for (int i = ctSectPrDocumentBody.getHeaderReferenceArray().length-1; i >= 0; i--) {
     org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef ctHdrFtrRef = ctSectPrDocumentBody.getHeaderReferenceArray(i);
     if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.FIRST) {
      ctSectPrDocumentBody.removeHeaderReference(i);
      continue;
     }
     if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.DEFAULT) {
      ctSectPrDocumentBody.removeHeaderReference(i);
      continue;
     }
     if (ctHdrFtrRef.getType() == org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.EVEN) {
      ctHdrFtrRef.setType(org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr.DEFAULT); //change this from STHdrFtr.EVEN to STHdrFtr.DEFAULT  
     }
    }
    // unset "there is a title page" for the last section.
    if (ctSectPrDocumentBody.isSetTitlePg()) ctSectPrDocumentBody.unsetTitlePg();
    // unset page numbering type for the last section. So it continues the previous.
    if (ctSectPrDocumentBody.isSetPgNumType()) ctSectPrDocumentBody.unsetPgNumType();
   }
   
   try {
    // unset evenAndOddHeaders in settings.xml
    java.lang.reflect.Field _settings = XWPFDocument.class.getDeclaredField("settings");
    _settings.setAccessible(true); 
    XWPFSettings xwpfsettings = (XWPFSettings)_settings.get(document); 
    java.lang.reflect.Field _ctSettings = XWPFSettings.class.getDeclaredField("ctSettings");
    _ctSettings.setAccessible(true); 
    org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSettings ctsettings = 
     (org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSettings)_ctSettings.get(xwpfsettings); 
    if (ctsettings.isSetEvenAndOddHeaders()) ctsettings.unsetEvenAndOddHeaders();
   } catch(Exception ex) {
    ex.printStackTrace();
   }
  }

  // content for the last section
  paragraph = document.createParagraph();
  run = paragraph.createRun();  
  run.setText("Last page. Page in last section ...");
  //ToDo: further content of last page in separate section

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

  XWPFDocument document = new XWPFDocument(new FileInputStream("./WordDocumentSource.docx"));
  
  addLastPageInSeparateSection(document);
  
  FileOutputStream out = new FileOutputStream("./WordDocumentResult.docx");
  document.write(out);
  out.close();
  document.close();
 }
}