<!-- 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>
<!-- 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>
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;
* 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;
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) {
- text.append(
- extractHeaderFooter(sheet.getHeader())
- );
- }
-
+ // Header(s), if present
+ text.append(
+ 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();) {
text.append("\n");
}
- // Finally footer, if present
- if(sheet.getFooter() != null) {
- text.append(
- extractHeaderFooter(sheet.getFooter())
- );
- }
+ // Finally footer(s), if present
+ text.append(
+ extractHeaderFooter(sheet.getFirstFooter())
+ );
+ text.append(
+ extractHeaderFooter(sheet.getOddFooter())
+ );
+ text.append(
+ extractHeaderFooter(sheet.getEvenFooter())
+ );
}
return text.toString();
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();
}
public CTHeaderFooter getHeaderFooter() {
return this.headerFooter;
}
-
+
public String getValue() {
- return this.value;
+ String value = getText();
+ if(value == null)
+ return "";
+ return value;
}
public abstract String getText();
public class HeaderFooterHelper {
+ // Note - XmlBeans handles entity encoding for us,
+ // so these should be & forms, not the & 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 = "&";
- private static final String HeaderFooterEntity_R = "&R";
- private static final String HeaderFooterEntity_L = "&L";
- private static final String HeaderFooterEntity_C = "&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);
- }
- return string.replaceAll(entity + oldSection, 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 parts;
}
-
- private String getSection(String string, String entity) {
- if (string == null) {
- return "";
- }
- String stringAfterEntity = "";
- if (string.indexOf(entity) >= 0) {
- stringAfterEntity = string.substring(string.indexOf(entity) + entity.length());
- }
- 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));
- }
- return stringAfterEntity;
+ private String joinParts(String[] parts) {
+ return joinParts(parts[0], parts[1], parts[2]);
}
+ 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);
+ }
+ 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();
+ }
}
/**
* 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(
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\n" + text, text.contains("test phrase"));
+ 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"));
}
}
}
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;
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;
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");
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());
}
import org.apache.poi.xssf.usermodel.helpers.HeaderFooterHelper;
-
+/**
+ * Test the header and footer helper.
+ * As we go through XmlBeans, should always use &,
+ * and not &
+ */
public class TestHeaderFooterHelper extends TestCase {
public void testGetCenterLeftRightSection() {
HeaderFooterHelper helper = new HeaderFooterHelper();
- String headerFooter = "&CTest the center section";
+
+ String headerFooter = "&CTest the center section";
assertEquals("Test the center section", helper.getCenterSection(headerFooter));
- headerFooter = "&CTest the center section&LThe left one&RAnd the right one";
+
+ headerFooter = "&CTest the center section<he 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));
headerFooter = helper.setRightSection(headerFooter, "First right");
assertEquals("First right", helper.getRightSection(headerFooter));
- assertEquals("&CFirst added center section&LFirst left&RFirst right", headerFooter);
+ assertEquals("&CFirst added center section&LFirst left&RFirst right", headerFooter);
- headerFooter = helper.setRightSection(headerFooter, "First right&");
- assertEquals("First right&", helper.getRightSection(headerFooter));
- assertEquals("&CFirst added center section&LFirst left&RFirst right&", 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&");
- assertEquals("First right", helper.getRightSection(headerFooter));
- assertEquals("&CFirst added center section&LFirst left&RFirst right&", headerFooter);
+ headerFooter = helper.setRightSection(headerFooter, "First right&");
+ assertEquals("First right&", helper.getRightSection(headerFooter));
+ assertEquals("&CFirst added center section&LFirst left&RFirst right&", headerFooter);
}
}