diff options
Diffstat (limited to 'src/ooxml')
5 files changed, 376 insertions, 44 deletions
diff --git a/src/ooxml/java/org/apache/poi/xwpf/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/XWPFDocument.java index 6b25a42b9e..e5d5924df2 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/XWPFDocument.java @@ -17,11 +17,16 @@ package org.apache.poi.xwpf; import java.io.IOException; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Iterator; import org.apache.poi.POIXMLDocument; +import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; +import org.apache.poi.xwpf.usermodel.XWPFComment; +import org.apache.poi.xwpf.usermodel.XWPFHyperlink; +import org.apache.poi.xwpf.usermodel.XWPFParagraph; +import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.xmlbeans.XmlException; import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.exceptions.OpenXML4JException; @@ -30,23 +35,14 @@ import org.openxml4j.opc.PackagePart; import org.openxml4j.opc.PackageRelationship; import org.openxml4j.opc.PackageRelationshipCollection; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument; import org.openxmlformats.schemas.wordprocessingml.x2006.main.DocumentDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.FtrDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument; import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; - -import org.apache.poi.xwpf.usermodel.XWPFFooter; -import org.apache.poi.xwpf.usermodel.XWPFHeader; -import org.apache.poi.xwpf.usermodel.XWPFHyperlink; -import org.apache.poi.xwpf.usermodel.XWPFParagraph; -import org.apache.poi.xwpf.usermodel.XWPFComment; -import org.apache.poi.xwpf.usermodel.XWPFTable; /** * Experimental class to do low level processing @@ -75,10 +71,9 @@ public class XWPFDocument extends POIXMLDocument { protected List<XWPFHyperlink> hyperlinks; protected List<XWPFParagraph> paragraphs; protected List<XWPFTable> tables; - /** Should only ever be zero or one of these, we think */ - protected XWPFHeader header; - /** Should only ever be zero or one of these, we think */ - protected XWPFFooter footer; + + /** Handles the joy of different headers/footers for different pages */ + private XWPFHeaderFooterPolicy headerFooterPolicy; public XWPFDocument(Package container) throws OpenXML4JException, IOException, XmlException { super(container); @@ -133,23 +128,8 @@ public class XWPFDocument extends POIXMLDocument { embedds.add(getTargetPart(rel)); } - // Fetch the header, if there's one - PackageRelationshipCollection headerRel = getCorePart().getRelationshipsByType(HEADER_RELATION_TYPE); - if(headerRel != null && headerRel.size() > 0) { - PackagePart headerPart = getTargetPart(headerRel.getRelationship(0)); - header = new XWPFHeader( - HdrDocument.Factory.parse(headerPart.getInputStream()).getHdr() - ); - } - - // Fetch the footer, if there's one - PackageRelationshipCollection footerRel = getCorePart().getRelationshipsByType(FOOTER_RELATION_TYPE); - if(footerRel != null && footerRel.size() > 0) { - PackagePart footerPart = getTargetPart(footerRel.getRelationship(0)); - footer = new XWPFFooter( - FtrDocument.Factory.parse(footerPart.getInputStream()).getFtr() - ); - } + // Sort out headers and footers + headerFooterPolicy = new XWPFHeaderFooterPolicy(this); } /** @@ -207,11 +187,26 @@ public class XWPFDocument extends POIXMLDocument { ); } - public XWPFHeader getDocumentHeader() { - return header; + /** + * Get the document part that's defined as the + * given relationship of the core document. + */ + public PackagePart getPartById(String id) { + try { + return getTargetPart( + getCorePart().getRelationship(id) + ); + } catch(InvalidFormatException e) { + throw new IllegalArgumentException(e); + } } - public XWPFFooter getDocumentFooter() { - return footer; + + /** + * Returns the policy on headers and footers, which + * also provides a way to get at them. + */ + public XWPFHeaderFooterPolicy getHeaderFooterPolicy() { + return headerFooterPolicy; } /** diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java new file mode 100644 index 0000000000..83a67ade99 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java @@ -0,0 +1,159 @@ +/* ==================================================================== + 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.xwpf.model; + +import java.io.IOException; + +import org.apache.poi.xwpf.XWPFDocument; +import org.apache.poi.xwpf.usermodel.XWPFFooter; +import org.apache.poi.xwpf.usermodel.XWPFHeader; +import org.apache.xmlbeans.XmlException; +import org.openxml4j.opc.PackagePart; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.FtrDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr; + +/** + * A .docx file can have no headers/footers, the same header/footer + * on each page, odd/even page footers, and optionally also + * a different header/footer on the first page. + * This class handles sorting out what there is, and giving you + * the right headers and footers for the document. + */ +public class XWPFHeaderFooterPolicy { + private XWPFHeader firstPageHeader; + private XWPFFooter firstPageFooter; + + private XWPFHeader evenPageHeader; + private XWPFFooter evenPageFooter; + + private XWPFHeader defaultHeader; + private XWPFFooter defaultFooter; + + + /** + * Figures out the policy for the given document, + * and creates any header and footer objects + * as required. + */ + public XWPFHeaderFooterPolicy(XWPFDocument doc) throws IOException, XmlException { + // Grab what headers and footers have been defined + // 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 + CTSectPr sectPr = doc.getDocumentBody().getSectPr(); + for(int i=0; i<sectPr.sizeOfHeaderReferenceArray(); i++) { + // Get the header + CTHdrFtrRef ref = sectPr.getHeaderReferenceArray(i); + PackagePart hdrPart = doc.getPartById(ref.getId()); + XWPFHeader hdr = new XWPFHeader( + HdrDocument.Factory.parse(hdrPart.getInputStream()).getHdr() + ); + + // Assign it + if(ref.getType() == STHdrFtr.FIRST) { + firstPageHeader = hdr; + } else if(ref.getType() == STHdrFtr.EVEN) { + evenPageHeader = hdr; + } else { + defaultHeader = hdr; + } + } + for(int i=0; i<sectPr.sizeOfFooterReferenceArray(); i++) { + // Get the footer + CTHdrFtrRef ref = sectPr.getFooterReferenceArray(i); + PackagePart ftrPart = doc.getPartById(ref.getId()); + XWPFFooter ftr = new XWPFFooter( + FtrDocument.Factory.parse(ftrPart.getInputStream()).getFtr() + ); + + // Assign it + if(ref.getType() == STHdrFtr.FIRST) { + firstPageFooter = ftr; + } else if(ref.getType() == STHdrFtr.EVEN) { + evenPageFooter = ftr; + } else { + defaultFooter = ftr; + } + } + } + + + public XWPFHeader getFirstPageHeader() { + return firstPageHeader; + } + public XWPFFooter getFirstPageFooter() { + return firstPageFooter; + } + /** + * Returns the odd page header. This is + * also the same as the default one... + */ + public XWPFHeader getOddPageHeader() { + return defaultHeader; + } + /** + * Returns the odd page footer. This is + * also the same as the default one... + */ + public XWPFFooter getOddPageFooter() { + return defaultFooter; + } + public XWPFHeader getEvenPageHeader() { + return evenPageHeader; + } + public XWPFFooter getEvenPageFooter() { + return evenPageFooter; + } + public XWPFHeader getDefaultHeader() { + return defaultHeader; + } + public XWPFFooter getDefaultFooter() { + return defaultFooter; + } + + /** + * Get the header that applies to the given + * (1 based) page. + * @param pageNumber The one based page number + */ + public XWPFHeader getHeader(int pageNumber) { + if(pageNumber == 1 && firstPageHeader != null) { + return firstPageHeader; + } + if(pageNumber % 2 == 0 && evenPageHeader != null) { + return evenPageHeader; + } + return defaultHeader; + } + /** + * Get the footer that applies to the given + * (1 based) page. + * @param pageNumber The one based page number + */ + public XWPFFooter getFooter(int pageNumber) { + if(pageNumber == 1 && firstPageFooter != null) { + return firstPageFooter; + } + if(pageNumber % 2 == 0 && evenPageFooter != null) { + return evenPageFooter; + } + return defaultFooter; + } +} diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XMLWordDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XMLWordDocument.java index 5e30c28408..3a79da9894 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XMLWordDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XMLWordDocument.java @@ -17,6 +17,7 @@ package org.apache.poi.xwpf.usermodel; import org.apache.poi.xwpf.XWPFDocument; +import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; /** * High level representation of a ooxml text document. @@ -46,10 +47,7 @@ public class XMLWordDocument { return xwpfXML.getComments(); } - public XWPFHeader getHeader() { - return xwpfXML.getDocumentHeader(); - } - public XWPFFooter getFooter() { - return xwpfXML.getDocumentFooter(); + public XWPFHeaderFooterPolicy getHeaderFooterPolicy() { + return xwpfXML.getHeaderFooterPolicy(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java b/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java new file mode 100644 index 0000000000..b1f6971652 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xwpf/model/TestXWPFHeaderFooterPolicy.java @@ -0,0 +1,180 @@ +/* ==================================================================== + 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.xwpf.model; + +import java.io.File; + +import org.apache.poi.POIXMLDocument; +import org.apache.poi.xwpf.XWPFDocument; + +import junit.framework.TestCase; + +/** + * Tests for XWPF Header Footer Stuff + */ +public class TestXWPFHeaderFooterPolicy extends TestCase { + private XWPFDocument noHeader; + private XWPFDocument header; + private XWPFDocument headerFooter; + private XWPFDocument footer; + private XWPFDocument oddEven; + private XWPFDocument diffFirst; + + protected void setUp() throws Exception { + super.setUp(); + File file; + + file = new File( + System.getProperty("HWPF.testdata.path") + + File.separator + "NoHeadFoot.docx" + ); + assertTrue(file.exists()); + noHeader = new XWPFDocument(POIXMLDocument.openPackage(file.toString())); + + file = new File( + System.getProperty("HWPF.testdata.path") + + File.separator + "ThreeColHead.docx" + ); + assertTrue(file.exists()); + header = new XWPFDocument(POIXMLDocument.openPackage(file.toString())); + + file = new File( + System.getProperty("HWPF.testdata.path") + + File.separator + "SimpleHeadThreeColFoot.docx" + ); + assertTrue(file.exists()); + headerFooter = new XWPFDocument(POIXMLDocument.openPackage(file.toString())); + + file = new File( + System.getProperty("HWPF.testdata.path") + + File.separator + "FancyFoot.docx" + ); + assertTrue(file.exists()); + footer = new XWPFDocument(POIXMLDocument.openPackage(file.toString())); + + file = new File( + System.getProperty("HWPF.testdata.path") + + File.separator + "PageSpecificHeadFoot.docx" + ); + assertTrue(file.exists()); + oddEven = new XWPFDocument(POIXMLDocument.openPackage(file.toString())); + + file = new File( + System.getProperty("HWPF.testdata.path") + + File.separator + "DiffFirstPageHeadFoot.docx" + ); + assertTrue(file.exists()); + diffFirst = new XWPFDocument(POIXMLDocument.openPackage(file.toString())); + } + + public void testPolicy() throws Exception { + XWPFHeaderFooterPolicy policy; + + policy = noHeader.getHeaderFooterPolicy(); + assertNull(policy.getDefaultHeader()); + assertNull(policy.getDefaultFooter()); + + assertNull(policy.getHeader(1)); + assertNull(policy.getHeader(2)); + assertNull(policy.getHeader(3)); + assertNull(policy.getFooter(1)); + assertNull(policy.getFooter(2)); + assertNull(policy.getFooter(3)); + + + policy = header.getHeaderFooterPolicy(); + assertNotNull(policy.getDefaultHeader()); + assertNull(policy.getDefaultFooter()); + + assertEquals(policy.getDefaultHeader(), policy.getHeader(1)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(2)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(3)); + assertNull(policy.getFooter(1)); + assertNull(policy.getFooter(2)); + assertNull(policy.getFooter(3)); + + + policy = footer.getHeaderFooterPolicy(); + assertNull(policy.getDefaultHeader()); + assertNotNull(policy.getDefaultFooter()); + + assertNull(policy.getHeader(1)); + assertNull(policy.getHeader(2)); + assertNull(policy.getHeader(3)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(1)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(2)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(3)); + + + policy = headerFooter.getHeaderFooterPolicy(); + assertNotNull(policy.getDefaultHeader()); + assertNotNull(policy.getDefaultFooter()); + + assertEquals(policy.getDefaultHeader(), policy.getHeader(1)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(2)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(3)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(1)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(2)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(3)); + + + policy = oddEven.getHeaderFooterPolicy(); + assertNotNull(policy.getDefaultHeader()); + assertNotNull(policy.getDefaultFooter()); + assertNotNull(policy.getEvenPageHeader()); + assertNotNull(policy.getEvenPageFooter()); + + assertEquals(policy.getDefaultHeader(), policy.getHeader(1)); + assertEquals(policy.getEvenPageHeader(), policy.getHeader(2)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(3)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(1)); + assertEquals(policy.getEvenPageFooter(), policy.getFooter(2)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(3)); + + + policy = diffFirst.getHeaderFooterPolicy(); + assertNotNull(policy.getDefaultHeader()); + assertNotNull(policy.getDefaultFooter()); + assertNotNull(policy.getFirstPageHeader()); + assertNotNull(policy.getFirstPageFooter()); + assertNull(policy.getEvenPageHeader()); + assertNull(policy.getEvenPageFooter()); + + assertEquals(policy.getFirstPageHeader(), policy.getHeader(1)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(2)); + assertEquals(policy.getDefaultHeader(), policy.getHeader(3)); + assertEquals(policy.getFirstPageFooter(), policy.getFooter(1)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(2)); + assertEquals(policy.getDefaultFooter(), policy.getFooter(3)); + } + + public void testContents() throws Exception { + XWPFHeaderFooterPolicy policy; + + // Just test a few bits + policy = diffFirst.getHeaderFooterPolicy(); + + assertEquals( + "I am the header on the first page, and I" + '\u2019' + "m nice and simple\n", + policy.getFirstPageHeader().getText() + ); + assertEquals( + "First header column!\tMid header\tRight header!\n", + policy.getDefaultHeader().getText() + ); + } +} diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java index b88f937dce..5a775c361d 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java @@ -48,7 +48,7 @@ public class TestXWPFParagraph extends TestCase { * Check that we get the right paragraph from the header */ public void testHeaderParagraph() throws Exception { - XWPFHeader hdr = xml.getDocumentHeader(); + XWPFHeader hdr = xml.getHeaderFooterPolicy().getDefaultHeader(); assertNotNull(hdr); XWPFParagraph[] ps = hdr.getParagraphs(); |