mirror of https://github.com/apache/poi.git
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:
parent
e7476735dc
commit
7360b10247
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
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();) {
|
||||
|
@ -133,12 +137,16 @@ public class XSSFExcelExtractor extends POIXMLTextExtractor {
|
|||
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();
|
||||
|
|
|
@ -24,21 +24,21 @@ 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();
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -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 & 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,8 +208,8 @@ 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\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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 &
|
||||
*/
|
||||
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));
|
||||
|
@ -44,15 +50,15 @@ public class TestHeaderFooterHelper extends TestCase {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue