]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Tue, 15 Jul 2008 23:40:09 +0000 (23:40 +0000)
committerNick Burch <nick@apache.org>
Tue, 15 Jul 2008 23:40:09 +0000 (23:40 +0000)
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-ffa450edef68

13 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/record/FontRecord.java
src/java/org/apache/poi/hssf/record/UnicodeString.java
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/testcases/org/apache/poi/hssf/model/TestWorkbook.java
src/testcases/org/apache/poi/hssf/record/TestFontRecord.java
src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFOptimiser.java [new file with mode: 0644]

index 1fee98e86e28ddfd867e0ff1902d0ae423d08eb3..99106b3a0aed51cc2085e3dbf85e0748e1fd2d37 100644 (file)
@@ -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>
index e6472d6e9a762a982524ce99c315e773d5aa5a92..96ff195f29c47367cb5ba73ec2abc67692931f92 100644 (file)
@@ -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>
index d56051445b2c35bb6309e6af9e8b47404a292730..36510eb8352e1ff52fbd0d9b2042812f8e5bd9fc 100644 (file)
@@ -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
index d6a5ce859fa721e6a1a258ffd915474bad1c34a1..11ba3aaa897d0ec0dd1fed71acebbbd0fcff2f31 100644 (file)
@@ -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 -
index b53fcd485c5dd9d1ede3126378e5b8876b0abc62..9919d52c3d5ee83c79c03a07101f97b314617eb5 100644 (file)
@@ -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()
index c81929afec353b79eaac911dd600301894f29c2f..e4a7e37d70b3cce776b227598d6e7c25fbceef65 100644 (file)
@@ -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
      *
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java b/src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java
new file mode 100644 (file)
index 0000000..f2c7acc
--- /dev/null
@@ -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);
+                               }
+                       }
+               }
+       }
+}
index 526925a024b2e56544a2190c8d97562180e6f032..ce5a40acbf3cc7d0edaab1c50232ef389bcc304f 100644 (file)
@@ -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) {
index 09d66e1f60fb8ef779df2770c29d1bfe8873686a..d5b801c5c1620ac164a46a4b9145d12dc47c88a5 100644 (file)
@@ -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
index 99663d2038cdc7997a2787112e759d02cfea3ebb..f89297390cd36c36c00c40b3adefa5cd679c8432 100644 (file)
@@ -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 {
index 6485f586f9818222ed1848cd669175b8b0a9d403..d5051e99150d101dcb88998edc892570734f9fb6 100644 (file)
@@ -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));
+    }
 }
index 363e58c142788aad28784887703e87e7f483af2a..15a19c09121c9ae679415c61170aa68ae8c66acd 100755 (executable)
@@ -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);
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFOptimiser.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFOptimiser.java
new file mode 100644 (file)
index 0000000..b998fdd
--- /dev/null
@@ -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());
+       }
+}