Fix bug #45540 - Fix XSSF header and footer support, and include headers and footers in the output of XSSFExcelExtractor

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@682674 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-08-05 11:26:38 +00:00
parent cd56a91ef9
commit b118d1afb9
8 changed files with 207 additions and 73 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.5.1-beta2" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="add">45540 - Fix XSSF header and footer support, and include headers and footers in the output of XSSFExcelExtractor</action>
<action dev="POI-DEVELOPERS" type="add">45431 - Support for .xlsm files, sufficient for simple files to be loaded by excel without warning</action>
<action dev="POI-DEVELOPERS" type="add">New class org.apache.poi.hssf.record.RecordFormatException, which DDF uses instead of the HSSF version, and the HSSF version inherits from</action>
<action dev="POI-DEVELOPERS" type="add">45431 - Partial support for .xlm files. Not quite enough for excel to load them though</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.5.1-beta2" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="add">45540 - Fix XSSF header and footer support, and include headers and footers in the output of XSSFExcelExtractor</action>
<action dev="POI-DEVELOPERS" type="add">45431 - Support for .xlsm files, sufficient for simple files to be loaded by excel without warning</action>
<action dev="POI-DEVELOPERS" type="add">New class org.apache.poi.hssf.record.RecordFormatException, which DDF uses instead of the HSSF version, and the HSSF version inherits from</action>
<action dev="POI-DEVELOPERS" type="add">45431 - Partial support for .xlm files. Not quite enough for excel to load them though</action>

View File

@ -25,9 +25,8 @@ import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.HeaderFooter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.xmlbeans.XmlException;
import org.openxml4j.exceptions.OpenXML4JException;
@ -37,7 +36,7 @@ import org.openxml4j.opc.Package;
* Helper class to extract text from an OOXML Excel file
*/
public class XSSFExcelExtractor extends POIXMLTextExtractor {
private Workbook workbook;
private XSSFWorkbook workbook;
private boolean includeSheetNames = true;
private boolean formulasNotResults = false;
private boolean includeCellComments = false;
@ -91,18 +90,23 @@ public class XSSFExcelExtractor extends POIXMLTextExtractor {
StringBuffer text = new StringBuffer();
for(int i=0; i<workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
XSSFSheet sheet = (XSSFSheet)workbook.getSheetAt(i);
if(includeSheetNames) {
text.append(workbook.getSheetName(i) + "\n");
}
// Header, if present
if(sheet.getHeader() != null) {
// Header(s), if present
text.append(
extractHeaderFooter(sheet.getHeader())
extractHeaderFooter(sheet.getFirstHeader())
);
text.append(
extractHeaderFooter(sheet.getOddHeader())
);
text.append(
extractHeaderFooter(sheet.getEvenHeader())
);
}
// Rows and cells
for (Object rawR : sheet) {
Row row = (Row)rawR;
for(Iterator<Cell> ri = row.cellIterator(); ri.hasNext();) {
@ -133,12 +137,16 @@ public class XSSFExcelExtractor extends POIXMLTextExtractor {
text.append("\n");
}
// Finally footer, if present
if(sheet.getFooter() != null) {
// Finally footer(s), if present
text.append(
extractHeaderFooter(sheet.getFooter())
extractHeaderFooter(sheet.getFirstFooter())
);
text.append(
extractHeaderFooter(sheet.getOddFooter())
);
text.append(
extractHeaderFooter(sheet.getEvenFooter())
);
}
}
return text.toString();

View File

@ -24,12 +24,9 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHeaderFooter;
public abstract class XSSFHeaderFooter implements HeaderFooter {
private HeaderFooterHelper helper;
private CTHeaderFooter headerFooter;
private String value;
public XSSFHeaderFooter(CTHeaderFooter headerFooter) {
this.headerFooter = headerFooter;
this.value = getText();
this.value = this.value != null ? this.value : "";
this.helper = new HeaderFooterHelper();
}
@ -38,7 +35,10 @@ public abstract class XSSFHeaderFooter implements HeaderFooter {
}
public String getValue() {
return this.value;
String value = getText();
if(value == null)
return "";
return value;
}
public abstract String getText();

View File

@ -19,55 +19,101 @@ package org.apache.poi.xssf.usermodel.helpers;
public class HeaderFooterHelper {
// Note - XmlBeans handles entity encoding for us,
// so these should be & forms, not the &amp; ones!
private static final String HeaderFooterEntity_L = "&L";
private static final String HeaderFooterEntity_C = "&C";
private static final String HeaderFooterEntity_R = "&R";
private static final String HeaderFooterEntity = "&amp;";
private static final String HeaderFooterEntity_R = "&amp;R";
private static final String HeaderFooterEntity_L = "&amp;L";
private static final String HeaderFooterEntity_C = "&amp;C";
// These are other entities that may be used in the
// left, center or right. Not exhaustive
public static final String HeaderFooterEntity_File = "&F";
public static final String HeaderFooterEntity_Date = "&D";
public static final String HeaderFooterEntity_Time = "&T";
public String getCenterSection(String string) {
return getSection(string, HeaderFooterEntity_C);
}
public String getLeftSection(String string) {
return getSection(string, HeaderFooterEntity_L);
return getParts(string)[0];
}
public String getCenterSection(String string) {
return getParts(string)[1];
}
public String getRightSection(String string) {
return getSection(string, HeaderFooterEntity_R);
return getParts(string)[2];
}
public String setCenterSection(String string, String newCenter) {
return setSection(string, newCenter, HeaderFooterEntity_C);
}
public String setLeftSection(String string, String newLeft) {
return setSection(string, newLeft, HeaderFooterEntity_L);
String[] parts = getParts(string);
parts[0] = newLeft;
return joinParts(parts);
}
public String setCenterSection(String string, String newCenter) {
String[] parts = getParts(string);
parts[1] = newCenter;
return joinParts(parts);
}
public String setRightSection(String string, String newRight) {
return setSection(string, newRight, HeaderFooterEntity_R);
String[] parts = getParts(string);
parts[2] = newRight;
return joinParts(parts);
}
public String setSection(String string, String newSection, String entity) {
string = string != null ? string : "";
String oldSection = getSection(string, entity);
if (oldSection.equals("")) {
return string.concat(entity + newSection);
/**
* Split into left, center, right
*/
private String[] getParts(String string) {
String[] parts = new String[] { "", "", "" };
if(string == null)
return parts;
// They can come in any order, which is just nasty
// Work backwards from the end, picking the last
// on off each time as we go
int lAt = 0;
int cAt = 0;
int rAt = 0;
while(
// Ensure all indicies get updated, then -1 tested
(lAt = string.indexOf(HeaderFooterEntity_L)) > -2 &&
(cAt = string.indexOf(HeaderFooterEntity_C)) > -2 &&
(rAt = string.indexOf(HeaderFooterEntity_R)) > -2 &&
(lAt > -1 || cAt > -1 || rAt > -1)
) {
// Pick off the last one
if(rAt > cAt && rAt > lAt) {
parts[2] = string.substring(rAt + HeaderFooterEntity_R.length());
string = string.substring(0, rAt);
} else if(cAt > rAt && cAt > lAt) {
parts[1] = string.substring(cAt + HeaderFooterEntity_C.length());
string = string.substring(0, cAt);
} else {
parts[0] = string.substring(lAt + HeaderFooterEntity_L.length());
string = string.substring(0, lAt);
}
return string.replaceAll(entity + oldSection, entity + newSection);
}
private String getSection(String string, String entity) {
if (string == null) {
return "";
return parts;
}
String stringAfterEntity = "";
if (string.indexOf(entity) >= 0) {
stringAfterEntity = string.substring(string.indexOf(entity) + entity.length());
private String joinParts(String[] parts) {
return joinParts(parts[0], parts[1], parts[2]);
}
String nextEntity = "";
if (stringAfterEntity.indexOf(HeaderFooterEntity) > 0) {
nextEntity = stringAfterEntity.substring(stringAfterEntity.indexOf(HeaderFooterEntity), stringAfterEntity.indexOf(HeaderFooterEntity) + (HeaderFooterEntity.length()));
stringAfterEntity = stringAfterEntity.substring(0, stringAfterEntity.indexOf(nextEntity));
private String joinParts(String l, String c, String r) {
StringBuffer ret = new StringBuffer();
// Join as c, l, r
if(c.length() > 0) {
ret.append(HeaderFooterEntity_C);
ret.append(c);
}
return stringAfterEntity;
if(l.length() > 0) {
ret.append(HeaderFooterEntity_L);
ret.append(l);
}
if(r.length() > 0) {
ret.append(HeaderFooterEntity_R);
ret.append(r);
}
return ret.toString();
}
}

View File

@ -192,10 +192,10 @@ public class TestXSSFExcelExtractor extends TestCase {
/**
* From bug #45540
*/
public void BROKENtestHeaderFooter() throws Exception {
public void testHeaderFooter() throws Exception {
String[] files = new String[] {
"45540_classic_Header.xlsx", "45540_form_Header.xlsx",
"45540_classic_Footer.xlsx", "45540_form_Footer.xlsx",
"45540_classic_Header.xlsx", "45540_form_Header.xlsx"
};
for(String file : files) {
File xml = new File(
@ -208,7 +208,7 @@ public class TestXSSFExcelExtractor extends TestCase {
new XSSFExcelExtractor(new XSSFWorkbook(xml.toString()));
String text = extractor.getText();
assertTrue("Unable to find expected word in text\n" + text, text.contains("testdoc"));
assertTrue("Unable to find expected word in text from " + file + "\n" + text, text.contains("testdoc"));
assertTrue("Unable to find expected word in text\n" + text, text.contains("test phrase"));
}
}

View File

@ -17,6 +17,9 @@
package org.apache.poi.xssf.usermodel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Iterator;
import junit.framework.TestCase;
import org.apache.poi.ss.usermodel.Cell;
@ -27,6 +30,7 @@ import org.apache.poi.ss.util.Region;
import org.apache.poi.xssf.model.CommentsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.helpers.ColumnHelper;
import org.openxml4j.opc.Package;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment;
@ -291,6 +295,74 @@ public class TestXSSFSheet extends TestCase {
assertEquals("test center footer", sheet.getOddFooter().getCenter());
}
public void testExistingHeaderFooter() throws Exception {
File xml = new File(
System.getProperty("HSSF.testdata.path") +
File.separator + "45540_classic_Header.xlsx"
);
assertTrue(xml.exists());
XSSFWorkbook workbook = new XSSFWorkbook(xml.toString());
XSSFOddHeader hdr;
XSSFOddFooter ftr;
// Sheet 1 has a header with center and right text
XSSFSheet s1 = (XSSFSheet)workbook.getSheetAt(0);
assertNotNull(s1.getHeader());
assertNotNull(s1.getFooter());
hdr = (XSSFOddHeader)s1.getHeader();
ftr = (XSSFOddFooter)s1.getFooter();
assertEquals("&Ctestdoc&Rtest phrase", hdr.getText());
assertEquals(null, ftr.getText());
assertEquals("", hdr.getLeft());
assertEquals("testdoc", hdr.getCenter());
assertEquals("test phrase", hdr.getRight());
assertEquals("", ftr.getLeft());
assertEquals("", ftr.getCenter());
assertEquals("", ftr.getRight());
// Sheet 2 has a footer, but it's empty
XSSFSheet s2 = (XSSFSheet)workbook.getSheetAt(1);
assertNotNull(s2.getHeader());
assertNotNull(s2.getFooter());
hdr = (XSSFOddHeader)s2.getHeader();
ftr = (XSSFOddFooter)s2.getFooter();
assertEquals(null, hdr.getText());
assertEquals("&L&F", ftr.getText());
assertEquals("", hdr.getLeft());
assertEquals("", hdr.getCenter());
assertEquals("", hdr.getRight());
assertEquals("&F", ftr.getLeft());
assertEquals("", ftr.getCenter());
assertEquals("", ftr.getRight());
// Save and reload
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
XSSFWorkbook wb = new XSSFWorkbook(Package.open(
new ByteArrayInputStream(baos.toByteArray())
));
hdr = (XSSFOddHeader)wb.getSheetAt(0).getHeader();
ftr = (XSSFOddFooter)wb.getSheetAt(0).getFooter();
assertEquals("", hdr.getLeft());
assertEquals("testdoc", hdr.getCenter());
assertEquals("test phrase", hdr.getRight());
assertEquals("", ftr.getLeft());
assertEquals("", ftr.getCenter());
assertEquals("", ftr.getRight());
}
public void testGetAllHeadersFooters() {
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = (XSSFSheet) workbook.createSheet("Sheet 1");
@ -626,7 +698,7 @@ public class TestXSSFSheet extends TestCase {
assertEquals(1, ctWorksheet.getColsArray(0).getColArray(0).getStyle());
XSSFRow row = (XSSFRow) sheet.createRow(0);
XSSFCell cell = (XSSFCell) sheet.getRow(0).createCell(3);
System.out.println(cell.getCellStyle());
//System.out.println(cell.getCellStyle());
}

View File

@ -21,14 +21,20 @@ import junit.framework.TestCase;
import org.apache.poi.xssf.usermodel.helpers.HeaderFooterHelper;
/**
* Test the header and footer helper.
* As we go through XmlBeans, should always use &,
* and not &amp;
*/
public class TestHeaderFooterHelper extends TestCase {
public void testGetCenterLeftRightSection() {
HeaderFooterHelper helper = new HeaderFooterHelper();
String headerFooter = "&amp;CTest the center section";
String headerFooter = "&CTest the center section";
assertEquals("Test the center section", helper.getCenterSection(headerFooter));
headerFooter = "&amp;CTest the center section&amp;LThe left one&amp;RAnd the right one";
headerFooter = "&CTest the center section&LThe left one&RAnd the right one";
assertEquals("Test the center section", helper.getCenterSection(headerFooter));
assertEquals("The left one", helper.getLeftSection(headerFooter));
assertEquals("And the right one", helper.getRightSection(headerFooter));
@ -44,15 +50,15 @@ public class TestHeaderFooterHelper extends TestCase {
headerFooter = helper.setRightSection(headerFooter, "First right");
assertEquals("First right", helper.getRightSection(headerFooter));
assertEquals("&amp;CFirst added center section&amp;LFirst left&amp;RFirst right", headerFooter);
assertEquals("&CFirst added center section&LFirst left&RFirst right", headerFooter);
headerFooter = helper.setRightSection(headerFooter, "First right&amp");
assertEquals("First right&amp", helper.getRightSection(headerFooter));
assertEquals("&amp;CFirst added center section&amp;LFirst left&amp;RFirst right&amp", headerFooter);
headerFooter = helper.setRightSection(headerFooter, "First right&F");
assertEquals("First right&F", helper.getRightSection(headerFooter));
assertEquals("&CFirst added center section&LFirst left&RFirst right&F", headerFooter);
headerFooter = helper.setRightSection(headerFooter, "First right&amp;");
assertEquals("First right", helper.getRightSection(headerFooter));
assertEquals("&amp;CFirst added center section&amp;LFirst left&amp;RFirst right&amp;", headerFooter);
headerFooter = helper.setRightSection(headerFooter, "First right&");
assertEquals("First right&", helper.getRightSection(headerFooter));
assertEquals("&CFirst added center section&LFirst left&RFirst right&", headerFooter);
}
}