57366: XWPFTable to Header / Footer

Task-Url: https://bz.apache.org/bugzilla/show_bug.cgi?id=57366

This update contains a breaking change

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1767175 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Mark Murphy 2016-10-30 18:49:16 +00:00
parent ca4a7d2f27
commit 14babbb970
6 changed files with 237 additions and 39 deletions

View File

@ -38,14 +38,10 @@ public class BetterHeaderFooterExample {
// create header/footer functions insert an empty paragraph // create header/footer functions insert an empty paragraph
XWPFHeader head = doc.createHeader(HeaderFooterType.DEFAULT); XWPFHeader head = doc.createHeader(HeaderFooterType.DEFAULT);
p = head.getParagraphArray(0); head.createParagraph().createRun().setText("header");
r = p.createRun();
r.setText("header");
XWPFFooter foot = doc.createFooter(HeaderFooterType.DEFAULT); XWPFFooter foot = doc.createFooter(HeaderFooterType.DEFAULT);
p = foot.getParagraphArray(0); foot.createParagraph().createRun().setText("footer");
r = p.createRun();
r.setText("footer");
try { try {
OutputStream os = new FileOutputStream(new File("header2.docx")); OutputStream os = new FileOutputStream(new File("header2.docx"));

View File

@ -0,0 +1,97 @@
/* ====================================================================
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.usermodel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGridCol;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblLayoutType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblLayoutType;
public class HeaderFooterTable {
public static void main(String[] args) throws IOException {
XWPFDocument doc = new XWPFDocument();
// Create a header with a 1 row, 3 column table
// changes made for issue 57366 allow a new header or footer
// to be created empty. This is a change. You will have to add
// either a paragraph or a table to the header or footer for
// the document to be considered valid.
XWPFHeader hdr = doc.createHeader(HeaderFooterType.DEFAULT);
XWPFTable tbl = hdr.createTable(1, 3);
// Set the padding around text in the cells to 1/10th of an inch
int pad = (int) (.1 * 1440);
tbl.setCellMargins(pad, pad, pad, pad);
// Set table width to 6.5 inches in 1440ths of a point
tbl.setWidth((int)(6.5 * 1440));
// Can not yet set table or cell width properly, tables default to
// autofit layout, and this requires fixed layout
CTTbl ctTbl = tbl.getCTTbl();
CTTblPr ctTblPr = ctTbl.addNewTblPr();
CTTblLayoutType layoutType = ctTblPr.addNewTblLayout();
layoutType.setType(STTblLayoutType.FIXED);
// Now set up a grid for the table, cells will fit into the grid
// Each cell width is 3120 in 1440ths of an inch, or 1/3rd of 6.5"
BigInteger w = new BigInteger("3120");
CTTblGrid grid = ctTbl.addNewTblGrid();
for (int i = 0; i < 3; i++) {
CTTblGridCol gridCol = grid.addNewGridCol();
gridCol.setW(w);
}
// Add paragraphs to the cells
XWPFTableRow row = tbl.getRow(0);
XWPFTableCell cell = row.getCell(0);
XWPFParagraph p = cell.getParagraphArray(0);
XWPFRun r = p.createRun();
r.setText("header left cell");
cell = row.getCell(1);
p = cell.getParagraphArray(0);
r = p.createRun();
r.setText("header center cell");
cell = row.getCell(2);
p = cell.getParagraphArray(0);
r = p.createRun();
r.setText("header right cell");
// Create a footer with a Paragraph
XWPFFooter ftr = doc.createFooter(HeaderFooterType.DEFAULT);
p = ftr.createParagraph();
r = p.createRun();
r.setText("footer text");
OutputStream os = new FileOutputStream(new File("headertable.docx"));
doc.write(os);
doc.close();
}
}

View File

@ -271,19 +271,19 @@ public class XWPFHeaderFooterPolicy {
ftr.setPArray(i, paragraphs[i].getCTP()); ftr.setPArray(i, paragraphs[i].getCTP());
} }
} else { } else {
CTP p = ftr.addNewP(); // CTP p = ftr.addNewP();
CTBody body = doc.getDocument().getBody(); // CTBody body = doc.getDocument().getBody();
if (body.sizeOfPArray() > 0) { // if (body.sizeOfPArray() > 0) {
CTP p0 = body.getPArray(0); // CTP p0 = body.getPArray(0);
if (p0.isSetRsidR()) { // if (p0.isSetRsidR()) {
byte[] rsidr = p0.getRsidR(); // byte[] rsidr = p0.getRsidR();
byte[] rsidrdefault = p0.getRsidRDefault(); // byte[] rsidrdefault = p0.getRsidRDefault();
p.setRsidP(rsidr); // p.setRsidP(rsidr);
p.setRsidRDefault(rsidrdefault); // p.setRsidRDefault(rsidrdefault);
} // }
} // }
CTPPr pPr = p.addNewPPr(); // CTPPr pPr = p.addNewPPr();
pPr.addNewPStyle().setVal(pStyle); // pPr.addNewPStyle().setVal(pStyle);
} }
return ftr; return ftr;
} }

View File

@ -43,10 +43,10 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
* Parent of XWPF headers and footers * Parent of XWPF headers and footers
*/ */
public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBody { public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBody {
List<XWPFParagraph> paragraphs = new ArrayList<XWPFParagraph>(1); List<XWPFParagraph> paragraphs = new ArrayList<XWPFParagraph>();
List<XWPFTable> tables = new ArrayList<XWPFTable>(1); List<XWPFTable> tables = new ArrayList<XWPFTable>();
List<XWPFPictureData> pictures = new ArrayList<XWPFPictureData>(); List<XWPFPictureData> pictures = new ArrayList<XWPFPictureData>();
List<IBodyElement> bodyElements = new ArrayList<IBodyElement>(1); List<IBodyElement> bodyElements = new ArrayList<IBodyElement>();
CTHdrFtr headerFooter; CTHdrFtr headerFooter;
XWPFDocument document; XWPFDocument document;
@ -323,13 +323,75 @@ public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBo
/** /**
* Adds a new paragraph at the end of the header or footer * Adds a new paragraph at the end of the header or footer
*
* @return new {@link XWPFParagraph} object
*/ */
public XWPFParagraph createParagraph() { public XWPFParagraph createParagraph() {
XWPFParagraph paragraph = new XWPFParagraph(headerFooter.addNewP(), this); XWPFParagraph paragraph = new XWPFParagraph(headerFooter.addNewP(), this);
paragraphs.add(paragraph); paragraphs.add(paragraph);
bodyElements.add(paragraph);
return paragraph; return paragraph;
} }
/**
* Adds a new table at the end of the header or footer
*
* @param rows - number of rows in the table
* @param cols - number of columns in the table
* @return new {@link XWPFTable} object
*/
public XWPFTable createTable(int rows, int cols) {
XWPFTable table = new XWPFTable(headerFooter.addNewTbl(), this, rows, cols);
tables.add(table);
bodyElements.add(table);
return table;
}
/**
* Removes a specific paragraph from this header / footer
*
* @param paragraph - {@link XWPFParagraph} object to remove
*/
public void removeParagraph(XWPFParagraph paragraph) {
if (paragraphs.contains(paragraph)) {
CTP ctP = paragraph.getCTP();
XmlCursor c = ctP.newCursor();
c.removeXml();
c.dispose();
paragraphs.remove(paragraph);
bodyElements.remove(paragraph);
}
}
/**
* Removes a specific table from this header / footer
*
* @param table - {@link XWPFTable} object to remove
*/
public void removeTable(XWPFTable table) {
if (tables.contains(table)) {
CTTbl ctTbl = table.getCTTbl();
XmlCursor c = ctTbl.newCursor();
c.removeXml();
c.dispose();
tables.remove(table);
bodyElements.remove(table);
}
}
/**
* Clears all paragraphs and tables from this header / footer
*/
public void clearHeaderFooter() {
XmlCursor c = headerFooter.newCursor();
c.removeXmlContents();
c.dispose();
paragraphs.clear();
tables.clear();
bodyElements.clear();
CTP ctp = CTP.Factory.newInstance();
}
/** /**
* add a new paragraph at position of the cursor * add a new paragraph at position of the cursor
* *
@ -538,4 +600,25 @@ public abstract class XWPFHeaderFooter extends POIXMLDocumentPart implements IBo
public POIXMLDocumentPart getPart() { public POIXMLDocumentPart getPart() {
return this; return this;
} }
@Override
protected void prepareForCommit() {
// must contain at least an empty paragraph
if (bodyElements.size() == 0) {
createParagraph();
}
// Cells must contain at least an empty paragraph
for (XWPFTable tbl : tables) {
for (XWPFTableRow row : tbl.tableRows) {
for (XWPFTableCell cell : row.getTableCells()) {
if (cell.getBodyElements().size() == 0) {
cell.addParagraph();
}
}
}
}
super.prepareForCommit();
}
} }

View File

@ -17,16 +17,22 @@
package org.apache.poi.xwpf.usermodel; package org.apache.poi.xwpf.usermodel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.IOException; import java.io.IOException;
import junit.framework.TestCase;
import org.apache.poi.xwpf.XWPFTestDataSamples; import org.apache.poi.xwpf.XWPFTestDataSamples;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.junit.Test;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
public final class TestXWPFHeader extends TestCase { public final class TestXWPFHeader {
@Test
public void testSimpleHeader() throws IOException { public void testSimpleHeader() throws IOException {
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerFooter.docx"); XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerFooter.docx");
@ -38,6 +44,7 @@ public final class TestXWPFHeader extends TestCase {
assertNotNull(footer); assertNotNull(footer);
} }
@Test
public void testImageInHeader() throws IOException { public void testImageInHeader() throws IOException {
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerPic.docx"); XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("headerPic.docx");
@ -49,6 +56,7 @@ public final class TestXWPFHeader extends TestCase {
assertEquals(1, header.getRelations().size()); assertEquals(1, header.getRelations().size());
} }
@Test
public void testSetHeader() throws IOException { public void testSetHeader() throws IOException {
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx"); XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx");
// no header is set (yet) // no header is set (yet)
@ -56,6 +64,7 @@ public final class TestXWPFHeader extends TestCase {
assertNull(policy.getDefaultHeader()); assertNull(policy.getDefaultHeader());
assertNull(policy.getFirstPageHeader()); assertNull(policy.getFirstPageHeader());
assertNull(policy.getDefaultFooter()); assertNull(policy.getDefaultFooter());
assertNull(policy.getFirstPageFooter());
CTP ctP1 = CTP.Factory.newInstance(); CTP ctP1 = CTP.Factory.newInstance();
CTR ctR1 = ctP1.addNewR(); CTR ctR1 = ctP1.addNewR();
@ -95,29 +104,31 @@ public final class TestXWPFHeader extends TestCase {
XWPFHeader headerD = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, pars); XWPFHeader headerD = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT, pars);
XWPFHeader headerF = policy.createHeader(XWPFHeaderFooterPolicy.FIRST); XWPFHeader headerF = policy.createHeader(XWPFHeaderFooterPolicy.FIRST);
// Set a default footer and capture the returned XWPFFooter object. // Set a default footer and capture the returned XWPFFooter object.
XWPFFooter footer = policy.createFooter(XWPFHeaderFooterPolicy.DEFAULT, pars2); XWPFFooter footerD = policy.createFooter(XWPFHeaderFooterPolicy.DEFAULT, pars2);
XWPFFooter footerF = policy.createFooter(XWPFHeaderFooterPolicy.FIRST);
// Ensure the headers and footer were set correctly.... // Ensure the headers and footer were set correctly....
assertNotNull(policy.getDefaultHeader()); assertNotNull(policy.getDefaultHeader());
assertNotNull(policy.getFirstPageHeader()); assertNotNull(policy.getFirstPageHeader());
assertNotNull(policy.getDefaultFooter()); assertNotNull(policy.getDefaultFooter());
assertNotNull(policy.getFirstPageFooter());
// ....and that the footer object captured above contains two // ....and that the footer object captured above contains two
// paragraphs of text. // paragraphs of text.
assertEquals(2, footer.getParagraphs().size()); assertEquals(2, footerD.getParagraphs().size());
assertEquals(0, footerF.getParagraphs().size());
// Check the header created with the paragraph got them, and the one // Check the header created with the paragraph got them, and the one
// created without got an empty one // created without got none
assertEquals(1, headerD.getParagraphs().size()); assertEquals(1, headerD.getParagraphs().size());
assertEquals(1, headerF.getParagraphs().size());
assertEquals(tText, headerD.getParagraphs().get(0).getText()); assertEquals(tText, headerD.getParagraphs().get(0).getText());
assertEquals("", headerF.getParagraphs().get(0).getText());
assertEquals(0, headerF.getParagraphs().size());
// As an additional check, recover the defauls footer and // As an additional check, recover the defauls footer and
// make sure that it contains two paragraphs of text and that // make sure that it contains two paragraphs of text and that
// both do hold what is expected. // both do hold what is expected.
footer = policy.getDefaultFooter(); footerD = policy.getDefaultFooter();
XWPFParagraph[] paras = footer.getParagraphs().toArray(new XWPFParagraph[0]); XWPFParagraph[] paras = footerD.getParagraphs().toArray(new XWPFParagraph[0]);
assertEquals(2, paras.length); assertEquals(2, paras.length);
assertEquals("First paragraph for the footer", paras[0].getText()); assertEquals("First paragraph for the footer", paras[0].getText());
@ -126,12 +137,15 @@ public final class TestXWPFHeader extends TestCase {
// Add some text to the empty header // Add some text to the empty header
String fText1 = "New Text!"; String fText1 = "New Text!";
headerF.getParagraphs().get(0).insertNewRun(0).setText(fText1); String fText2 = "More Text!";
// TODO Add another paragraph and check headerF.createParagraph().insertNewRun(0).setText(fText1);
headerF.createParagraph().insertNewRun(0).setText(fText2);
// headerF.getParagraphs().get(0).insertNewRun(0).setText(fText1);
// Check it // Check it
assertEquals(tText, headerD.getParagraphs().get(0).getText()); assertEquals(tText, headerD.getParagraphs().get(0).getText());
assertEquals(fText1, headerF.getParagraphs().get(0).getText()); assertEquals(fText1, headerF.getParagraphs().get(0).getText());
assertEquals(fText2, headerF.getParagraphs().get(1).getText());
// Save, re-open, ensure it's all still there // Save, re-open, ensure it's all still there
@ -141,7 +155,7 @@ public final class TestXWPFHeader extends TestCase {
assertNotNull(policy.getFirstPageHeader()); assertNotNull(policy.getFirstPageHeader());
assertNull(policy.getEvenPageHeader()); assertNull(policy.getEvenPageHeader());
assertNotNull(policy.getDefaultFooter()); assertNotNull(policy.getDefaultFooter());
assertNull(policy.getFirstPageFooter()); assertNotNull(policy.getFirstPageFooter());
assertNull(policy.getEvenPageFooter()); assertNull(policy.getEvenPageFooter());
// Check the new headers still have their text // Check the new headers still have their text
@ -149,16 +163,20 @@ public final class TestXWPFHeader extends TestCase {
headerF = policy.getFirstPageHeader(); headerF = policy.getFirstPageHeader();
assertEquals(tText, headerD.getParagraphs().get(0).getText()); assertEquals(tText, headerD.getParagraphs().get(0).getText());
assertEquals(fText1, headerF.getParagraphs().get(0).getText()); assertEquals(fText1, headerF.getParagraphs().get(0).getText());
assertEquals(fText2, headerF.getParagraphs().get(1).getText());
// Check the new footers have their new text too // Check the new footers have their new text too
footer = policy.getDefaultFooter(); footerD = policy.getDefaultFooter();
paras = footer.getParagraphs().toArray(new XWPFParagraph[0]); paras = footerD.getParagraphs().toArray(new XWPFParagraph[0]);
footerF = policy.getFirstPageFooter();
assertEquals(2, paras.length); assertEquals(2, paras.length);
assertEquals("First paragraph for the footer", paras[0].getText()); assertEquals("First paragraph for the footer", paras[0].getText());
assertEquals("Second paragraph for the footer", paras[1].getText()); assertEquals("Second paragraph for the footer", paras[1].getText());
assertEquals(1, footerF.getParagraphs().size());
} }
@Test
public void testSetWatermark() throws IOException { public void testSetWatermark() throws IOException {
XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx"); XWPFDocument sampleDoc = XWPFTestDataSamples.openSampleDocument("SampleDoc.docx");
@ -183,18 +201,22 @@ public final class TestXWPFHeader extends TestCase {
assertNotNull(policy.getEvenPageHeader()); assertNotNull(policy.getEvenPageHeader());
} }
@Test
public void testAddPictureData() { public void testAddPictureData() {
// TODO // TODO
} }
@Test
public void testGetAllPictures() { public void testGetAllPictures() {
// TODO // TODO
} }
@Test
public void testGetAllPackagePictures() { public void testGetAllPackagePictures() {
// TODO // TODO
} }
@Test
public void testGetPictureDataById() { public void testGetPictureDataById() {
// TODO // TODO
} }

View File

@ -73,7 +73,7 @@ public class TestXWPFPictureData extends TestCase {
// Add a default header // Add a default header
policy = doc.createHeaderFooterPolicy(); policy = doc.createHeaderFooterPolicy();
XWPFHeader header = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT); XWPFHeader header = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT);
header.getParagraphs().get(0).createRun().setText("Hello, Header World!"); header.createParagraph().createRun().setText("Hello, Header World!");
header.createParagraph().createRun().setText("Paragraph 2"); header.createParagraph().createRun().setText("Paragraph 2");
assertEquals(0, header.getAllPictures().size()); assertEquals(0, header.getAllPictures().size());
assertEquals(2, header.getParagraphs().size()); assertEquals(2, header.getParagraphs().size());