From 37f1598ac2989fef0be17d65a7e0407802380f7a Mon Sep 17 00:00:00 2001 From: Mark Murphy Date: Thu, 13 Oct 2016 01:13:45 +0000 Subject: [PATCH] 53009: Problem creating header and footer Task-Url: https://bz.apache.org/bugzilla/show_bug.cgi?id=53009 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1764563 13f79535-47bb-0310-9956-ffa450edef68 --- .../usermodel/SimpleDocumentWithHeader.java | 60 ++++++++ .../poi/wp/usermodel/HeaderFooterType.java | 62 ++++++++ .../xwpf/model/XWPFHeaderFooterPolicy.java | 145 +++++++++++------- .../poi/xwpf/usermodel/XWPFDocument.java | 82 +++++++--- 4 files changed, 275 insertions(+), 74 deletions(-) create mode 100644 src/examples/src/org/apache/poi/xwpf/usermodel/SimpleDocumentWithHeader.java create mode 100644 src/java/org/apache/poi/wp/usermodel/HeaderFooterType.java diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/SimpleDocumentWithHeader.java b/src/examples/src/org/apache/poi/xwpf/usermodel/SimpleDocumentWithHeader.java new file mode 100644 index 0000000000..7595b5b499 --- /dev/null +++ b/src/examples/src/org/apache/poi/xwpf/usermodel/SimpleDocumentWithHeader.java @@ -0,0 +1,60 @@ +package org.apache.poi.xwpf.usermodel; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText; + +/** + * + * @author Richard Ngo + * + */ +public class SimpleDocumentWithHeader { + + private static XWPFParagraph[] pars; + + public static void main(String[] args) { + XWPFDocument doc = new XWPFDocument(); + + XWPFParagraph p = doc.createParagraph(); + + XWPFRun r = p.createRun(); + r.setText("Some Text"); + r.setBold(true); + r = p.createRun(); + r.setText("Goodbye"); + + CTP ctP = CTP.Factory.newInstance(); + CTText t = ctP.addNewR().addNewT(); + t.setStringValue("header"); + pars = new XWPFParagraph[1]; + p = new XWPFParagraph(ctP, doc); + pars[0] = p; + + XWPFHeaderFooterPolicy hfPolicy = doc.createHeaderFooterPolicy(); + hfPolicy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, pars); + + ctP = CTP.Factory.newInstance(); + t = ctP.addNewR().addNewT(); + t.setStringValue("My Footer"); + pars[0] = new XWPFParagraph(ctP, doc); + hfPolicy.createFooter(XWPFHeaderFooterPolicy.DEFAULT, pars); + + try { + OutputStream os = new FileOutputStream(new File("header.docx")); + doc.write(os); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/wp/usermodel/HeaderFooterType.java b/src/java/org/apache/poi/wp/usermodel/HeaderFooterType.java new file mode 100644 index 0000000000..bca6b6658d --- /dev/null +++ b/src/java/org/apache/poi/wp/usermodel/HeaderFooterType.java @@ -0,0 +1,62 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.wp.usermodel; + +/** + * @since POI v3.16 beta 1 + */ +public enum HeaderFooterType { + + /** + * This is the default header or Footer, It is displayed on every page where + * a more specific header or footer is not specified. It is always displayed + * on ODD pages that are not the first page of the section. + */ + DEFAULT(2), + + /** + * This is an even page header or footer, it is displayed on even pages that + * are not the first page of the section. + */ + EVEN(1), + + /** + * This is a first page header or footer It is displayed on the first page + * of the section. + */ + FIRST(3); + + private final int code; + + private HeaderFooterType(int i) { + code = i; + } + + public int toInt() { + return code; + } + + public static HeaderFooterType forInt(int i) { + for (HeaderFooterType type : values()) { + if (type.code == i) { + return type; + } + } + throw new IllegalArgumentException("Invalid HeaderFooterType code: " + i); + } +} diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java index e303fcf343..ac3d7d5e00 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java +++ b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java @@ -18,9 +18,6 @@ package org.apache.poi.xwpf.model; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; -import java.io.IOException; -import java.io.OutputStream; - import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLDocumentPart.RelationPart; import org.apache.poi.util.POILogFactory; @@ -90,7 +87,7 @@ public class XWPFHeaderFooterPolicy { * as required. */ public XWPFHeaderFooterPolicy(XWPFDocument doc) { - this(doc, doc.getDocument().getBody().getSectPr()); + this(doc, null); } /** @@ -103,6 +100,12 @@ public class XWPFHeaderFooterPolicy { // For now, we don't care about different ranges, as it // doesn't seem that .docx properly supports that // feature of the file format yet + if (sectPr == null) { + CTBody ctBody = doc.getDocument().getBody(); + sectPr = ctBody.isSetSectPr() + ? ctBody.getSectPr() + : ctBody.addNewSectPr(); + } this.doc = doc; for (int i = 0; i < sectPr.sizeOfHeaderReferenceArray(); i++) { // Get the header @@ -154,7 +157,7 @@ public class XWPFHeaderFooterPolicy { * Creates an empty header of the specified type, containing a single * empty paragraph, to which you can then set text, add more paragraphs etc. */ - public XWPFHeader createHeader(Enum type) throws IOException { + public XWPFHeader createHeader(Enum type) { return createHeader(type, null); } @@ -163,33 +166,35 @@ public class XWPFHeaderFooterPolicy { * supplied (and previously unattached!) paragraphs are * added to. */ - public XWPFHeader createHeader(Enum type, XWPFParagraph[] pars) throws IOException { - XWPFRelation relation = XWPFRelation.HEADER; - String pStyle = "Header"; - int i = getRelationIndex(relation); - HdrDocument hdrDoc = HdrDocument.Factory.newInstance(); - - XWPFHeader wrapper = (XWPFHeader) doc.createRelationship(relation, XWPFFactory.getInstance(), i); - wrapper.setXWPFDocument(doc); - - CTHdrFtr hdr = buildHdr(type, pStyle, wrapper, pars); - wrapper.setHeaderFooter(hdr); - - OutputStream outputStream = wrapper.getPackagePart().getOutputStream(); - hdrDoc.setHdr(hdr); - - assignHeader(wrapper, type); - hdrDoc.save(outputStream, DEFAULT_XML_OPTIONS); - outputStream.close(); - - return wrapper; + public XWPFHeader createHeader(Enum type, XWPFParagraph[] pars) { + XWPFHeader header = getHeader(type); + + if (header == null) { + HdrDocument hdrDoc = HdrDocument.Factory.newInstance(); + + XWPFRelation relation = XWPFRelation.HEADER; + int i = getRelationIndex(relation); + + XWPFHeader wrapper = (XWPFHeader) doc.createRelationship(relation, + XWPFFactory.getInstance(), i); + wrapper.setXWPFDocument(doc); + + String pStyle = "Header"; + CTHdrFtr hdr = buildHdr(type, pStyle, wrapper, pars); + wrapper.setHeaderFooter(hdr); + hdrDoc.setHdr(hdr); + assignHeader(wrapper, type); + header = wrapper; + } + + return header; } /** * Creates an empty footer of the specified type, containing a single * empty paragraph, to which you can then set text, add more paragraphs etc. */ - public XWPFFooter createFooter(Enum type) throws IOException { + public XWPFFooter createFooter(Enum type) { return createFooter(type, null); } @@ -198,25 +203,28 @@ public class XWPFHeaderFooterPolicy { * supplied (and previously unattached!) paragraphs are * added to. */ - public XWPFFooter createFooter(Enum type, XWPFParagraph[] pars) throws IOException { - XWPFRelation relation = XWPFRelation.FOOTER; - String pStyle = "Footer"; - int i = getRelationIndex(relation); - FtrDocument ftrDoc = FtrDocument.Factory.newInstance(); - - XWPFFooter wrapper = (XWPFFooter) doc.createRelationship(relation, XWPFFactory.getInstance(), i); - wrapper.setXWPFDocument(doc); - - CTHdrFtr ftr = buildFtr(type, pStyle, wrapper, pars); - wrapper.setHeaderFooter(ftr); - - OutputStream outputStream = wrapper.getPackagePart().getOutputStream(); - ftrDoc.setFtr(ftr); - - assignFooter(wrapper, type); - ftrDoc.save(outputStream, DEFAULT_XML_OPTIONS); - outputStream.close(); - return wrapper; + public XWPFFooter createFooter(Enum type, XWPFParagraph[] pars) { + XWPFFooter footer = getFooter(type); + + if (footer == null) { + FtrDocument ftrDoc = FtrDocument.Factory.newInstance(); + + XWPFRelation relation = XWPFRelation.FOOTER; + int i = getRelationIndex(relation); + + XWPFFooter wrapper = (XWPFFooter) doc.createRelationship(relation, + XWPFFactory.getInstance(), i); + wrapper.setXWPFDocument(doc); + + String pStyle = "Footer"; + CTHdrFtr ftr = buildFtr(type, pStyle, wrapper, pars); + wrapper.setHeaderFooter(ftr); + ftrDoc.setFtr(ftr); + assignFooter(wrapper, type); + footer = wrapper; + } + + return footer; } private int getRelationIndex(XWPFRelation relation) { @@ -349,6 +357,21 @@ public class XWPFHeaderFooterPolicy { } return defaultHeader; } + + /** + * Get this section header for the given type + * + * @param type of header to return + * @return {@link XWPFHeader} object + */ + public XWPFHeader getHeader(Enum type) { + if (type == STHdrFtr.EVEN) { + return evenPageHeader; + } else if (type == STHdrFtr.FIRST) { + return firstPageHeader; + } + return defaultHeader; + } /** * Get the footer that applies to the given @@ -365,19 +388,31 @@ public class XWPFHeaderFooterPolicy { } return defaultFooter; } + + /** + * Get this section footer for the given type + * + * @param type of footer to return + * @return {@link XWPFFooter} object + */ + public XWPFFooter getFooter(Enum type) { + if (type == STHdrFtr.EVEN) { + return evenPageFooter; + } else if (type == STHdrFtr.FIRST) { + return firstPageFooter; + } + return defaultFooter; + } + public void createWatermark(String text) { XWPFParagraph[] pars = new XWPFParagraph[1]; - try { - pars[0] = getWatermarkParagraph(text, 1); - createHeader(DEFAULT, pars); - pars[0] = getWatermarkParagraph(text, 2); - createHeader(FIRST, pars); - pars[0] = getWatermarkParagraph(text, 3); - createHeader(EVEN, pars); - } catch (IOException e) { - LOG.log(POILogger.ERROR, "error while creating watermark", e); - } + pars[0] = getWatermarkParagraph(text, 1); + createHeader(DEFAULT, pars); + pars[0] = getWatermarkParagraph(text, 2); + createHeader(FIRST, pars); + pars[0] = getWatermarkParagraph(text, 3); + createHeader(EVEN, pars); } /* diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index 23a63d0e80..eb9da3c332 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -1,5 +1,6 @@ /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 @@ -14,6 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + package org.apache.poi.xwpf.usermodel; import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; @@ -55,27 +57,13 @@ import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.PackageHelper; +import org.apache.poi.wp.usermodel.HeaderFooterType; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.DocumentDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.EndnotesDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.FootnotesDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.NumberingDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.STDocProtect; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; /** *

High(ish) level class for working with .docx files.

@@ -438,13 +426,69 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } public XWPFHeaderFooterPolicy createHeaderFooterPolicy() { if (headerFooterPolicy == null) { - if (! ctDocument.getBody().isSetSectPr()) { - ctDocument.getBody().addNewSectPr(); - } +// if (! ctDocument.getBody().isSetSectPr()) { +// ctDocument.getBody().addNewSectPr(); +// } headerFooterPolicy = new XWPFHeaderFooterPolicy(this); } return headerFooterPolicy; } + + /** + * Create a header of the given type + * + * @param type {@link HeaderFooterType} enum + * @return object of type {@link XWPFHeader} + */ + public XWPFHeader createHeader(HeaderFooterType type) { + XWPFHeaderFooterPolicy hfPolicy = createHeaderFooterPolicy(); + // TODO this needs to be migrated out into section code + if (type == HeaderFooterType.FIRST) { + CTSectPr ctSectPr = getSection(); + if (ctSectPr.isSetTitlePg() == false) { + CTOnOff titlePg = ctSectPr.addNewTitlePg(); + titlePg.setVal(STOnOff.ON); + } + } else if (type == HeaderFooterType.EVEN) { + // TODO Add support for Even/Odd headings and footers + } + return hfPolicy.createHeader(STHdrFtr.Enum.forInt(type.toInt())); + } + + + /** + * Create a footer of the given type + * + * @param type {@link HeaderFooterType} enum + * @return object of type {@link XWPFFooter} + */ + public XWPFFooter createFooter(HeaderFooterType type) { + XWPFHeaderFooterPolicy hfPolicy = createHeaderFooterPolicy(); + // TODO this needs to be migrated out into section code + if (type == HeaderFooterType.FIRST) { + CTSectPr ctSectPr = getSection(); + if (ctSectPr.isSetTitlePg() == false) { + CTOnOff titlePg = ctSectPr.addNewTitlePg(); + titlePg.setVal(STOnOff.ON); + } + } else if (type == HeaderFooterType.EVEN) { + // TODO Add support for Even/Odd headings and footers + } + return hfPolicy.createFooter(STHdrFtr.Enum.forInt(type.toInt())); + } + + /** + * Return the {@link CTSectPr} object that corresponds with the + * last section in this document. + * + * @return {@link CTSectPr} object + */ + private CTSectPr getSection() { + CTBody ctBody = getDocument().getBody(); + return (ctBody.isSetSectPr() ? + ctBody.getSectPr() : + ctBody.addNewSectPr()); + } /** * Returns the styles object used -- 2.39.5