<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.1.1-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="add">New helper, HSSFOptimiser, which handles removing duplicated font and style records, to avoid going over the limits in Excel</action>
<action dev="POI-DEVELOPERS" type="fix">45322 - Fixed NPE in HSSFSheet.autoSizeColumn() when cell number format was not found</action>
<action dev="POI-DEVELOPERS" type="add">45380 - Missing return keyword in ArrayPtg.toFormulaString()</action>
<action dev="POI-DEVELOPERS" type="add">44958 - Record level support for Data Tables. (No formula parser support though)</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.1.1-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="add">New helper, HSSFOptimiser, which handles removing duplicated font and style records, to avoid going over the limits in Excel</action>
<action dev="POI-DEVELOPERS" type="fix">45322 - Fixed NPE in HSSFSheet.autoSizeColumn() when cell number format was not found</action>
<action dev="POI-DEVELOPERS" type="add">45380 - Missing return keyword in ArrayPtg.toFormulaString()</action>
<action dev="POI-DEVELOPERS" type="add">44958 - Record level support for Data Tables. (No formula parser support though)</action>
numfonts++;
return rec;
}
+
+ /**
+ * Removes the given font record from the
+ * file's list. This will make all
+ * subsequent font indicies drop by one,
+ * so you'll need to update those yourself!
+ */
+ public void removeFontRecord(FontRecord rec) {
+ records.remove(rec); // this updates FontPos for us
+ numfonts--;
+ }
/**
* gets the number of font records
return retval;
}
+
+ /**
+ * Removes the given ExtendedFormatRecord record from the
+ * file's list. This will make all
+ * subsequent font indicies drop by one,
+ * so you'll need to update those yourself!
+ */
+ public void removeExFormatRecord(ExtendedFormatRecord rec) {
+ records.remove(rec); // this updates XfPos for us
+ numxfs--;
+ }
+
/**
* creates a new Cell-type Extneded Format Record and adds it to the end of
result = prime * result + field_10_font_name_len;
return result;
}
+
+ /**
+ * Does this FontRecord have all the same font
+ * properties as the supplied FontRecord?
+ * Note that {@link #equals(Object)} will check
+ * for exact objects, while this will check
+ * for exact contents, because normally the
+ * font record's position makes a big
+ * difference too.
+ */
+ public boolean sameProperties(FontRecord other) {
+ return
+ field_1_font_height == other.field_1_font_height &&
+ field_2_attributes == other.field_2_attributes &&
+ field_3_color_palette_index == other.field_3_color_palette_index &&
+ field_4_bold_weight == other.field_4_bold_weight &&
+ field_5_super_sub_script == other.field_5_super_sub_script &&
+ field_6_underline == other.field_6_underline &&
+ field_7_family == other.field_7_family &&
+ field_8_charset == other.field_8_charset &&
+ field_9_zero == other.field_9_zero &&
+ field_10_font_name_len == other.field_10_font_name_len &&
+ field_11_font_name.equals(other.field_11_font_name)
+ ;
+ }
/**
* Only returns two for the same exact object -
this.field_5_ext_rst = ext_rst;
}
+
+ /**
+ * Swaps all use in the string of one font index
+ * for use of a different font index.
+ * Normally only called when fonts have been
+ * removed / re-ordered
+ */
+ public void swapFontUse(short oldFontIndex, short newFontIndex) {
+ Iterator i = field_4_format_runs.iterator();
+ while(i.hasNext()) {
+ FormatRun run = (FormatRun)i.next();
+ if(run.fontIndex == oldFontIndex) {
+ run.fontIndex = newFontIndex;
+ }
+ }
+ }
+
/**
* unlike the real records we return the same as "getString()" rather than debug info
* @see #getDebugInfo()
* object.
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#getCellStyleAt(short)
*/
-
public HSSFCellStyle getCellStyle()
{
short styleIndex=record.getXFIndex();
ExtendedFormatRecord xf = book.getWorkbook().getExFormatAt(styleIndex);
return new HSSFCellStyle(styleIndex, xf, book);
}
-
+
/**
* used for internationalization, currently -1 for unchanged, 0 for compressed unicode or 1 for 16-bit
*
--- /dev/null
+/* ====================================================================
+ Copyright 2002-2004 Apache Software Foundation
+
+ Licensed 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.hssf.usermodel;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apache.poi.hssf.record.ExtendedFormatRecord;
+import org.apache.poi.hssf.record.FontRecord;
+import org.apache.poi.hssf.record.UnicodeString;
+
+/**
+ * Excel can get cranky if you give it files containing too
+ * many (especially duplicate) objects, and this class can
+ * help to avoid those.
+ * In general, it's much better to make sure you don't
+ * duplicate the objects in your code, as this is likely
+ * to be much faster than creating lots and lots of
+ * excel objects+records, only to optimise them down to
+ * many fewer at a later stage.
+ * However, sometimes this is too hard / tricky to do, which
+ * is where the use of this class comes in.
+ */
+public class HSSFOptimiser {
+ /**
+ * Goes through the Workbook, optimising the fonts by
+ * removing duplicate ones.
+ * For now, only works on fonts used in {@link HSSFCellStyle}
+ * and {@link HSSFRichTextString}. Any other font uses
+ * (eg charts, pictures) may well end up broken!
+ * This can be a slow operation, especially if you have
+ * lots of cells, cell styles or rich text strings
+ * @param workbook The workbook in which to optimise the fonts
+ */
+ public static void optimiseFonts(HSSFWorkbook workbook) {
+ // Where each font has ended up, and if we need to
+ // delete the record for it. Start off with no change
+ short[] newPos =
+ new short[workbook.getWorkbook().getNumberOfFontRecords()+1];
+ boolean[] zapRecords = new boolean[newPos.length];
+ for(int i=0; i<newPos.length; i++) {
+ newPos[i] = (short)i;
+ zapRecords[i] = false;
+ }
+
+ // Get each font record, so we can do deletes
+ // without getting confused
+ FontRecord[] frecs = new FontRecord[newPos.length];
+ for(int i=0; i<newPos.length; i++) {
+ // There is no 4!
+ if(i == 4) continue;
+
+ frecs[i] = workbook.getWorkbook().getFontRecordAt(i);
+ }
+
+ // Loop over each font, seeing if it is the same
+ // as an earlier one. If it is, point users of the
+ // later duplicate copy to the earlier one, and
+ // mark the later one as needing deleting
+ // Note - don't change built in fonts (those before 5)
+ for(int i=5; i<newPos.length; i++) {
+ // Check this one for being a duplicate
+ // of an earlier one
+ int earlierDuplicate = -1;
+ for(int j=0; j<i && earlierDuplicate == -1; j++) {
+ if(j == 4) continue;
+
+ FontRecord frCheck = workbook.getWorkbook().getFontRecordAt(j);
+ if(frCheck.sameProperties(frecs[i])) {
+ earlierDuplicate = j;
+ }
+ }
+
+ // If we got a duplicate, mark it as such
+ if(earlierDuplicate != -1) {
+ newPos[i] = (short)earlierDuplicate;
+ zapRecords[i] = true;
+ }
+ }
+
+ // Update the new positions based on
+ // deletes that have occurred between
+ // the start and them
+ // Only need to worry about user fonts
+ for(int i=5; i<newPos.length; i++) {
+ // Find the number deleted to that
+ // point, and adjust
+ short preDeletePos = newPos[i];
+ short newPosition = preDeletePos;
+ for(int j=0; j<preDeletePos; j++) {
+ if(zapRecords[j]) newPosition--;
+ }
+
+ // Update the new position
+ newPos[i] = newPosition;
+ }
+
+ // Zap the un-needed user font records
+ for(int i=5; i<newPos.length; i++) {
+ if(zapRecords[i]) {
+ workbook.getWorkbook().removeFontRecord(
+ frecs[i]
+ );
+ }
+ }
+
+ // Tell HSSFWorkbook that it needs to
+ // re-start its HSSFFontCache
+ workbook.resetFontCache();
+
+ // Update the cell styles to point at the
+ // new locations of the fonts
+ for(int i=0; i<workbook.getWorkbook().getNumExFormats(); i++) {
+ ExtendedFormatRecord xfr = workbook.getWorkbook().getExFormatAt(i);
+ xfr.setFontIndex(
+ newPos[ xfr.getFontIndex() ]
+ );
+ }
+
+ // Update the rich text strings to point at
+ // the new locations of the fonts
+ // Remember that one underlying unicode string
+ // may be shared by multiple RichTextStrings!
+ HashSet doneUnicodeStrings = new HashSet();
+ for(int sheetNum=0; sheetNum<workbook.getNumberOfSheets(); sheetNum++) {
+ HSSFSheet s = workbook.getSheetAt(sheetNum);
+ Iterator rIt = s.rowIterator();
+ while(rIt.hasNext()) {
+ HSSFRow row = (HSSFRow)rIt.next();
+ Iterator cIt = row.cellIterator();
+ while(cIt.hasNext()) {
+ HSSFCell cell = (HSSFCell)cIt.next();
+ if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
+ HSSFRichTextString rtr = cell.getRichStringCellValue();
+ UnicodeString u = rtr.getRawUnicodeString();
+
+ // Have we done this string already?
+ if(! doneUnicodeStrings.contains(u)) {
+ // Update for each new position
+ for(short i=5; i<newPos.length; i++) {
+ if(i != newPos[i]) {
+ u.swapFontUse(i, newPos[i]);
+ }
+ }
+
+ // Mark as done
+ doneUnicodeStrings.add(u);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Goes through the Wokrbook, optimising the cell styles
+ * by removing duplicate ones.
+ * For best results, optimise the fonts via a call to
+ * {@link #optimiseFonts(HSSFWorkbook)} first.
+ * @param workbook The workbook in which to optimise the cell styles
+ */
+ public static void optimiseCellStyles(HSSFWorkbook workbook) {
+ // Where each style has ended up, and if we need to
+ // delete the record for it. Start off with no change
+ short[] newPos =
+ new short[workbook.getWorkbook().getNumExFormats()];
+ boolean[] zapRecords = new boolean[newPos.length];
+ for(int i=0; i<newPos.length; i++) {
+ newPos[i] = (short)i;
+ zapRecords[i] = false;
+ }
+
+ // Get each style record, so we can do deletes
+ // without getting confused
+ ExtendedFormatRecord[] xfrs = new ExtendedFormatRecord[newPos.length];
+ for(int i=0; i<newPos.length; i++) {
+ xfrs[i] = workbook.getWorkbook().getExFormatAt(i);
+ }
+
+ // Loop over each style, seeing if it is the same
+ // as an earlier one. If it is, point users of the
+ // later duplicate copy to the earlier one, and
+ // mark the later one as needing deleting
+ // Only work on user added ones, which come after 20
+ for(int i=21; i<newPos.length; i++) {
+ // Check this one for being a duplicate
+ // of an earlier one
+ int earlierDuplicate = -1;
+ for(int j=0; j<i && earlierDuplicate == -1; j++) {
+ ExtendedFormatRecord xfCheck = workbook.getWorkbook().getExFormatAt(j);
+ if(xfCheck.equals(xfrs[i])) {
+ earlierDuplicate = j;
+ }
+ }
+
+ // If we got a duplicate, mark it as such
+ if(earlierDuplicate != -1) {
+ newPos[i] = (short)earlierDuplicate;
+ zapRecords[i] = true;
+ }
+ }
+
+ // Update the new positions based on
+ // deletes that have occurred between
+ // the start and them
+ // Only work on user added ones, which come after 20
+ for(int i=21; i<newPos.length; i++) {
+ // Find the number deleted to that
+ // point, and adjust
+ short preDeletePos = newPos[i];
+ short newPosition = preDeletePos;
+ for(int j=0; j<preDeletePos; j++) {
+ if(zapRecords[j]) newPosition--;
+ }
+
+ // Update the new position
+ newPos[i] = newPosition;
+ }
+
+ // Zap the un-needed user style records
+ for(int i=21; i<newPos.length; i++) {
+ if(zapRecords[i]) {
+ workbook.getWorkbook().removeExFormatRecord(
+ xfrs[i]
+ );
+ }
+ }
+
+ // Finally, update the cells to point at
+ // their new extended format records
+ for(int sheetNum=0; sheetNum<workbook.getNumberOfSheets(); sheetNum++) {
+ HSSFSheet s = workbook.getSheetAt(sheetNum);
+ Iterator rIt = s.rowIterator();
+ while(rIt.hasNext()) {
+ HSSFRow row = (HSSFRow)rIt.next();
+ Iterator cIt = row.cellIterator();
+ while(cIt.hasNext()) {
+ HSSFCell cell = (HSSFCell)cIt.next();
+ short oldXf = cell.getCellValueRecord().getXFIndex();
+ HSSFCellStyle newStyle = workbook.getCellStyleAt(
+ newPos[oldXf]
+ );
+ cell.setCellStyle(newStyle);
+ }
+ }
+ }
+ }
+}
/** Called whenever the unicode string is modified. When it is modified
* we need to create a new SST index, so that other LabelSSTRecords will not
- * be affected by changes tat we make to this string.
+ * be affected by changes that we make to this string.
*/
private UnicodeString cloneStringIfRequired() {
if (book == null)
return string.getString();
}
- /** Used internally by the HSSFCell to get the internal string value*/
+ /**
+ * Used internally by the HSSFCell to get the internal
+ * string value.
+ * Will ensure the string is not shared
+ */
UnicodeString getUnicodeString() {
return cloneStringIfRequired();
}
+
+ /**
+ * Returns the raw, probably shared Unicode String.
+ * Used when tweaking the styles, eg updating font
+ * positions.
+ * Changes to this string may well effect
+ * other RichTextStrings too!
+ */
+ UnicodeString getRawUnicodeString() {
+ return string;
+ }
/** Used internally by the HSSFCell to set the internal string value*/
void setUnicodeString(UnicodeString str) {
return retval;
}
+
+ /**
+ * Reset the fonts cache, causing all new calls
+ * to getFontAt() to create new objects.
+ * Should only be called after deleting fonts,
+ * and that's not something you should normally do
+ */
+ protected void resetFontCache() {
+ fonts = new Hashtable();
+ }
/**
* create a new Cell style and add it to the workbook's style table
Workbook wb = (new HW()).getWorkbook();
assertEquals(4, wb.getNumberOfFontRecords());
+ assertEquals(68, wb.getRecords().size());
FontRecord f1 = wb.getFontRecordAt(0);
FontRecord f4 = wb.getFontRecordAt(3);
// There is no 4! new ones go in at 5
FontRecord n = wb.createNewFont();
+ assertEquals(69, wb.getRecords().size());
assertEquals(5, wb.getNumberOfFontRecords());
assertEquals(5, wb.getFontIndex(n));
assertEquals(n, wb.getFontRecordAt(5));
+
+ // And another
+ FontRecord n6 = wb.createNewFont();
+ assertEquals(70, wb.getRecords().size());
+ assertEquals(6, wb.getNumberOfFontRecords());
+ assertEquals(6, wb.getFontIndex(n6));
+ assertEquals(n6, wb.getFontRecordAt(6));
+
+
+ // Now remove the one formerly at 5
+ assertEquals(70, wb.getRecords().size());
+ wb.removeFontRecord(n);
+
+ // Check that 6 has gone to 5
+ assertEquals(69, wb.getRecords().size());
+ assertEquals(5, wb.getNumberOfFontRecords());
+ assertEquals(5, wb.getFontIndex(n6));
+ assertEquals(n6, wb.getFontRecordAt(5));
+
+ // Check that the earlier ones are unchanged
+ assertEquals(0, wb.getFontIndex(f1));
+ assertEquals(3, wb.getFontIndex(f4));
+ assertEquals(f1, wb.getFontRecordAt(0));
+ assertEquals(f4, wb.getFontRecordAt(3));
+
+ // Finally, add another one
+ FontRecord n7 = wb.createNewFont();
+ assertEquals(70, wb.getRecords().size());
+ assertEquals(6, wb.getNumberOfFontRecords());
+ assertEquals(6, wb.getFontIndex(n7));
+ assertEquals(n7, wb.getFontRecordAt(6));
}
private class HW extends HSSFWorkbook {
for (int i = 0; i < data.length; i++)
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
}
+
+ public void testSameProperties() throws Exception {
+ FontRecord f1 = new FontRecord(new TestcaseRecordInputStream((short)0x31, (short)data.length, data));
+ FontRecord f2 = new FontRecord(new TestcaseRecordInputStream((short)0x31, (short)data.length, data));
+
+ assertTrue(f1.sameProperties(f2));
+
+ f2.setFontName("Arial2");
+ assertFalse(f1.sameProperties(f2));
+ f2.setFontName("Arial");
+ assertTrue(f1.sameProperties(f2));
+
+ f2.setFontHeight((short)11);
+ assertFalse(f1.sameProperties(f2));
+ f2.setFontHeight((short)0xc8);
+ assertTrue(f1.sameProperties(f2));
+ }
}
result.addTestSuite(TestHSSFDateUtil.class);
result.addTestSuite(TestHSSFHeaderFooter.class);
result.addTestSuite(TestHSSFHyperlink.class);
+ result.addTestSuite(TestHSSFOptimiser.class);
result.addTestSuite(TestHSSFPalette.class);
result.addTestSuite(TestHSSFPatriarch.class);
result.addTestSuite(TestHSSFPicture.class);
--- /dev/null
+/* ====================================================================
+ Copyright 2002-2004 Apache Software Foundation
+
+ Licensed 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.hssf.usermodel;
+
+import org.apache.poi.hssf.model.Workbook;
+
+import junit.framework.TestCase;
+
+public class TestHSSFOptimiser extends TestCase {
+ public void testDoesNoHarmIfNothingToDo() throws Exception {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ HSSFFont f = wb.createFont();
+ f.setFontName("Testing");
+ HSSFCellStyle s = wb.createCellStyle();
+ s.setFont(f);
+
+ assertEquals(5, wb.getNumberOfFonts());
+ assertEquals(22, wb.getNumCellStyles());
+
+ // Optimise fonts
+ HSSFOptimiser.optimiseFonts(wb);
+
+ assertEquals(5, wb.getNumberOfFonts());
+ assertEquals(22, wb.getNumCellStyles());
+
+ assertEquals(f, s.getFont(wb));
+
+ // Optimise styles
+ HSSFOptimiser.optimiseCellStyles(wb);
+
+ assertEquals(5, wb.getNumberOfFonts());
+ assertEquals(22, wb.getNumCellStyles());
+
+ assertEquals(f, s.getFont(wb));
+ }
+
+ public void testOptimiseFonts() throws Exception {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ // Add 6 fonts, some duplicates
+ HSSFFont f1 = wb.createFont();
+ f1.setFontHeight((short)11);
+ f1.setFontName("Testing");
+
+ HSSFFont f2 = wb.createFont();
+ f2.setFontHeight((short)22);
+ f2.setFontName("Also Testing");
+
+ HSSFFont f3 = wb.createFont();
+ f3.setFontHeight((short)33);
+ f3.setFontName("Unique");
+
+ HSSFFont f4 = wb.createFont();
+ f4.setFontHeight((short)11);
+ f4.setFontName("Testing");
+
+ HSSFFont f5 = wb.createFont();
+ f5.setFontHeight((short)22);
+ f5.setFontName("Also Testing");
+
+ HSSFFont f6 = wb.createFont();
+ f6.setFontHeight((short)66);
+ f6.setFontName("Also Unique");
+
+
+
+ // Use all three of the four in cell styles
+ assertEquals(21, wb.getNumCellStyles());
+
+ HSSFCellStyle cs1 = wb.createCellStyle();
+ cs1.setFont(f1);
+ assertEquals(5, cs1.getFontIndex());
+
+ HSSFCellStyle cs2 = wb.createCellStyle();
+ cs2.setFont(f4);
+ assertEquals(8, cs2.getFontIndex());
+
+ HSSFCellStyle cs3 = wb.createCellStyle();
+ cs3.setFont(f5);
+ assertEquals(9, cs3.getFontIndex());
+
+ HSSFCellStyle cs4 = wb.createCellStyle();
+ cs4.setFont(f6);
+ assertEquals(10, cs4.getFontIndex());
+
+ assertEquals(25, wb.getNumCellStyles());
+
+
+ // And three in rich text
+ HSSFSheet s = wb.createSheet();
+ HSSFRow r = s.createRow(0);
+
+ HSSFRichTextString rtr1 = new HSSFRichTextString("Test");
+ rtr1.applyFont(0, 2, f1);
+ rtr1.applyFont(3, 4, f2);
+ r.createCell((short)0).setCellValue(rtr1);
+
+ HSSFRichTextString rtr2 = new HSSFRichTextString("AlsoTest");
+ rtr2.applyFont(0, 2, f3);
+ rtr2.applyFont(3, 5, f5);
+ rtr2.applyFont(6, 8, f6);
+ r.createCell((short)1).setCellValue(rtr2);
+
+
+ // Check what we have now
+ assertEquals(10, wb.getNumberOfFonts());
+ assertEquals(25, wb.getNumCellStyles());
+
+ // Optimise
+ HSSFOptimiser.optimiseFonts(wb);
+
+ // Check font count
+ assertEquals(8, wb.getNumberOfFonts());
+ assertEquals(25, wb.getNumCellStyles());
+
+ // Check font use in cell styles
+ assertEquals(5, cs1.getFontIndex());
+ assertEquals(5, cs2.getFontIndex()); // duplicate of 1
+ assertEquals(6, cs3.getFontIndex()); // duplicate of 2
+ assertEquals(8, cs4.getFontIndex()); // two have gone
+
+
+ // And in rich text
+
+ // RTR 1 had f1 and f2, unchanged
+ assertEquals(5, r.getCell(0).getRichStringCellValue().getFontAtIndex(0));
+ assertEquals(5, r.getCell(0).getRichStringCellValue().getFontAtIndex(1));
+ assertEquals(6, r.getCell(0).getRichStringCellValue().getFontAtIndex(3));
+ assertEquals(6, r.getCell(0).getRichStringCellValue().getFontAtIndex(4));
+
+ // RTR 2 had f3 (unchanged), f5 (=f2) and f6 (moved down)
+ assertEquals(7, r.getCell(1).getRichStringCellValue().getFontAtIndex(0));
+ assertEquals(7, r.getCell(1).getRichStringCellValue().getFontAtIndex(1));
+ assertEquals(6, r.getCell(1).getRichStringCellValue().getFontAtIndex(3));
+ assertEquals(6, r.getCell(1).getRichStringCellValue().getFontAtIndex(4));
+ assertEquals(8, r.getCell(1).getRichStringCellValue().getFontAtIndex(6));
+ assertEquals(8, r.getCell(1).getRichStringCellValue().getFontAtIndex(7));
+ }
+
+ public void testOptimiseStyles() throws Exception {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ // Two fonts
+ assertEquals(4, wb.getNumberOfFonts());
+
+ HSSFFont f1 = wb.createFont();
+ f1.setFontHeight((short)11);
+ f1.setFontName("Testing");
+
+ HSSFFont f2 = wb.createFont();
+ f2.setFontHeight((short)22);
+ f2.setFontName("Also Testing");
+
+ assertEquals(6, wb.getNumberOfFonts());
+
+
+ // Several styles
+ assertEquals(21, wb.getNumCellStyles());
+
+ HSSFCellStyle cs1 = wb.createCellStyle();
+ cs1.setFont(f1);
+
+ HSSFCellStyle cs2 = wb.createCellStyle();
+ cs2.setFont(f2);
+
+ HSSFCellStyle cs3 = wb.createCellStyle();
+ cs3.setFont(f1);
+
+ HSSFCellStyle cs4 = wb.createCellStyle();
+ cs4.setFont(f1);
+ cs4.setAlignment((short)22);
+
+ HSSFCellStyle cs5 = wb.createCellStyle();
+ cs5.setFont(f2);
+ cs5.setAlignment((short)111);
+
+ HSSFCellStyle cs6 = wb.createCellStyle();
+ cs6.setFont(f2);
+
+ assertEquals(27, wb.getNumCellStyles());
+
+
+ // Use them
+ HSSFSheet s = wb.createSheet();
+ HSSFRow r = s.createRow(0);
+
+ r.createCell((short)0).setCellStyle(cs1);
+ r.createCell((short)1).setCellStyle(cs2);
+ r.createCell((short)2).setCellStyle(cs3);
+ r.createCell((short)3).setCellStyle(cs4);
+ r.createCell((short)4).setCellStyle(cs5);
+ r.createCell((short)5).setCellStyle(cs6);
+ r.createCell((short)6).setCellStyle(cs1);
+ r.createCell((short)7).setCellStyle(cs2);
+
+ assertEquals(21, r.getCell(0).getCellValueRecord().getXFIndex());
+ assertEquals(26, r.getCell(5).getCellValueRecord().getXFIndex());
+ assertEquals(21, r.getCell(6).getCellValueRecord().getXFIndex());
+
+
+ // Optimise
+ HSSFOptimiser.optimiseCellStyles(wb);
+
+
+ // Check
+ assertEquals(6, wb.getNumberOfFonts());
+ assertEquals(25, wb.getNumCellStyles());
+
+ // cs1 -> 21
+ assertEquals(21, r.getCell(0).getCellValueRecord().getXFIndex());
+ // cs2 -> 22
+ assertEquals(22, r.getCell(1).getCellValueRecord().getXFIndex());
+ // cs3 = cs1 -> 21
+ assertEquals(21, r.getCell(2).getCellValueRecord().getXFIndex());
+ // cs4 --> 24 -> 23
+ assertEquals(23, r.getCell(3).getCellValueRecord().getXFIndex());
+ // cs5 --> 25 -> 24
+ assertEquals(24, r.getCell(4).getCellValueRecord().getXFIndex());
+ // cs6 = cs2 -> 22
+ assertEquals(22, r.getCell(5).getCellValueRecord().getXFIndex());
+ // cs1 -> 21
+ assertEquals(21, r.getCell(6).getCellValueRecord().getXFIndex());
+ // cs2 -> 22
+ assertEquals(22, r.getCell(7).getCellValueRecord().getXFIndex());
+ }
+}