https://svn.apache.org:443/repos/asf/poi/trunk ........ r677028 | nick | 2008-07-15 21:19:06 +0100 (Tue, 15 Jul 2008) | 1 line Method to check if two fonts have the same contents ........ r677029 | nick | 2008-07-15 21:24:53 +0100 (Tue, 15 Jul 2008) | 1 line Support for removing low level font records ........ r677041 | nick | 2008-07-15 22:15:16 +0100 (Tue, 15 Jul 2008) | 1 line Start on HSSFOptimiser, which removes un-needed cell styles and fonts, fixing up references as it does so ........ r677057 | nick | 2008-07-15 22:38:38 +0100 (Tue, 15 Jul 2008) | 1 line Cell Style optimisations too ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@677103 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_5_BETA2
@@ -50,6 +50,7 @@ | |||
<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> |
@@ -47,6 +47,7 @@ | |||
<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> |
@@ -443,6 +443,17 @@ public class Workbook implements Model | |||
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 | |||
@@ -702,6 +713,18 @@ public class Workbook implements Model | |||
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 |
@@ -579,6 +579,31 @@ public class FontRecord | |||
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 - |
@@ -439,6 +439,23 @@ public class UnicodeString | |||
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() |
@@ -938,14 +938,13 @@ public class HSSFCell implements Cell | |||
* 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 | |||
* |
@@ -0,0 +1,261 @@ | |||
/* ==================================================================== | |||
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); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -69,7 +69,7 @@ public class HSSFRichTextString | |||
/** 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) | |||
@@ -169,10 +169,25 @@ public class HSSFRichTextString | |||
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) { |
@@ -1094,6 +1094,16 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
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 |
@@ -32,6 +32,7 @@ public final class TestWorkbook extends TestCase { | |||
Workbook wb = (new HW()).getWorkbook(); | |||
assertEquals(4, wb.getNumberOfFontRecords()); | |||
assertEquals(68, wb.getRecords().size()); | |||
FontRecord f1 = wb.getFontRecordAt(0); | |||
FontRecord f4 = wb.getFontRecordAt(3); | |||
@@ -45,9 +46,41 @@ public final class TestWorkbook extends TestCase { | |||
// 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 { |
@@ -121,4 +121,21 @@ public class TestFontRecord | |||
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)); | |||
} | |||
} |
@@ -47,6 +47,7 @@ public class AllUserModelTests { | |||
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); |
@@ -0,0 +1,240 @@ | |||
/* ==================================================================== | |||
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()); | |||
} | |||
} |